Compare commits

..
440 Commits
Author SHA1 Message Date
Weiko c8bd91903f Bump version 2025-02-27 11:10:19 +01:00
Weiko c7d4e6c76f Fix filter rest api (#10537)
## Context
Rest API is now using cache metadata but the change was not done
everywhere which was breaking the API
<img width="953" alt="Screenshot 2025-02-27 at 10 48 31"
src="https://github.com/user-attachments/assets/84f549a6-59b5-4989-b1ba-2d5c515ee47c"
/>
2025-02-27 11:07:44 +01:00
Charles Bochet 9ad35b39ed Bump version 2025-02-27 10:32:34 +01:00
Charles BochetandCharles Bochet db9a906130 Fix field empty value check for Person standard object (#10533)
The previous code was not correct if labelIdentifier is a FULL_NAME.
We already have an util for this

Fixes: https://github.com/twentyhq/twenty/issues/10532
2025-02-27 10:32:08 +01:00
Weiko 218cb798e6 translate = no 2025-02-26 18:03:51 +01:00
Weiko a364692925 Bump version 2025-02-26 18:02:35 +01:00
Weiko 1991f0d3c0 Fix REST API not using metadata cache (#10521) 2025-02-26 18:00:07 +01:00
Charles Bochet ce428985aa Bump version 2025-02-21 18:37:03 +01:00
Charles BochetandGitHub 2039986684 Fix error messages on sign up (#10399)
In this PR:
- adding logs to track workspace creation performance
- refactor useIsWorkspaceSuspended to be more generic
- only fetch favorites and views if workspace is Active to avoid error
messages on sign up (workspace is not created yet)
2025-02-21 18:34:40 +01:00
MarieandGitHub 1ed9de2300 [permission] (fix ) use SettingsPermissions and not SettingsFeatures (#10398)
oops
2025-02-21 17:29:01 +01:00
MarieandGitHub ee2810281e [permissions] Add permission gates on workspace-invitations (#10394)
Adding permission gates on all workspace-invitations endpoints:
sendInvitation, resendInvitation, deleteWorkspaceInvitation,
findWorkspaceInvitations (the latter being from my understanding only
used to list the invitations to then re-send them or detee them).

+ tests on Api & webhooks permission gates
2025-02-21 17:26:01 +01:00
MarieandGitHub 50bd91262f [permissions] Rename enum SettingsFeatures --> SettingsPermissions (#10389) 2025-02-21 16:04:30 +00:00
bf92860d19 feat(twenty-server): add trusted domain - backend crud (#10290)
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
2025-02-21 16:02:48 +00:00
Lucas BordeauandGitHub 22203bfd3c Removed sort definitions (#10368)
This PR focuses on complete removal of sort definitions.
2025-02-21 16:59:38 +01:00
Charles BochetandGitHub d747366bf3 Provide a wrapper to execute command on workspace with easier devXP (#10391)
Proposal:
- Add a method in ActiveWorkspaceCommand to loop over workspace safely
(add counter, add try / catch, provide datasource with fresh cache,
destroy datasource => as we do always do it)

Also in this PR:
- make sure we clear all dataSources (and not only the one on metadata
version in RAM)
2025-02-21 16:40:33 +01:00
Raphaël BosiandGitHub 7a3e92fe0b Fix command menu selectable items ordering (#10392)
The order of the selectableItems was not the same as the command groups
so the navigation with arrow keys inside the command menu was broken.

Before:


https://github.com/user-attachments/assets/a3487b58-9c26-4522-9e7b-584d30396a21


After:


https://github.com/user-attachments/assets/bd60263b-b4e2-4d41-bad1-eef9115caec6
2025-02-21 16:05:31 +01:00
Félix MalfaitandGitHub ba51c091f0 Improve i18n CI (#10390) 2025-02-21 15:42:01 +01:00
Raphaël BosiandGitHub f269f8b905 Fix autogrow input whitespace bug (#10388)
Also fixed chip alignment.

Before:


https://github.com/user-attachments/assets/7bfe7c73-676a-4dee-b5df-fed15b663267

After:


https://github.com/user-attachments/assets/9060b3cc-051b-41f7-8149-418ed1c223cb
2025-02-21 15:37:07 +01:00
MarieandGitHub 6fb81e757b [permissions] Add conditional permission gate on billing's checkoutSession (#10387)
Following a conversation with @etiennejouan and @martmull, we are adding
a permission gate on billing resolver's checkoutSession, which should
only be accessible to entitled users or at workspace creation (when
there are no roles yet), when the subscription is incomplete
2025-02-21 14:01:36 +00:00
Raphaël BosiandGitHub 0571eb2cf6 Only display record actions inside the RecordIndexActionMenuDropdown (#10384)
In the action menu dropdown, we only want to display the actions for the
selected records, so we need to filter available actions according to
their type and scope.
Note: the `Search record` action was appearing twice because one is a
standard action and one is a fallback action and both are registered in
the available actions. We don't want to display this action because it
isn't related to the selected records, it is a global action.

Before:
<img width="226" alt="Capture d’écran 2025-02-21 à 12 12 53"
src="https://github.com/user-attachments/assets/0a4f9b8c-4e30-4697-9861-559e76dc288f"
/>

After:
<img width="241" alt="Capture d’écran 2025-02-21 à 12 10 34"
src="https://github.com/user-attachments/assets/0a98c9d5-432d-4018-a4d3-bf3a6d0c49be"
/>
2025-02-21 12:29:41 +01:00
EtienneandGitHub 5863c45d4b cleaning workspace job - fix on soft delete condition (#10380)
Context
If the command runs multiple times, soft deleted workspaces are soft
deleted again (+ email spamming)

Solution
Check for soft deletion before entering soft delete condition
2025-02-21 12:12:08 +01:00
WeikoandGitHub 29d079babc [FE] Improve Read-only field behavior (#10382)
Fixes https://github.com/twentyhq/core-team-issues/issues/235
2025-02-21 12:09:52 +01:00
Raphaël BosiandGitHub ec9587414b Fix open record in setting for command menu v1 (#10383)
- Hide open record in settings for command menu version 1 users
- Create a selector to check which version of the command menu the user
has before returning if the record should be opened in the record page
or in the side panel
2025-02-21 12:06:27 +01:00
Charles BochetandGitHub 311fc402d5 Enhance ActiveWorkspaceCommand to order, limit and startFrom workspaceId (#10378)
I have tested it locally with all combinaisons of params
2025-02-21 10:37:04 +01:00
nitinandGitHub 4720afe3fb health status refactor followup fix (#10379) 2025-02-21 10:32:00 +01:00
Raphaël BosiandGitHub 9f454c565b 410 open in side panel (#10363)
Closes https://github.com/twentyhq/core-team-issues/issues/410

- Added `openRecordIn` column in the `view` entity, which is set to
`SIDE_PANEL` by default
- Created a new option inside the view option dropdown to be able to set
`openRecordIn`
- Updated all record show page openings to reflect the setting behavior
- For `workflow`, `workflowVersion` and `workflowRun` (what I call
workflow objects), we want the default view `openRecordIn` to be set to
`RECORD_PAGE`. When seeding the views for the new workspaces, we set
`openRecordIn` to `RECORD_PAGE` for workflow objects. Since the workflow
objects views `openRecordIn` will be set to the default value
`SIDE_PANEL` for the existing workspaces when the sync metadata runs, I
created a script to run in the 0.43 update to update this value.
- Updated `closeCommandMenu` because of problems introduced by the
animate presence wrapper around the command menu. We now reset the
states at the end of the animation.

Note: We want to be able to open all workflow objects pages in the side
panel, but this requires some refactoring of the workflow module. For
now @Bonapara wanted to allow the possibility to change the
`openRecordIn` setting to `SIDE_PANEL` even for the workflows even if
it's buggy and not ready for the moment. Since this is an experimental
feature, it shouldn't cause too many problems.
2025-02-21 09:27:33 +00:00
e301c7856b Add all object level read-only behavior (#10356)
Fixes https://github.com/twentyhq/core-team-issues/issues/427

---------

Co-authored-by: Marie Stoppa <marie.stoppa@essec.edu>
2025-02-21 09:59:47 +01:00
nitinandGitHub c46f7848b7 refactor + new account sync metrics + isolating health status inside folder admin-panel > health-status (#10314)
closes https://github.com/twentyhq/core-team-issues/issues/444
https://github.com/twentyhq/core-team-issues/issues/443
https://github.com/twentyhq/core-team-issues/issues/442
2025-02-21 09:48:47 +01:00
Charles Bochet 41bbb4b47f Fix upgrade command 0.42 2025-02-21 00:20:44 +01:00
Weiko d397981d99 Fix billing page routing 2025-02-20 23:49:16 +01:00
Charles BochetandGitHub 4037fb5860 Fix upgrade command 0.42 (#10373) 2025-02-20 20:22:27 +01:00
WeikoandGitHub c0fda41519 Fix N+1 featureFlags (#10371)
## Context
featureFlag query was done for each record returned by the ORM and
transformed in the QueryResultGettersFactory which was causing an N+1
2025-02-20 19:32:30 +01:00
Baptiste DevessierandGitHub 05d00e6604 Store the current flow definition in a state to not depend on a specific workflow version (#10352)
This PR introduces a new Recoil state to store the flow.

A few parts of the application need to know the definition of the
current flow. Previously, we stored the workflow version's ID and
fetched its definition with the `useWorkflowVersion` hook. However, we
must use another strategy to visualize workflow runs. Indeed, we now
store the definition of the workflow in the workflow run output when it
is executed. This is useful for draft versions, which can change between
the moment they were executed and the moment they are visualized.
2025-02-20 17:12:03 +01:00
EtienneandGitHub d96865abc3 fix deleteCurrentWorkspace resolver output (#10365)
Tests are planned in this sprint - Core priority

closes https://github.com/twentyhq/twenty/issues/10347
2025-02-20 17:09:59 +01:00
Paul RastoinandGitHub 68cb94f365 [BUGFIX] Call and await destroyDataSourceForWorkspace to avoid killing poor pod (#10364)
# Motivations
Upgrade migration is not possible atm in production because running over
all workspaces cause cpu raise leading to pod termination
2025-02-20 16:45:21 +01:00
GuillimGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
f12bc5e126 Deduplicate-emails (#10361)
last request from @felix

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-02-20 16:34:27 +01:00
MarieandGitHub b2bbf88e28 [permissions] Place lab + billing behind settings/workspace permission gates (#10354) 2025-02-20 15:31:11 +00:00
Lucas BordeauandGitHub 3c80e2601f Add initialization of new record sorts state and added remove record sorts util (#10358)
This PR adds the same synchronization utils and hooks from view to
record sorts, as we did with view and record filters.

The goal is to apply what's in the view sorts only when needed.

Also added tests for those utils and hooks.

We also add useRemoveRecordSorts.
2025-02-20 16:24:02 +01:00
GuillimandGitHub aeb8806f0d replyto-microsoft-fix (#10360)
During QA on main, I realised there was a tiny issue with this new
button for microsoft
2025-02-20 14:49:56 +00:00
GuillimGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
04a62e9749 Deduplicate-emails (#10355)
Following User request to remove duplicate emails

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-02-20 15:40:49 +01:00
EtienneandGitHub d4aba8d1ff move deletion limit on hard deletion (#10353) 2025-02-20 11:20:58 +01:00
94c0d0f8d2 [BUG] Refactor actor composite type (#10232)
fixes #10200 

The FieldActor Zod schema was updated to correctly handle null context.

---------

Co-authored-by: prastoin <paul@twenty.com>
2025-02-20 11:05:26 +01:00
martmullandGitHub 927b8c717e Poc lambda deployment duration (#10340)
closes https://github.com/twentyhq/core-team-issues/issues/436

## Acheivements
Improve aws lambda deployment time from ~10/15 secs to less that 1 sec

## Done
- migrate with the new code executor architecture for local and lambda
drivers
- support old and new executor architecture to avoid breaking changes
- first run is long, next runs are quick even if code step is updated

## Demo using `lambda` driver
### Before


https://github.com/user-attachments/assets/7f7664b4-658f-4689-8949-ea2c31131252


### After



https://github.com/user-attachments/assets/d486c8e2-f8f8-4dbd-a801-c9901e440b29
2025-02-20 10:49:57 +01:00
Lucas BordeauandGitHub 3f93aba5fc Added parallel code path to set new record sorts state (#10345)
This PR implements a parallel code path to set record sorts without
impacting the actual sort, like we did on record filter.

We add a ViewBarRecordSortEffect which mirrors
ViewBarRecordFilterEffect.

It also adds availableFieldMetadataItemsForSortFamilySelector to replace
sortDefinitions progressively.
2025-02-20 10:40:25 +01:00
316876fcb5 Add files deletion when destroying attachment, workspace or workspaceMember (#10222)
Solution
- update attachment soft delete logic by destroy (seen with Weiko &
Felix)
- add two jobs for file and workspace folder deletion
- add listener to attachment and workspaceMember destroy event -> add
file deletion job
- update logic in deleteWorkspace method -> add wokspace folder deletion
job

closes https://github.com/twentyhq/core-team-issues/issues/147

To go further
- delete old avatar when workspaceMember replaces its avatar
- same with workspace picture

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-02-20 09:35:14 +00:00
Paul RastoinandGitHub 422e4e33c0 [BUGFIX][PROD] RICH_TEXT_V2 command handle {} body col value (#10324)
# Introduction
Encountered in issue in production where we have a lot of records that
has RICH_TEXT_FIELD set to `{}`

```sh
[Nest] 20106  - 02/19/2025, 12:43:08 PM     LOG [MigrateRichTextFieldCommand] Generating markdown for 1 records
[Nest] 20106  - 02/19/2025, 12:43:09 PM     LOG [MigrateRichTextFieldCommand] Error in workspace 3b8e6458-5fc1-4e63-8563-008ccddaa6db: TypeError: o is not iterable
```

## Fix
While reading `fieldValue` definition also strictly check if it's `{}` +
checking after JSON parse if it's an iterable to pass to the
`serverBlockNoteEditor` in order to be 100 bullet proof for prod
migration command

## Refactor Dry run
Implemented dry run

## Refactor to Idempotency
Made the script idempotent in order to avoid issues with re-running
commands

## Error repro
- In local checkout on v0.41.5 run `yarn && npx nx reset && npx nx
start`
- Create record manually in db that has a RICH_TEXT body to `{}`
- Checkout to main, `yarn && npx nx reset && npx nx build twenty-server
&& yarn command:prod upgrade-0.42:migrate-rich-text-field -d`
2025-02-20 09:16:58 +00:00
EtienneandGitHub ef9328e2e9 add not found redirection logic if object in url param not exists (#10339)
closes #10150
2025-02-20 09:17:52 +01:00
WeikoandGitHub 65bd1d7775 Role permissions tab should display object permissions from API result (#10344)
## Context
Role all-objects permissions were mocked. Now that the backend returns
it, we are using its values.

<img width="556" alt="Screenshot 2025-02-19 at 18 39 07"
src="https://github.com/user-attachments/assets/9a1c57fd-dc18-43ef-bc2d-be738d1716f5"
/>
2025-02-19 18:58:19 +01:00
WeikoandGitHub 4a2a312efd remove lingui no single variables to translate #2 (#10342) 2025-02-19 18:34:35 +01:00
WeikoandGitHub 202af87be5 Remove no-single-variables-to-translate eslint rule (#10341)
## Context
This rule crashes the FE for some reason, disabling it for now
2025-02-19 18:22:41 +01:00
GuillimandGitHub 78178010f1 following emails logic with intertnal conversaiotn (#10333) 2025-02-19 16:44:09 +00:00
984eeda807 refactor(sso): standardize SSO identity provider query names (#10335)
Updated method, query, and variable names to align with a consistent
naming convention for fetching SSO identity providers. Added
comprehensive unit tests to validate SSO service logic, ensuring better
reliability and maintainability.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-19 17:39:31 +01:00
4fd0c28439 i18n - translations (#10334)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-19 17:17:07 +01:00
Lucas BordeauandGitHub 2fc8eaa25b Implement record sort states and record sort context (#10257)
This PR is simple, it creates states for record sort, mirroring record
filter states.

It also implements RecordSortsComponentInstanceContext everywhere
RecordFiltersComponentInstanceContext is used.

This could be later merged into a common RecordContext concept but we
first need to decide how to handle the existing ContextStore and
RecordIndexContext and ideally end up with a unique context (or a
context provider component that wraps in all those contexts at once).

Some bugs are already present on main when trying to delete a sort, they
will be fixed in the next PRs.
2025-02-19 16:51:49 +01:00
2372114f52 i18n - translations (#10332)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-02-19 16:45:44 +01:00
ad47ab5bcd Display confirmation dialog on connected account deletion (#10323)
![deleteConfirmation](https://github.com/user-attachments/assets/1f0fea78-d02c-4351-b79f-733c08e0e0ec)

Fairly straightforward change, it's my first contribution in this
project so tried to follow the existing patterns.
I couldn't find any component tests or e2e Playwright tests touching
this area to update - happy to write some from scratch if necessary!

Closes #10285

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-19 16:29:10 +01:00
Félix MalfaitandGitHub 7bf851b29b I18n ci 9 (#10331) 2025-02-19 16:08:31 +01:00
Félix MalfaitandGitHub 3b8647e368 Improve i18n CI - 8 (#10330) 2025-02-19 15:44:34 +01:00
Baptiste DevessierandGitHub 77caf36d90 Fix workflow run ouput format (#10302)
- Adapt the frontend to use the new output format
2025-02-19 15:10:59 +01:00
GuillimandGitHub ad9af65898 internal-messages-backend-and-frontend (#10291)
fixes https://github.com/twentyhq/twenty/issues/10263
fixes https://github.com/twentyhq/twenty/issues/10262 also
2025-02-19 15:02:10 +01:00
GuillimandGitHub 7636def54d typo (#10327)
typo when doing the fix
2025-02-19 13:52:43 +00:00
f9763ff7ac fixing test mainly by @charlesBochet (#10325)
to enable CI to work

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-02-19 14:13:30 +01:00
Thomas TrompetteandGitHub d628958905 Remove status check when ending workflow (#10299)
Should be possible to end workflow, not matter what the current status
is.
On failure before the workflow was started, this error prevent the
workflow to be marked as failed with the right error message.
2025-02-19 13:58:32 +01:00
nitinandGitHub d5b04fae28 impersonate regression fix (#10306) 2025-02-19 12:05:40 +01:00
MarieandGitHub 861face2a8 [permissions] Enforce object-records permission checks in resolvers (#10304)
Closes https://github.com/twentyhq/core-team-issues/issues/393

- enforcing object-records permission checks in resolvers for now. we
will move the logic to a lower level asap
- add integration tests that will still be useful when we have moved the
logic
- introduce guest seeded role to test limited permissions on
object-records
2025-02-19 11:21:03 +01:00
Lucas BordeauandGitHub 33178fa8b2 Fix sorts on table and board (#10311)
This PR fixes sorts on table and board.

The sorts were not taken into account due to the recent view refactor
that had the side effect of removing the synchronization between
recordSortsState and changing the state.

The fix was to use combined view sorts instead, which are currently the
source of truth for sorts.

Also added a confirmation modal for manual sorting on board.
2025-02-19 10:57:13 +01:00
MarieandGitHub 645065abba Fix queryRunnerArgsFactory for updateMany resolver (#10322)
This was updated a few weeks ago and went unnoticed since 1) integration
tests were broken + 2) we don't have actionnable updateMany mutations in
the product at the moment

It will fix some tests, at least
all-people-resolvers.integration-spec.ts for instance
2025-02-19 10:09:27 +01:00
Antoine MoreauxGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
c0014cfe96 refactor(domain-manager): improve default workspace resolution (#10282)
Refactor logic for determining the default workspace in single-workspace
mode. Added fallback to Apple workspace when multiple workspaces are
found and updated validations to ensure a workspace is always returned.
Simplified handling of scenarios where multi-workspace mode is enabled.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-02-19 09:06:34 +00:00
GuillimandGitHub dca4fc1423 [BUGFIX] Reply button opens google (#10269)
fixes https://github.com/twentyhq/twenty/issues/10242
2025-02-19 09:58:50 +01:00
Félix MalfaitandGitHub 248ec4cd6f i18n CI 7 (#10321) 2025-02-19 09:24:01 +01:00
Félix MalfaitandGitHub df3d3a3c0c I18n ci 6 (#10320) 2025-02-19 08:35:07 +01:00
45566fbdb6 i18n - translations (#10319)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-19 07:21:43 +01:00
84eb5fa46a i18n - translations (#10318)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-19 05:20:53 +01:00
dce7f375bc i18n - translations (#10317)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-19 03:44:15 +01:00
46914652b2 i18n - translations (#10316)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-19 01:32:47 +01:00
8d819a1489 i18n - translations (#10315)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 23:20:04 +01:00
Félix MalfaitandGitHub 7f4b5795bf Stop showing lines on translation files (#10313) 2025-02-18 21:21:32 +01:00
9c5b6c844b i18n - translations (#10312)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 21:21:01 +01:00
82acafb843 i18n - translations (#10310)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 19:21:28 +01:00
GuillimandGitHub 8d8bd4a597 feedback from Thomas (#10297)
as required by @bonapara
here :
https://discord.com/channels/1130383047699738754/1341421200739536978/1341421200739536978
2025-02-18 19:05:24 +01:00
GuillimandGitHub 9cdfa3384f fix the missing createdBy icon for Companies (#10307)
fixes the missing icons for createdBy chips on Companies

Refference fo the bug detection :
https://discord.com/channels/1130383047699738754/1341407800424927232/1341407800424927232


<img width="378" alt="Screenshot 2025-02-18 at 17 37 34"
src="https://github.com/user-attachments/assets/8c35ebf4-b946-4674-9f56-290be7c20c4f"
/>
2025-02-18 19:04:07 +01:00
MarieandGitHub 222f475cc6 [permissions] Fix db reset leads to broken state if env var permission is not true (#10309) 2025-02-18 17:25:54 +00:00
Baptiste DevessierandGitHub 804aab6e0b Bring back raw workflow run output visualizer (#10294)
[My last
PR](https://github.com/twentyhq/twenty/pull/10146#issuecomment-2665863910),
which introduced the workflow run visualizer, made it impossible to
debug an execution as it's not complete yet.

In this PR, I'm bringing back the raw output visualizer and hiding the
flow visualizer as it's been broken by the recent change to the output
format.
2025-02-18 18:14:46 +01:00
ded6767dae 0.42 changelog (#10298)
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-02-18 18:11:52 +01:00
EtienneandGitHub af8a167fb2 Update searchVector on new richTextV2 note and task entities + migration command (#10303)
closes https://github.com/twentyhq/core-team-issues/issues/343
closes https://github.com/twentyhq/core-team-issues/issues/340
2025-02-18 18:11:29 +01:00
Raphaël BosiandGitHub aeed1c9f15 406 animate the command menu button (#10305)
Closes https://github.com/twentyhq/core-team-issues/issues/406

- Added animation on the Icon (The dots rotate and transform into an a
cross)
- Introduced a new component `AnimatedButton`. All the button styling
could be extracted to another file so we don't duplicate the code, but
since `AnimatedLightIconButton` duplicates the style from
`LightIconButton`, I did the same here.
- Added an animate presence component on the command menu to have a
smooth transition from `open` to `close` state
- Merged the open and close command menu button
- For all the pages that are not an index page or a record page, we want
the old behavior because there is no button in the page header to open
the command menu

# Before


https://github.com/user-attachments/assets/5ec7d9eb-9d8b-4838-af1b-c04382694342


# After


https://github.com/user-attachments/assets/f700deec-1c52-4afd-b294-f9ee7b9206e9
2025-02-18 18:07:11 +01:00
fef6a7dcee i18n - translations (#10300)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 17:21:14 +01:00
WeikoandGitHub 5cf0a71d12 fix server ci integration (#10296)
Now that the CI is running, I'm trying to fix the tests
2025-02-18 17:01:29 +01:00
d6655a2c3b Health monitor status for admin panel (#10186)
# Health Monitoring for Self-Hosted Instances

This PR implements basic health monitoring for self-hosted instances in
the admin panel.

## Service Status Checks
We're adding real-time health checks for:
- Redis Connection
- Database Connection
- Worker Status
- Message Sync Status

## Existing Functionality
We already have message sync and captcha counters that store aggregated
metrics in cache within a configurable time window (default: 5 minutes).

## New Endpoints
1. `/healthz` - Basic server health check for Kubernetes pod monitoring
2. `/healthz/{serviceName}` - Individual service health checks (returns
200 if healthy)
3. `/metricsz/{metricName}` - Time-windowed metrics (message sync,
captcha)
4. GraphQL resolver in admin panel for UI consumption

All endpoints use the same underlying service, with different
presentation layers for infrastructure and UI needs.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-18 15:52:19 +01:00
WeikoandGitHub 2fca60436b Add settingsPermission gate on the frontend (#10179)
## Context
With the new permissions system, we now need to hide some items from the
settings navigation and gate some routes so they can't be accessed
directly.
To avoid having to set permission gates in all the component pages, I'm
introducing wrapper at the route level and in the Navigation. This is
not required and is mostly for pages that are strictly mapped to a
single permission, for the rest we still need to use the different hooks
manually but it should avoid a bit of boilerplate for most of the cases.

- currentUserWorkspaceState to access settingsPermissions
- SettingsProtectedRouteWrapper in the router that can take a
settingFeature or a featureFlag as a gate logic, if the currentUser does
not have access to the settingFeature or the featureFlag is not enabled
they will be redirected to the profile page.
- SettingsNavigationItemWrapper & SettingsNavigationSectionWrapper. The
former will check the same logic as SettingsProtectedRouteWrapper and
not display the item if needed. The later will check if all
SettingsNavigationItemWrapper are not visible and hide itself if that's
the case.
- useHasSettingsPermission to get a specific permission state for the
current user
- useSettingsPermissionMap to get a map of all permissions with their
values for the current user
- useFeatureFlagsMap same but for featureFlags
2025-02-18 15:50:23 +01:00
755d1786fc i18n - translations (#10292)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 15:20:07 +01:00
WeikoandGitHub 0234c8d707 Fix featureFlag N+1 queries (#10261)
## Context
Regression was introduced 3 weeks ago when we added relations v2.
Because the relation logic is recursive during the life of a request, we
were querying the featureFlags many times.

We are now always using the featureFlag map and it's now available in
the base resolver so we don't need to query it everywhere, preferably
passing it as a parameter instead.

Note: We should introduce a cache for featureFlags in the future, this
is something easy to control and invalidate when needed.
2025-02-18 14:43:42 +01:00
Baptiste DevessierandGitHub ade13826c2 Fix e2e tests (#10289)
- Remove the demo test as I don't think it provides much value
- Re-run a test that was discarded because it failed due to a bug;
modified the test so the bug doesn't make the test fail
- Fixed all workflow tests
2025-02-18 14:30:40 +01:00
fb42046033 Refacto views (#10272)
In this huge (sorry!) PR:
- introducing objectMetadataItem in contextStore instead of
objectMetadataId which is more convenient
- splitting some big hooks into smaller parts to avoid re-renders
- removing Effects to avoid re-renders (especially onViewChange)
- making the view prefetch separate from favorites to avoid re-renders
- making the view prefetch load a state and add selectors on top of it
to avoir re-renders

As a result, the performance is WAY better (I suspect the favorite
implementation to trigger a lot of re-renders unfortunately).
However, we are still facing a random app freeze on view creation. I
could not investigate the root cause. As this seems to be already there
in the precedent release, we can move forward but this seems a urgent
follow up to me ==> EDIT: I've found the root cause after a few ours of
deep dive... an infinite loop in RecordTableNoRecordGroupBodyEffect...

prastoin edit: close https://github.com/twentyhq/twenty/issues/10253

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: prastoin <paul@twenty.com>
2025-02-18 13:51:07 +01:00
Félix MalfaitandGitHub 103dff4bd0 File previewer (#10260)
Add a file previewer for pdf, image, doc, xls

<img width="991" alt="Screenshot 2025-02-17 at 15 03 10"
src="https://github.com/user-attachments/assets/7516c13d-d6cb-4a10-b10f-b422268d223b"
/>
2025-02-18 10:18:59 +01:00
Raphaël BosiandGitHub 270744eca6 Fix command menu context chip display on show page (#10267)
## New Company
### Before:
<img width="500" alt="Capture d’écran 2025-02-17 à 16 47 54"
src="https://github.com/user-attachments/assets/4573450d-14b1-41f0-9b86-24003f489fde"
/>

### After:
<img width="500" alt="Capture d’écran 2025-02-17 à 16 46 24"
src="https://github.com/user-attachments/assets/6622bd75-900a-451b-ac21-c98bddeee32d"
/>


## Task
### Before:
<img width="500" alt="Capture d’écran 2025-02-17 à 16 47 35"
src="https://github.com/user-attachments/assets/04b77faa-b628-4839-ab94-95c8570c1818"
/>

### After:
<img width="501" alt="Capture d’écran 2025-02-17 à 16 47 03"
src="https://github.com/user-attachments/assets/1577dea6-7541-497e-af6e-3a4559f1a913"
/>
2025-02-18 09:52:25 +01:00
905cfedaf0 i18n - translations (#10279)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 09:21:08 +01:00
5b38059c4d i18n - translations (#10278)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 07:21:23 +01:00
3bebc187fd i18n - translations (#10277)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 05:20:50 +01:00
09f5e230ff i18n - translations (#10276)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 03:43:55 +01:00
3dbd2ec39e i18n - translations (#10275)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-18 01:32:25 +01:00
41ff6aaf89 i18n - translations (#10274)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-17 23:19:50 +01:00
0cb6f35baf i18n - translations (#10273)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-17 21:20:21 +01:00
df30850e82 i18n - translations (#10271)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-17 19:21:20 +01:00
Paul RastoinandGitHub cd8e240311 [NITPICK] call closeDropdown before openModal import spreadsheet (#10254)
# Introduction
Just a nitpick resulting from
https://github.com/twentyhq/twenty/pull/10205
Avoid any possible race condition between competing event handlers by
closing dropdown before opening modal
2025-02-17 19:14:46 +01:00
MarieandGitHub cb3bd1353a [permissions] Add object records permissions to role entity (#10255)
Closes https://github.com/twentyhq/core-team-issues/issues/388

- Add object records-related permissions to role entity
- Add it to queriable `currentUserWorkspace` (used in FE)
2025-02-17 18:32:39 +01:00
Charles BochetandGitHub 5b4cb4bd2c Simplify last visited (#10259)
In this PR, I'm simplifying the lastVisitedView / Object logic:
- removing fallback logic as it's not useful
- splitting hooks into smaller hooks (to avoir re-renders)
- removing componentState on those states that are global
2025-02-17 17:27:28 +01:00
a526472ddc i18n - translations (#10268)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-17 17:21:23 +01:00
Raphaël BosiandGitHub e6a484bb58 Fix search fallback action (#10256)
The command menu search bar was reset after clicking on the `Search
records` fallback action, but it shouldn't.
This PR:
- Introduces a new type of action: `Fallback` actions
- Reset the search bar only if the action key differs from
'record-search-fallback'
2025-02-17 14:24:42 +00:00
e8a55fd4dc i18n - translations (#10258)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-17 15:20:16 +01:00
Mohammed Abdul Razak WahabandGitHub 91b7d2855e Fix: Ensure First Click on Spreadsheet Import 'Select File' Button Triggers File picker (#10205)
Fixes #10154 

### What was happening:
1. Click "Import" in dropdown -> Dropdown stays open
2. Click "Select file" button -> Two events occur:
   - Button tries to open file dialog
   - Click event bubbles up, reaches dropdown, dropdown tries to close
3. These competing actions cause the first click to fail

### Fix
By closing the dropdown immediately when clicking "Import":

No competing event handlers are active
The dropdown is fully closed before any file selector interactions
The open() function from useDropzone can work without interference
2025-02-17 14:28:28 +01:00
fca5fc364b i18n - translations (#10251)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-17 13:11:35 +01:00
Raphaël BosiandGitHub 7ed142e987 Fix command menu selection (#10248)
- Created a state `hasUserSelectedCommandState` : This state is set to
`true` when the user selects an element in the command menu list. It is
set to false upon redirection or when the command menu is closed.
- Modified `CommandMenuDefaultSelectionEffect` to have the expected
selection behavior for the command menu
2025-02-17 11:02:17 +00:00
14478afa7e i18n - translations (#10250)
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-02-17 12:00:59 +01:00
Félix MalfaitandGitHub ecc388c4bf Skip ref checkout in 18n (#10249) 2025-02-17 11:54:26 +01:00
WeikoandGitHub 2ba9f2d0e5 Fix integration test ci (#10226)
## Context
Integration test ci was not running properly. This PR should fix that

Todo: This was not running for a while, many tests are not succeeding so
I'll try to fix them in this PR now that the action is running properly.
2025-02-17 11:45:44 +01:00
Félix MalfaitandGitHub 896ff1dc2e I18n ci 5 (#10247) 2025-02-17 11:42:32 +01:00
Félix MalfaitandGitHub 13053fd6fa Fix i18n ci (#10246) 2025-02-17 11:30:25 +01:00
5b961cbb7f Tasks assigned to me view (#9567) (#9568)
Issue: https://github.com/twentyhq/core-team-issues/issues/154

View is now added.

TODO: Fix filtering a relation field by 'Me' in Tasks.

---------

Co-authored-by: ad-elias <elias@autodiligence.com>
2025-02-17 10:13:55 +00:00
Raphaël BosiandGitHub 9ef6486b06 Fix autogrowing input glitch (#10224)
- Removed unnecessary resize observer
- Removed unused component
- Fixed autogrowing input glitch

# Before


https://github.com/user-attachments/assets/a7de71d5-bc6e-495f-851c-18df596749dd

# After


https://github.com/user-attachments/assets/63588d0e-1122-43fe-b685-3f3a4ec4114e
2025-02-17 10:10:39 +00:00
Charles BochetandGitHub 0cd6e29bff Leave old body field on task and note object (#10243) 2025-02-17 10:38:20 +01:00
Raphaël BosiandGitHub 9ddaaa803b Display object name plural instead of object (#10228)
Display object name plural instead of 'Object' inside the command menu
section related to the current object.
2025-02-17 10:34:58 +01:00
JarWarrenandGitHub afdd44ff66 Cloudflare name typo (#10239)
Fix typo in Cloudflare
2025-02-17 08:50:15 +01:00
Félix MalfaitandGitHub 1dca3694f7 Delay loading support chat (#10234)
There are a lot of requests being done in parallel when you load the
page, and the support chat is responsible for some of them. I don't
think it's critical to have it upon loading so delaying it by 2 seconds
2025-02-16 12:58:20 +01:00
Paul RastoinandGitHub f795d90702 [WEBSITE] Upgrade guide 0.41.0 -> 0.42.0 (#10231)
# Introduction
Upgrade guide from 0.41.0 to 0.42.0

## Note
@guillim I've been searching for references to the new `env vars` added
in the Microsoft scope to the version to the `setup.mdx` page, unless
I'm mistaken `IS_MICROSOFT_SYNC_ENABLED` seems to be missing ? I'm not
sure if it's required or not and on where to add it within the page,
could you please give it a look 🙏 ?
2025-02-15 09:11:57 +01:00
Paul RastoinandGitHub db1b11742b [0.42][FIX] Seeder activate rich text v2 feature flag (#10229)
as per title
2025-02-14 17:57:32 +00:00
GuillimandGitHub bab529b138 Fixing unhandled exception for microsoft driver (#10223)
Fixing a difficult bug: an unhandled exception for microsoft driver when
one of the many Microsoft BATCH request failed.
2025-02-14 18:50:53 +01:00
Lucas BordeauandGitHub 70e5cdfe83 Remove filter definition (#10227)
This PR removes completely filter definition usage and states,
everything is now derived from fieldMetadataItem.
2025-02-14 17:37:34 +00:00
MarieandGitHub 68db9a7a8c (fix) throw if Access JWT does not have a userWorkspaceId (#10225)
After introducing userWorkspaceId into JWTs, we were wrongfully
executing
```
const userWorkspace = await this.userWorkspaceRepository.findOne({
      where: {
        id: payload.userWorkspaceId,
      },
    });
```

which would return a random userWorkpace if `payload.userWorkspaceId` is
undefined.

All generated JWTs have had a userWorkspaceId for more than a week now,
but in tests we had not modified the accessToken in use, which did not
have a userWorkspaceId, until [this
pr](https://github.com/twentyhq/twenty/pull/10204)
2025-02-14 17:48:06 +01:00
MarieandGitHub 12cc61e096 [permissions] Add workspace + security settings permission gates (#10204)
In this PR

- closing https://github.com/twentyhq/core-team-issues/issues/313
- adding permission gates on workspace settings and security settings
- adding integration tests for each of the protected setting and
security
2025-02-14 17:32:42 +01:00
EtienneandGitHub db526778e3 Update suspended cleaning command (#10195)
closes https://github.com/twentyhq/core-team-issues/issues/382
2025-02-14 15:49:44 +00:00
Paul RastoinandGitHub 968ad3bd31 [NITPICK] From args list to record args RecordPositionFactory (#10215)
# Introduction
Just some nitpicking while debugging a bug that wasn't one
2025-02-14 15:43:09 +01:00
Charles BochetandGitHub c624657b29 update seeding views to seed the new bodyV2 field on workspace creation (#10221)
As per title
2025-02-14 15:28:25 +01:00
Thomas TrompetteandGitHub 017280384a Add flow to run output (#10220)
We need the version trigger and steps to be stored in the output. We
should not rely on the version itself because some run are made on draft
versions. Which means versions could be edited afterwards.
2025-02-14 15:20:27 +01:00
Thomas TrompetteandGitHub 5fe101545d Add side panel in lab (#10183)
<img width="908" alt="Capture d’écran 2025-02-13 à 11 15 22"
src="https://github.com/user-attachments/assets/642b181c-8a20-4935-bb32-97d9d2b0f4c9"
/>
2025-02-14 13:59:27 +00:00
Charles BochetandGitHub 258025a18f Fix upgrade-0-42 command to migrate RICH_TEXT field metadata to RICH_TEXT_V2 (#10218)
While making sure the upgrade-0.42 command was working as expected to
upgrade from 0.41 to 0.42, we've detected that the standardId of the new
bodyV2 (type RICH_TEXT_V2) was not properly set ; standardId was not
correct.

This was forcing the sync-metadata command to try to re-create the field
again.
2025-02-14 13:50:06 +01:00
Félix MalfaitandGitHub a36ba02efe Imrove I18n ci (#10219) 2025-02-14 13:42:16 +01:00
Raphaël BosiandGitHub 80c55b4462 384 update the input of the record show page inside the command menu (#10213)
Created a new component `RecordTitleCell` with an API close to
`RecordInlineCell`.
This new component is an autogrowing input. 
It consumes the `FieldContext`. It uses some hooks and states from
`RecordInlineCell` because I didn't want to duplicate all the logic, but
this logic could be duplicated.

Two issues that I didn't solve in this PR:
- There is a flashing glitch inside the input when typing
- The input of a workflow isn't focused when creating a new one. This is
because of an issue with the `useHotkeyScopeOnMount` hook which is
deprecated but still used in some components. Upon redirection on the
workflow showpage, the hokey scope of the input is overridden by the
hokey scopes of the components which use `useHotkeyScopeOnMount`. I
decided not to open the input for now.

## Command menu record show page

### Single input


https://github.com/user-attachments/assets/50dc235c-8f34-4445-8b04-586125606bd5

### Double input


https://github.com/user-attachments/assets/bdcfd6eb-d25e-4006-a87f-6e615e8a6e7e

## Workflow breadcrumb


https://github.com/user-attachments/assets/ded38dd6-5794-4779-a4ae-b3948567595a

## Record show page

### Single input


https://github.com/user-attachments/assets/8ad7a606-556a-416b-8788-93415f7989e1

### Double input


https://github.com/user-attachments/assets/55aae40b-36ae-40f1-8171-06f1a5db3532
2025-02-14 12:33:18 +00:00
Antoine MoreauxandGitHub a4a085392d fix(auth): Invite with public url and social sso. (#10216)
Correct the URL parameter name from "inviteHash" to
"workspaceInviteHash" and enhance domain URL handling for
multi-workspace environments.
2025-02-14 11:58:01 +00:00
Antoine MoreauxandGitHub 171091e1ef refactor(domain-manager): simplify frontend URL configuration (#10194)
Replaced multiple environment variables for frontend URL construction
with a single FRONTEND_URL variable. This change reduces complexity and
improves clarity by consolidating frontend URL handling into one source.
Updated relevant validations and removed unused variables like
FRONT_PROTOCOL and FRONT_PORT.

Fix #10016
2025-02-14 12:03:07 +01:00
Charles BochetandGitHub 01665ca8ae Fix bug visibility table (#10214) 2025-02-14 11:48:41 +01:00
WeikoandGitHub 934a97df51 Fix custom exceptions not propagating error code (#10210)
## Context
In some CustomException exceptions, we were instantiating a code without
initializing it which was overriding the parent code and it was then
lost when retrieving it in filters.
Removing them to make sure we don't reproduce this pattern
2025-02-14 10:27:47 +01:00
Antoine MoreauxGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
ea07692abb refactor(ui/settings): update styles and improve structure (#10201)
Adjusted border color in SettingsRadioCard for better visual hierarchy.
Simplified grid template layout in SSO form for consistency. Enhanced
HorizontalSeparator by wrapping text in Label for improved accessibility
and styling.

Fix
https://github.com/twentyhq/core-team-issues/issues/385#event-16262349051

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-02-14 10:19:02 +01:00
Antoine MoreauxandGitHub 8bd2f5c3c4 refactor(server): rename PORT to NODE_PORT (#10193)
Updated the environment variable name from PORT to NODE_PORT for more
clarity and alignment with node server conventions. Adjusted references
in configuration and application bootstrap accordingly.

Fix #10017
2025-02-14 10:03:58 +01:00
Félix MalfaitandGitHub f45f7c28f2 Improve i18n ci (#10209) 2025-02-14 09:56:05 +01:00
Félix MalfaitandGitHub 1090fb8fad Remove localization feature flag and improve CI workflow (#10208) 2025-02-14 09:41:35 +01:00
martmullandGitHub f2da915b20 Poc lambda subhosting (#10192)
Add `SERVERLESS_LAMBDA_SUBHOSTING_ROLE` to allow hosting lambdas in
separate aws account

Tested with old configuration and new configuration
2025-02-14 09:18:49 +01:00
Lucas BordeauandGitHub 9cbcf6293b Fix number null value forced to 0 (#10202)
This PR fixes https://github.com/twentyhq/twenty/issues/9980

In here we implement a check to pass null through instead of converting
it to 0.
2025-02-13 18:15:21 +00:00
Baptiste DevessierandGitHub 81b2d5bc89 Add workflow run visualizer (#10146)
- Remove the tabs from the workflowRun show page; now, we only show the
visualizer with the nodes highlighted based on the run's output
- Create the `generateWorkflowRunDiagram` function to go other each step
and assign a `runStatus` to it based on the workflow run's output

Remaining to do:

- Show the output of each step in the right drawer when selecting one
- The labels (e.g. "1 item") are not set on the edges; we might
implement that later


https://github.com/user-attachments/assets/bcf22f4c-db8c-4b02-9a1a-62d688b4c28e

Closes https://github.com/twentyhq/core-team-issues/issues/338
Closes https://github.com/twentyhq/core-team-issues/issues/336
2025-02-13 18:57:54 +01:00
Lucas BordeauandGitHub 1863ef7d10 Fix bug with table aggregate footer when switching object (#10198)
This PR fixes a bug that happens when switching from an object to
another.

It is a temporary fix because it fixes a problem downstream, that
shouldn't happen, but it is tied to the new implementation of the main
context store.

We should find a way to have context store states defined earlier
because it might impact logic that was developed during the previous
months that didn't anticipated the state of current view id taking more
re-renders to be correctly set between page change / table change.
2025-02-13 18:36:36 +01:00
Paul RastoinandGitHub 5963c0f384 [REFACTOR][BUG] Dynamically compute field to write in cache CREATE (#10130)
# Introduction
While importing records encountering missing expected fields when
writting a fragment from apollo cache

## Updates

### 1/ `createdBy` Default value
When inserting in cache in create single or many we will now make
optimistic behavior on the createdBy value

### 2/ `createRecordInCache` dynamically create `recordGrqlFields`
When creating an entry in cache, we will now dynamically generate fields
to be written in the fragment instead of expecting all of them. As by
nature record could be partial

### 3/ Strictly typed `RecordGqlFields`

# Conclusion
closes #9927
2025-02-13 17:43:54 +01:00
GuillimandGitHub 58a62ec6f0 [settings/security] Prevent toggle if only 1 authentication option (#10169)
fix https://github.com/twentyhq/core-team-issues/issues/387
2025-02-13 15:54:37 +00:00
Antoine MoreauxGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
8a425456f2 feat(workspace): add support for custom domain status toggle (#10114)
Introduce isCustomDomainEnabled field in Workspace entity to manage
custom domain activation. Update related services, types, and logic to
validate and toggle the custom domain's status dynamically based on its
current state. This ensures accurate domain configurations are reflected
across the system.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-02-13 15:01:33 +00:00
Charles BochetandGitHub b67e850011 Fix table content missing when navigating to previously browsed page (#10188)
We are using useInView to detect if a row should be rendered or not.

# Behavior issue

When browsing an Index page (let's say People), then navigating to
another one (Company), then back to People, the rows were not displayed
anymore. For some reason the inView value was set to false in this case

# Fix
- I have updated the useInView (react-intersection-observer) package but
it did not fix it
- useInView provides a ref. However, I believe this was conflicting with
the draggableRef; cecause we are in a <table> we cannot add additional
containers and are forced to apply both refs to the <tr> (draggableRef +
inViewRef). I believe this was causing the issue. I have added the
inView ref to an empty <td> within the row which is achieving the same
goal without forcing to combine refs
2025-02-13 13:40:33 +01:00
Charles BochetandGitHub 41dfeb34d1 Add sync metadata to migrate command (#10182)
Add sync-metdata to 0-42 upgrade command
2025-02-13 13:36:29 +01:00
Antoine MoreauxandGitHub 75268c3040 chore: update eslint configuration and clean up index file (#10185)
Removed eslint disable comment from the index file and updated the
eslint ignore list to exclude the serverless driver index file. This
ensures the file is skipped during linting without unnecessary inline
comments.
2025-02-13 11:35:55 +01:00
Lucas BordeauandGitHub 467aa775a8 Fixed context menu bug with new record filters refactor (#10181)
This PR enforces objectMetadataItem to be passed to hook
useColumnDefinitionsFromFieldMetadata, which was never used without an
objectMetadataItem passed in parameters (and probably shouldn't anyway)

Removed the associated test that tested for an undefined
objectMetadataItem parameter in useColumnDefinitionsFromFieldMetadata.

This allows to remove the need to rely on recordIndexContext to retrieve
the filterableFieldMetadataItems.
2025-02-13 11:15:41 +01:00
Antoine MoreauxandGitHub 77d72e9b1c refactor(auth/sso): rename GetAuthorizationUrl for clarity (#10173)
- Rename `GetAuthorizationUrl` to `GetAuthorizationUrlForSSO`
- Move `GetAuthorizationUrlForSSO` from `sso.resolver.ts` to
`auth.resolver.ts` to avoid the permission guard and let users use an
SSO provider.
- Fix an issue in OIDC guard that breaks the connection if you have
multiple SSO providers + add tests for OIDC guard.
2025-02-13 11:15:22 +01:00
WeikoandGitHub d7b84de1b5 Fix CI after SettingsFeature enum update (#10178) 2025-02-13 09:19:36 +00:00
GuillimandGitHub 372ebd14fe withDelete option to TypeOrm (#10161)
fixes https://github.com/twentyhq/core-team-issues/issues/112
2025-02-13 08:50:24 +01:00
Charles Bochet d2a5027549 Fix contextProvider not set preventing login 2025-02-13 01:03:10 +01:00
Charles Bochet eb6757f99c Fix linter on main 2025-02-13 00:58:14 +01:00
ba8797d220 Remove filterDefinition.type usage (#10164)
This PR essentially removes the usage of filterDefinition.type, by
replacing it with fieldMetadataItem.type derivation. Thus allowing to
completely remove filterDefinition later on.

In computeFilterRecordGqlOperationFilter, emptyOperationFilter is now
returned before going into the big switch case. This avoids repeating
the same exact call to getEmptyRecordGqlOperationFilter for each type.

Fixed some tests that need
getJestMetadataAndApolloMocksAndActionMenuWrapper to have record filters
properly working with the new implementation. We'll probably want to
refactor the record context store, record index context, etc.

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-02-13 00:57:28 +01:00
GuillimandGitHub 6e57f02ae3 Feature flag IS_MICROSOFT_SYNC_ENABLED removal (#10170)
Since we have already removed the feature flag from twenty-infra, and
the most part of the application. we can now remove it from these
declaration

fixes https://github.com/twentyhq/core-team-issues/issues/331
2025-02-13 00:52:31 +01:00
Charles BochetandGitHub 466f8c733f Fix latest visited view (#10158)
Fixes https://github.com/twentyhq/twenty/issues/9772

In this PR:
- the root cause of the issue that the ContextStoreViewIdEffect was not
filtering the views on objectMetadata properly
- I'm also deleting some over complex in the latestVisited view logic
- Duplicated logic between ContextStoreViewIdEffect and
ViewBarViewIdEffect, see my comment
2025-02-13 00:52:04 +01:00
8f69352d17 i18n - translations (#10174)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 21:20:38 +01:00
0fa03aeb2c i18n - translations (#10172)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 19:21:30 +01:00
WeikoandGitHub 193ef432a0 add object settings permission tab (#10159)
## Context
Introducing the "Permissions" tab in the role page

Next: Need to address some css improvements, some components might be
reusable and it still does not fully match the figma (icon missing for
permission types for example). We decided to merge like this for now so
we have something functional and I will update the code in an upcoming
PR

<img width="633" alt="Screenshot 2025-02-12 at 13 54 16"
src="https://github.com/user-attachments/assets/762db5d7-e0a6-4ee1-b299-24de6645bad1"
/>
2025-02-12 18:49:50 +01:00
WeikoandGitHub 61881d6d0f Fix relation creation (#10171)
## Context
We recently introduced a createMany on the field metadata service to
improve seeding performances. This broke relation metadata creation
because it was using a method with the same name that was inherited from
TypeOrmQueryService.
2025-02-12 18:49:26 +01:00
EtienneandGitHub 0609b31c64 add WorkspaceDuplicateCriteria decorator + update duplicate resolver logic (#10128)
## Context

All objects have '...duplicates' resolver but only companies and people
have duplicate criteria (hard coded constant).
Gql schema and resolver should be created only if duplicate criteria
exist.

## Solution

- Add a new @WorkspaceDuplicateCriteria decorator at object level,
defining duplicate criteria for given object.
- Add a new duplicate criteria field in ObjectMetadata table
- Update schema and resolver building logic
- Update front requests for duplicate check (only for object with
criteria defined)



closes https://github.com/twentyhq/twenty/issues/9828
2025-02-12 17:32:59 +01:00
martmullandGitHub b66289c44c replace log by throw (#10167)
as title
2025-02-12 16:31:41 +00:00
fb936cccfb i18n - translations (#10168)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 17:11:13 +01:00
martmullandGitHub 39e7a0b61c Remove log to fix serverless function (#10166)
as title
2025-02-12 15:39:00 +00:00
martmullandGitHub c30f416912 Add log (#10162)
add log to fix issue in staging
2025-02-12 15:01:34 +00:00
f98668febf i18n - translations (#10160)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 15:20:02 +01:00
GuillimandGitHub f3586d4989 HandleAlias (#10156)
If a user has microsoft aliases, they will be taken into account, in a
similar way as we did for google
2025-02-12 14:22:13 +01:00
2b24bf43a4 i18n - translations (#10157)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 13:22:35 +01:00
d2d3b21c90 consistent background color in notes (#10097)
fixes #10086 

![image](https://github.com/user-attachments/assets/c8233d63-9020-457f-9ce1-4c88d6dc2019)

![image](https://github.com/user-attachments/assets/3deeb644-eeb1-4073-9ee8-dd08745282f6)

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
2025-02-12 17:15:26 +05:30
23d2e54439 RICH_TEXT_V2 upgrade command (#10094)
Adds two migration commands:
- copy note and task `body` data to `bodyV2`
- hide `body` view field and swap position with `bodyV2` view field

Related to issue https://github.com/twentyhq/twenty/issues/7613

---------

Co-authored-by: ad-elias <elias@autodiligence.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-02-12 12:26:29 +01:00
Thomas TrompetteandGitHub 33af71ccd3 Create base search record action (#10135)
Create Search Record action without filters neither sorting

<img width="234" alt="Capture d’écran 2025-02-11 à 18 19 25"
src="https://github.com/user-attachments/assets/f18caaa3-476a-436f-9f93-1ad506b24da2"
/>
<img width="262" alt="Capture d’écran 2025-02-11 à 18 19 38"
src="https://github.com/user-attachments/assets/25fcbfcf-57fb-476f-aba9-119be7c3e067"
/>
<img width="236" alt="Capture d’écran 2025-02-11 à 18 19 53"
src="https://github.com/user-attachments/assets/1eb2be25-0727-4797-868c-afdc05040e6a"
/>
2025-02-12 10:29:32 +00:00
9cc6ea501a i18n - translations (#10149)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 11:20:24 +01:00
MarieandGitHub e4ae76ac20 [permissions] Add permission gates on API & Webhooks + Security settings (#10133)
Closes https://github.com/twentyhq/core-team-issues/issues/312
Closes https://github.com/twentyhq/core-team-issues/issues/315
2025-02-12 09:40:26 +00:00
08fd227049 i18n - translations (#10145)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 09:21:13 +01:00
Félix MalfaitandGitHub 2c0f66dab6 Progress on translations (#10142) 2025-02-12 08:13:29 +01:00
b55a366e2f i18n - translations (#10144)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 03:43:55 +01:00
1d4bf2c576 i18n - translations (#10143)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-12 01:32:33 +01:00
Thomas TrompetteandGitHub 959046ee12 Prevent data visualizer from fetching workflows when not enabled (#10116)
- Use existing active objects filter
- Add memoization on active objects. Otherwise the reference will keep
changing into
[SettingsDataModelOverviewEffect.tsx](https://github.com/twentyhq/twenty/compare/tt-fix-data-visualizer?expand=1#diff-737ffd4d69acac03975f83069add9503ef3d0a744b56c9760a2d386f1f50e85c),
which will cause infinite re-renders

With workflows
<img width="694" alt="Capture d’écran 2025-02-10 à 18 28 04"
src="https://github.com/user-attachments/assets/72b1d707-4bf7-4920-a442-92f3e685978f"
/>


No workflows
<img width="694" alt="Capture d’écran 2025-02-10 à 18 28 19"
src="https://github.com/user-attachments/assets/0fe54ed4-6776-4e70-ae6a-8e7a5c59e0a0"
/>
2025-02-11 23:36:11 +01:00
6faf356be2 i18n - translations (#10141)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-11 23:19:57 +01:00
d5b247fa6b i18n - translations (#10140)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-11 21:20:29 +01:00
nitinandGitHub 5f8fc8cad8 minor env tab description font color fix (#10139)
Before: 

<img width="630" alt="Screenshot 2025-02-12 at 00 48 14"
src="https://github.com/user-attachments/assets/0df50cc6-be0c-4286-9883-2a9d23f4f9d9"
/>

After:

<img width="628" alt="Screenshot 2025-02-12 at 00 47 54"
src="https://github.com/user-attachments/assets/cb02c127-fb01-493f-8183-e558449c0193"
/>
2025-02-12 01:03:19 +05:30
88ab7cb553 i18n - translations (#10137)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-11 19:21:13 +01:00
Lucas BordeauandGitHub b757a37d07 Fix bug where aggregate resets record filters (#10136)
This PR fixes a bug where setting an aggregate in the footer of a record
table makes the currentRecordFilters state being overwritten by the
view, which we don't want.
2025-02-11 19:14:18 +01:00
252922b522 Admin panel refactor (#10119)
addressing > 
There are two patterns to avoid:
Creating functions that return JSX like renderThing() -> this was taken
already addressed in https://github.com/twentyhq/twenty/pull/10011
Making a hook that "stores" all the logic of a component - > this PR is
addressing this particular pattern
In essence, handlers should remain in the component and be connected to
their events.
And everything in a handler can be abstracted into its dedicated hook.
For example:
const { myReactiveState } =
useRecoilValue(myReactiveStateComponentState);
const { removeThingFromOtherThing } = useRemoveThingFromOtherThing();

const handleClick = () => {
  if (isDefined(myReactiveState)) {
    removeThingFromOtherThing();
  }
}

Broadly speaking, this is how you can split large components into
several sub-hooks.

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-02-11 17:10:28 +00:00
martmullandGitHub 83bf2d1739 Webhook follow up (#10124)
- fix webhook creation
- fix pointer
2025-02-11 16:30:11 +00:00
5252b22b64 i18n - translations (#10131)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-02-11 17:21:21 +01:00
GuillimandGitHub a4806b72c7 folders (#10081)
# Folders

Adding the possibility to synchronize messages form more than one
microsoft folder (think "inbox" or "sent items")

It will keep the current way for gmail.

- step 1 : implement a first version of full message & partial message 
- step 2 : implement retro-compatibility which includes the command to
run the migration to backfill microsoft synccursor from messageChannelt
o messageFolders
2025-02-11 16:19:53 +00:00
Félix MalfaitandGitHub b4fd408109 Update i18n-compile.yaml (#10132) 2025-02-11 17:19:42 +01:00
ed4a5b0c15 fix: many fields in an object (#10061)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-02-11 17:15:30 +01:00
Paul RastoinandGitHub 47f262c970 [REFACTOR][BUG] Dynamically compute field to write in cache UPDATE & DELETE (#10079)
# Introduction
At the moment when updating any record cache occurence, we will build a
fragment that will expect all of the object metadata item fields to be
provided.
Which result in the following traces: ( in the video companies aren't
fetch with companyId and other missing fields )


https://github.com/user-attachments/assets/56eab7c1-8f01-45ff-8f5d-78737b788b92

By definition as we're using graphql we might not request every record's
fields each time we wanna consume them.
In this way we will now dynamically compute or expect depending on the
CRUD operation specific fields to be written in the cache, and not all
of them

Tested all optimistic and failure management use cases

## Covering cache
Added coverage only for the `deleteOne` and `deleteMany` hooks, it cover
only the record record cache and not its relations hydratation ( for the
moment )

## Why not closing #9927 
Unless I'm mistaken everything done here have fixed the same logs/traces
issue for updates and deletion but not creation.
Which means we still need to investigate the mass upload from import and
prefillRecord behavior

In a nutshell: went over each `updateRecordFromCache` calls, still need
to do all `createRecordInCache` calls

related to #9927 

## Conlusion
Sorry for the big PR should have ejected into a specific one for the
`MinimalRecord` refactor
Will also continue covering others hooks later in my week as for the
`deleteOne`
As always any suggestions are welcomed !
2025-02-11 16:21:44 +01:00
Félix MalfaitandGitHub 05403ae247 I18n CI (#10129) 2025-02-11 15:58:17 +01:00
0307815e84 i18n - translations (#10126)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-02-11 15:50:25 +01:00
Félix MalfaitandGitHub dabb140b3e Update i18n-push.yaml (#10127) 2025-02-11 15:38:55 +01:00
Félix MalfaitandGitHub cc68deaab1 Translations cleaning / workflows (#10125) 2025-02-11 15:26:21 +01:00
Thomas TrompetteandGitHub 6da9976193 Fix readonly in some steps (#10121)
- readonly in titles
- readonly in code step
- update a few icons
- update event label
2025-02-11 14:22:18 +00:00
WeikoandGitHub 02ced028e5 add role assignment page (#10115)
## Context
This PR introduces the "assignment" tab in the Role edit page, currently
allowing admin users to assign workspace members to specific roles.

Note: For now, a user can only have one role and a modal will warn you
if you try to re-assign a user to a new role.

## Test
<img width="648" alt="Screenshot 2025-02-10 at 17 59 21"
src="https://github.com/user-attachments/assets/dabd7a17-6aca-4d2b-95d8-46182f53e1e8"
/>
<img width="668" alt="Screenshot 2025-02-10 at 17 59 33"
src="https://github.com/user-attachments/assets/802aab7a-db67-4f83-9a44-35773df100f7"
/>
<img width="629" alt="Screenshot 2025-02-10 at 17 59 42"
src="https://github.com/user-attachments/assets/277db061-3f05-4ccd-8a83-7a96d6c1673e"
/>
2025-02-11 14:51:31 +01:00
Baptiste DevessierandGitHub 179d3ae2a4 Add workflow success edge (#10120)
- Refactor the handles: the source handles are now part of the edges as
markerStart
- **As the source handles are now part of the edges, we can delete the
`markLeafNodes` logic; this can be done in another PR**. See
https://github.com/twentyhq/core-team-issues/issues/386
- Create a custom edge component for the default edge
- Create a custom edge component for the success edge; this includes a
label

**The edges can be tested in Storybook. I wrote two stories for the
edges.**

| Default | Success |
|--------|--------|
| ![CleanShot 2025-02-11 at 11 46
09@2x](https://github.com/user-attachments/assets/c7c42328-6502-4c77-bdc9-dea825d4651a)
| ![CleanShot 2025-02-11 at 11 46
16@2x](https://github.com/user-attachments/assets/572204de-299c-4cbc-9900-46744b59c351)
|
2025-02-11 13:01:11 +00:00
4f06b83d7f RICH_TEXT_V2 frontend (#10083)
Adds task and note support for the new `bodyV2` field. (Field metadata
type of `bodyV2` is `RICH_TEXT_V2`.)

Related to issue https://github.com/twentyhq/twenty/issues/7613

Upgrade commands will be in separate PRs.

Fixes https://github.com/twentyhq/twenty/issues/10084

---------

Co-authored-by: ad-elias <elias@autodiligence.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-02-11 10:21:03 +00:00
Antoine MoreauxandGitHub de91a5e39e chore(twenty-server): remove eslint warn + add maxWarning 0 (#10103) 2025-02-11 10:38:43 +01:00
Antoine GCandGitHub 59af303b4c Update docker-compose.mdx (#10118)
Add details on how to configure Twenty when using docker compose
2025-02-11 00:35:16 +01:00
EtienneandGitHub 3eaafbde55 fix log + add 3 indexes on fielMetadata and indexFieldMetadata (#10113) 2025-02-11 00:34:33 +01:00
Thomas TrompetteandGitHub b5736a28fd Fix workflow activation optimistic rendering in Cmd+K (#10109)
Close
https://discord.com/channels/1130383047699738754/1334441759484149793

Using refetch queries was not working for certain use cases.
To find manual active workflows in cmd+k, we use a query with specific
filters that was complicated to refetch.

Finally I will update the cache manually. It was not properly updated
before because the json value of the version trigger was stringified
without spaces. So the entity was not found in apollo cache.
2025-02-10 16:46:36 +00:00
GuillimandGitHub d52c7ffd73 we don't create companies for personal domain emails anylonger (#10110)
and we reomve an usued function
2025-02-10 16:34:35 +00:00
0083f0b939 chore: add translations (#10112)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-10 17:16:03 +01:00
5948bc2685 Fix: Get rid of subgroups in admin/env-variables (#10105)
closes https://github.com/twentyhq/core-team-issues/issues/379

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-10 16:54:37 +01:00
martmullandGitHub c07f43fcb1 Simplify webhook creation flow (#10107)
## Before


https://github.com/user-attachments/assets/6bc61970-f0e2-4826-bf95-2b0c9fff5113


## After
- no new webhook form anymore
- autosave on update


https://github.com/user-attachments/assets/c7a304ec-76f5-4c2b-ac5e-7a846bd7f23b

@Bonapara ok for you?
2025-02-10 16:48:51 +01:00
Antoine MoreauxandGitHub f733307517 refacto(*): rename hostname to custom domain (#10100) 2025-02-10 15:12:36 +00:00
2a0f937899 Hide inactive objects and fields (#10104)
This PR modifies the data model visualizer to only show objects and
fields which are active.

- Added `isActive` check in the filter within the useEffect of the
`packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewEffect.tsx`
to filter out objects that are inactive
- Also added `isActive` check to the `StyledInnerCard` in the file
`packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx`
within the filter to filter out fields that have been marked inactive

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-10 15:45:09 +01:00
Félix MalfaitandGitHub 62e44de831 Fix issue with pr-PT and pt-BR casing (#10106)
Fix a casing issue affecting MacOS users
2025-02-10 15:25:36 +01:00
Mohammed Abdul Razak WahabandGitHub 6ae80c5f93 hide inactive objects (#10099)
fixes #10090
2025-02-10 15:19:02 +01:00
EtienneandGitHub d4ffd52988 add invalid captcha and messageChannel sync status health monitoring (#10029)
Context : 
We want to implement some counters to monitor server health. First
counters will track : messageChannel sync status during job execution
and invalid captcha.

How : 
Counters are stored in cache and grouped by one-minute windows.
Controllers are created for each metric, aggregating counter over a
five-minutes window.
Endpoints are public and will be queried by Prometheus.

closes https://github.com/twentyhq/core-team-issues/issues/55
2025-02-10 11:24:42 +00:00
Thomas TrompetteandGitHub e70e69cf94 Store output step by step (#10101)
- add context field
- store it with the output after each step execution
2025-02-10 11:27:15 +01:00
martmullandGitHub c908a687fb Fix doc (#10102)
## Why
Lots of sefl-hosted Help thread require to check if the worker is
running

This PR adds a new section to document **How to check the worker is
running?**

![image](https://github.com/user-attachments/assets/b2cbea68-0788-4ca0-9491-1dbc8264c688)
2025-02-10 11:22:47 +01:00
d4b23e020a chore: add translations (#10098)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-10 10:29:13 +01:00
Antoine MoreauxandGitHub 1b98f40f17 feat(custom-domain): enable UI for custom domain (#10062) 2025-02-10 08:43:13 +00:00
Félix MalfaitandGitHub f8c653f153 Iterations on i18n CI (#10093) 2025-02-10 08:27:09 +01:00
Félix MalfaitandGitHub f9d7f62ec2 I18n CI and small type improvement (#10092) 2025-02-09 22:17:54 +01:00
Félix MalfaitandGitHub bb24c97f80 Translations - Crowdin, Set workspace member locale on signup, and optimizations (#10091)
More progress on translations:
- Migrate from translations.io to crowdin
- Optimize performance and robustness 
- Set workspaceMember/user locale upon signup
2025-02-09 22:10:41 +01:00
Raphaël BosiandGitHub fd3f01ab80 Focus on the input on command menu page change (#10082)
Add effect component to focus on the input on command menu page change

Before:


https://github.com/user-attachments/assets/a066b5b4-d495-42ca-8c13-8cc456eaaeda

After:


https://github.com/user-attachments/assets/1abd06dc-5714-44b5-80e4-22b55dc341c5
2025-02-07 18:56:30 +01:00
Raphaël BosiandGitHub acae5f910d Reset search bar when clicking on search action (#10076)
Before:


https://github.com/user-attachments/assets/c10f990a-6446-4436-b687-5a0384a461bf


After:


https://github.com/user-attachments/assets/1a6bcb26-9079-4f39-b16a-01cad1ece435
2025-02-07 18:27:54 +01:00
Raphaël BosiandGitHub c16cbc6fd5 Updates in the workflow actions config (#10080)
- Update discard draft icon
- Pin `Remove from favorites` action
- Update workflow action labels to add clarity

Note: this is a small PR, the number of modified lines is due to the
translations
2025-02-07 16:26:25 +00:00
Lucas BordeauandGitHub f74028d9bb Fix soft delete filter hook bug with actions (#10077)
This PR fixes a bug that happened when we open the command menu with
multiple records selected.

The problem was that the new useCheckIsSoftDeleteFilter hook depended on
RecordIndexContext which can be undefined in the command menu context.

Because our direction right now is not completely clear with
RecordIndexContext and ContextStore, in this PR I just removed the
dependency and used objectMetadataItems state directly so that the hook
has no dependency anymore.

Also renamed useFilterableFieldMetadataItems to
useFilterableFieldMetadataItemsInRecordIndexContext.
2025-02-07 17:24:17 +01:00
martmullandGitHub ead626c2ec 360 workflow implement workflow cron triggers frontend 2 (#10051)
as title, closes https://github.com/twentyhq/core-team-issues/issues/360

## Cron Setting behavior

https://github.com/user-attachments/assets/0de3a8b9-d899-4455-a945-20c7541c3053

## Cron running behavior


https://github.com/user-attachments/assets/4c33f167-857c-4fcb-9dbe-0f9b661c9e61
2025-02-07 17:15:03 +01:00
Raphaël BosiandGitHub 988ab9697c Command menu list design updates (#10075)
- Add 2px gap between items
- Update `MenuItemCommand` style to distinguish `hovered` and `selected`
states



https://github.com/user-attachments/assets/d1e29f07-32e7-4ace-9aa1-0ea83712f052
2025-02-07 16:55:24 +01:00
MarieandGitHub a24e411384 [permissions] Add SettingsPermissionGuard on data model and roles features (#10063)
Adding SettingsPermissionsGuard to execute permission check. 

The guard is added directly in resolver, either at resolver level (ex:
roles) or resolver-endpoint level (ex: metadata). this can be challenged
!
2025-02-07 16:48:04 +01:00
MarieandGitHub 859e7c94f9 [permissions] Add settingsPermissions to getCurrentUser (#10054)
For a member with admin role:
<img width="392" alt="Capture d’écran 2025-02-06 à 15 04 07"
src="https://github.com/user-attachments/assets/f47e8611-9577-4d0b-889c-0846acfb0d75"
/>

For a member without admin role:
<img width="394" alt="Capture d’écran 2025-02-06 à 15 04 51"
src="https://github.com/user-attachments/assets/5daacd7a-aa4f-4e06-a886-661bf0830418"
/>

For a member of a workspace that does not have the feature flag enabled:
<img width="390" alt="Capture d’écran 2025-02-06 à 15 05 22"
src="https://github.com/user-attachments/assets/05071bd6-fd2d-4823-b121-8fd11313b833"
/>
2025-02-07 15:33:17 +01:00
Raphaël BosiandGitHub 08b8a0cc60 Do not display back button at the root level of the command menu (#10073)
Do not display back button at the root level of the command menu
2025-02-07 15:33:02 +01:00
Paul RastoinandGitHub aee43b10ad [BUG] Avoid backfilling record position on update one (#10069)
# Introduction
Disabled backfilling position process on single record update

It might be a too global solution ? feels a bit too easy tbh
Please let me know, could not think of other side-effects

close [#9925](https://github.com/twentyhq/twenty/issues/9925)
2025-02-07 14:19:58 +00:00
Félix MalfaitandGitHub 22c9acf993 Remove session store secret (#10074)
Fixes #10033
2025-02-07 14:58:50 +01:00
Raphaël BosiandGitHub 1403c55625 321 command menu context chips compact version (#10072)
Closes https://github.com/twentyhq/core-team-issues/issues/321

- Create component
- Create stories
- Fix bug due to `WorkflowDiagramCanvasEditableEffect`
2025-02-07 13:48:41 +00:00
Antoine MoreauxandGitHub 68183b7c85 feat(): enable custom domain usage (#9911)
# Content
- Introduce the `workspaceUrls` property. It contains two
sub-properties: `customUrl, subdomainUrl`. These endpoints are used to
access the workspace. Even if the `workspaceUrls` is invalid for
multiple reasons, the `subdomainUrl` remains valid.
- Introduce `ResolveField` workspaceEndpoints to avoid unnecessary URL
computation on the frontend part.
- Add a `forceSubdomainUrl` to avoid custom URL using a query parameter
2025-02-07 14:34:26 +01:00
Baptiste DevessierandGitHub 3cc66fe712 Remove the source handle for leaf nodes (#10057)
- Do not render a source handle for the leaf nodes
- Upgrade the `@xyflow/react` library

| Before | After |
|--------|--------|
| ![CleanShot 2025-02-06 at 16 21
08@2x](https://github.com/user-attachments/assets/42b7d11b-76bf-43b9-ba91-8d0c5c2f1792)
| ![CleanShot 2025-02-06 at 16 21
24@2x](https://github.com/user-attachments/assets/ac94aa32-45ad-4462-8db9-0078d6252ea4)
|

## Other options considered

React Flow exposes a hook to get the connections of the current node. I
tried to use this hook – which makes things way simpler – but I couldn't
find a way to make it work in Storybook. I had two options: 1. Set up
React Flow to render the nodes properly, 2. Mock the hook in Storybook.

The first option was hard to achieve as the `<Reactflow />` component
renders a whole flow, and it doesn't play well with the idea of
rendering a single node in a story.

The second option seemed overkill as mocking modules with Storybook is
not straightforward. See
https://storybook.js.org/docs/writing-stories/mocking-data-and-modules/mocking-modules.

I chose to keep the initial version of my code, written before I spot a
function simplifying the code. We can give it a look another time.
2025-02-07 13:17:43 +01:00
Lucas BordeauandGitHub 30e4fdbd06 Refactor duplication of hard coded soft delete filter logic (#10058)
This PR adds a useCheckIsSoftDeleteFilter hook instead of the
undocumented in-place logic to retrieve the soft delete filter.

Also took the opportunity to refactor a recent change of @prastoin with
it.

Split VariantFilterChip into SoftDeleteFilterChip and RecordFilterChip
to separate concerns about this soft delete filtering.
2025-02-07 10:03:13 +00:00
Antoine MoreauxandGitHub e081d8ab5e refactor(auth): simplify continueWithEmail and remove useCallback (#10068)
Refactored continueWithEmail to remove unnecessary dependencies and
simplified the useIsMatchingLocation hook by eliminating useCallback.
This reduces complexity and addresses potential infinite loop issues.
2025-02-07 09:50:23 +00:00
Antoine MoreauxandGitHub aaea49d5f5 fix(auth): prevent infinite redirect (#10065)
Refactored the isMatchingLocation function to use useCallback for
memoization, improving performance by avoiding unnecessary re-creations.
Moved addTrailingSlash and getConstructedPath inside useCallback for
better encapsulation.
2025-02-07 09:27:11 +00:00
Félix MalfaitandGitHub 8df59c085d Lingui working with NODE ENV=production again (#10067)
Lingui now offers an option to disable stripping even in prod mode so we
can bring it back
2025-02-07 10:05:07 +01:00
Félix MalfaitandGitHub 12cec01d80 Update billing to EE (#10060)
Doesn't make sense to have billing under GPL since it's only used for
our cloud and not by self-hosters
2025-02-06 21:10:30 +01:00
Antoine MoreauxandGitHub 005762c240 fix(auth): reset signInUp state when email change (#9990)
When you use a prefilled email the web app checks the db to know if the
user exists. If not the web app enter in signUp mode.
If you changed your email after, the signup mode was not reset.

This PR fixes that.

Fix the error with the message "Invalid sign in up params".
2025-02-06 19:53:31 +01:00
Baptiste DevessierandGitHub c27e930293 Make the e2e ci pass (#10059)
An E2E test always fails due to an identified bug. Since it's best to
keep the E2E CI pass rather than get used to it being broken, I mark the
test as needing to be fixed.

The identified bug:
https://discord.com/channels/1130383047699738754/1337002448602005567
2025-02-06 17:11:39 +01:00
1b150e1da6 Environment variables in admin panel (read only) - front (#10011)
Frontend for https://github.com/twentyhq/core-team-issues/issues/293

POC - https://github.com/twentyhq/twenty/pull/9903

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-02-06 17:08:44 +01:00
Antoine MoreauxandGitHub a85c4f263a fix(settings routing): handle trailing slashes in base paths (#10055)
Adjusted URL construction to properly handle trailing slashes in base
paths, ensuring consistent matching logic. Added logic for setting the
hotkey scope when navigating to the domain settings path.
2025-02-06 16:07:19 +00:00
Antoine MoreauxandGitHub 5b79ac771c fix(auth): update error message in sign-in-up service test (#10056)
Revised exception message for better clarity when a user is not part of
the workspace. Adjusts test expectations to match the updated error
output.
2025-02-06 16:33:41 +01:00
Félix MalfaitandGitHub f40d7e2ba8 Deprecate message queue type (#10040)
Not removing all the code for now, maybe we should 🤔
2025-02-06 16:06:54 +01:00
GuillimandGitHub adc6dd5c07 signup sync with microsoft (#10008)
We want users to be able to sync their emails with microsoft duting
signup

We also want to respect the env ariables that decide if we allow
messaging and calendar providers.
```
MESSAGING_PROVIDER_GMAIL_ENABLED=false
MESSAGING_PROVIDER_MICROSOFT_ENABLED=false
CALENDAR_PROVIDER_GOOGLE_ENABLED=false
CALENDAR_PROVIDER_MICROSOFT_ENABLED=false
```

# design

https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=53729-251492&t=1cwVzB0xFAom9PlM-0
2025-02-06 14:29:21 +01:00
Antoine MoreauxandGitHub d85b8bef4e fix(signin): allow to signin in pending creation workspace (#10052) 2025-02-06 14:05:19 +01:00
WeikoandGitHub e849378726 Add role edit page container (#10037)
## Context

This PR adds a new SettingsRoleEdit page, the existing roles page now
redirects to the role edition page when clicking on it.
For now, we can't edit anything. Next step is to allow role assignment
in the corresponding tab.

<img width="941" alt="Screenshot 2025-02-05 at 17 16 14"
src="https://github.com/user-attachments/assets/ca46de15-6237-4de6-88e1-2384a09d4a27"
/>
2025-02-06 11:27:56 +01:00
MarieandGitHub 8a660d5d3f [permissions] Avoid N+1 on roles (#10041) 2025-02-06 11:27:11 +01:00
Lucas BordeauandGitHub 049a0118aa Record filters - Introduced fieldMetadataItemUsedInDropdown instead of filterDefinitionUsedInDropdown (#10044)
This PR progressively introduces fieldMetadataItemUsedInDropdown instead
of filterDefinitionUsedInDropdown where most easy to replace.

This allows to use `fieldMetadataItemUsedInDropdown.id` instead of
`filterDefinition.fieldMetadataId`, which is one easy dependency to
remove on filter definition.

We still derive filterDefinition instead of fully replacing it, because
it will be easier to remove RecordFilterDefinition usage in a bottom-up
approach instead.

In multiple components of the filter dropdown, we try to replace
filterDefinition by fieldMetadataItem derivation : Icon, label, id,
type, etc.

We also introduce the usage of subFieldNameUsedInDropdown instead of
storing it dynamically on filterDefinition, for handling filtering on
composite sub fields.

The method `formatFieldMetadataItemAsFilterDefinition()` that is used to
derive filterDefinition from fieldMetadataItem is what was being used
originally to create the availableFilterDefinition state. (That is
already removed)

Fixed associated unit tests accordingly.
2025-02-06 11:03:55 +01:00
Baptiste DevessierandGitHub e21cbb2fe2 Simplify the component layers for workflow nodes (#10042)
- Remove the `WorkflowDiagramBaseStepNode` component
- Create the `WorkflowDiagramStepNodeIcon` component to centralize the
icon put in workflow step nodes; that was the main task of the
`WorkflowDiagramBaseStepNode` component and the new
`WorkflowDiagramStepNodeIcon` component made it obsolete
- Update the `WorkflowDiagramStepNodeBase` component to be THE low level
component
2025-02-06 10:42:32 +01:00
Raphaël BosiandGitHub 9838bec004 259 add navigation hotkeys for command menu level 2 (#10043)
Closes https://github.com/twentyhq/core-team-issues/issues/259



https://github.com/user-attachments/assets/abdcb44d-fc13-4597-befc-da99c94c1ef0
2025-02-06 10:07:20 +01:00
Antoine MoreauxandGitHub f6ce27b61e refactor(security): extend SAML prefix handling (#10047)
Revised parsing logic to handle multiple XML prefixes for SAML metadata,
improving flexibility in handling diverse metadata structures. Added
corresponding test case to ensure robustness of the implementation.
2025-02-05 21:46:38 +01:00
Baptiste DevessierandGitHub 700eb2d473 Add the new workflow not executed node type (#10030)
- Added the new workflow `not executed` node type
- Fixed minor style issues
- Created one big catalog for all node variants


![image](https://github.com/user-attachments/assets/5e510d49-c6a2-42a9-9641-057cff481dd9)
2025-02-05 18:43:46 +01:00
Raphaël BosiandGitHub 5528577707 10004 tasks will not delete in people view (#10039)
Fixes #10004 
- Fixed `useListenClickOutside` which wasn't working with
`excludeClassNames` for `comparePixels` mode
- Added `emitCloseEvent` parameter to the `closeRightDrawer` function
because closing the right drawer after deleting a note or a task was
triggering an update after the deletion.

This bug was only for the old version of the command menu.
2025-02-05 18:25:39 +01:00
MarieandGitHub e3182a145d Implement updateRole (#10009)
In this PR, we are implementing the updateRole endpoint with the
following rules

1. A user can only update a member's role if they have the permission (=
the admin role)
2. Admin role can't be unassigned if there are no other admin in the
workspace
3. (For now) as members can only have one role for now, when they are
assigned a new role, they are first unassigned the other role (if any)
4. (For now) removing a member's admin role = leaving the member with no
role = calling updateRole with a null roleId
2025-02-05 18:02:14 +01:00
ef1b492e2f Lab image cover (#10027)
Added Object-Cover to eliminate image distortion. Set all cards to the
large variant to enhance the design.

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
2025-02-05 22:24:23 +05:30
Paul RastoinandGitHub f31ee88fe8 [NITPICK] Avoid duplicated action position index in DefaultActionsConfigV2 (#10031)
# Introduction Nitpick
Action's record has two entries with `position: 0`
Unless I'm mistaken I could not find any place where the `position`
property is consumed
So this is just for sake of the +1 suite
2025-02-05 16:20:57 +01:00
Paul RastoinandGitHub dadee81175 [FEAT] Destroy many Workflow actions (#10028)
# Introduction
Added destroy many records to the `WorkflowActionConfig`

## Repro:
- Filter by deleted workflow
- Select mulitple rows
- Click on destroy

# Pinned action standardization
After discussion with @Bonapara and @bosiraphael we landed on a
standardization of the pinned actions between `DefaultActionsConfigV2`
and `WorkflowActionConfig` which means that now are pinned the `DELETE`
and `DESTROY` for both `single/multiple` modes for both features

Related to https://github.com/twentyhq/twenty/pull/9991
2025-02-05 16:16:13 +01:00
Paul RastoinandGitHub 710366514c BaseObjectRecord and__typename naming updates (#10024)
Folllowing:
https://github.com/twentyhq/twenty/pull/10014/commits/9049c47fd24c55dc7b03227a99587aadf651854d
From PR https://github.com/twentyhq/twenty/pull/10014
2025-02-05 14:51:30 +01:00
Paul RastoinandGitHub c3c800b097 Post #10014 merged nitpicks followup (#10021)
## Only nitpicks
Method signature mutation from several to one record arg
Renamed a variable in map to fit related business logic

#10014 Followup
2025-02-05 14:28:17 +01:00
Raphaël BosiandGitHub 5c24cf4084 320 new command menu navigation bar buttons (#10018)
Closes https://github.com/twentyhq/core-team-issues/issues/320


https://github.com/user-attachments/assets/8082e986-07fd-46fb-9652-ad006aa9dac8
2025-02-05 13:25:29 +00:00
36d148d5e5 Fetch roles in roles settings page (#10001)
## Context
Following the addition of the new Roles page, we are now fetching roles
from the DB thanks to this PR #9955

## Test
<img width="1136" alt="Screenshot 2025-02-04 at 14 46 21"
src="https://github.com/user-attachments/assets/2c55c4d0-ee51-47bb-8113-efce172a9365"
/>

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-02-05 14:22:00 +01:00
Paul RastoinandGitHub 3e05c3743e [BUG][PROD] Fix ViewGroup creation optimistic cache (#10014)
# Introduction
When we create a new `view` from record table that has relation such as
opportunities.
Encountered invariant conditions:

## Unknown fiel `__typename`
`Should never occur, encountered unknown fields __typename in
objectMetadaItem viewGroup`,

### Fixed by ignoring unknown internal fields


## Provided both relation `view` and `viewId`
`Should never provide relation mutation through anything else than the
fieldId e.g companyId and not company, encountered: view`

### Fixed by sending only `viewId` to `createManyRecords` in
`usePersistViewGroupRecords.ts`
2025-02-05 12:22:45 +01:00
martmullandGitHub 736b845c98 335 workflow implement workflow cron triggers backend (#9988)
[Backend side] Add cron triggers to workflow
Closes https://github.com/twentyhq/core-team-issues/issues/335
2025-02-05 11:02:49 +00:00
Lucas BordeauandGitHub 074cc113ac Implement query variables in useCombinedFindManyRecords (#10015)
Implements filtering, ordering and cursor filtering for the hook
useCombinedFindManyRecords, because it was not implemented, which was
misleading because variables could be passed to it.

The difficult part was to make sure that the cursor filtering was
working, both before and after a cursor, because it was only hard coded
for last cursor (equivalent to after).

The duplicate limit parameter in the type RecordGqlOperationVariables
was merged into one limit parameter, because it was making the developer
guess how both could be handled.

This single limit parameter can be used for either : general limit
without cursor, first records from after cursor, last records until
before cursor. Since those cases are exclusive it's better to have only
one limit parameter and have an internal logic handling those cases.

Tests were added on the relevant parts, especially
useCombinedFindManyRecordsQueryVariables which requires its own unit
test to handle this cursor + limit logic.

Record show page pagination was tested to make sure removing the
duplicate limit parameter had no impact.
2025-02-05 11:59:38 +01:00
Paul RastoinandGitHub 28a3f75946 [FEAT] RecordAction destroy many record (#9991)
# Introduction
Added the `RecordAction` destroy multiple record

## Repro
Select multiples `deletedRecords`, you should be able to see the
`Destroy` pinned CTA ( iso short label with the destroy one ), open
control panel and fin new CTA `Permanently delete records`


https://github.com/user-attachments/assets/31ee8738-9d61-4dec-9a1f-41bb6785e018

## TODO
- [ ] Gain granularity within tests to assert the action should be
registered only when filtering by deleted

## Conclusion
Closes https://github.com/twentyhq/core-team-issues/issues/110
2025-02-05 11:33:01 +01:00
Paul RastoinandGitHub aa003f25d9 [CHORE] Twenty emails deps to twenty-shared lockfile update (#10013)
Related to https://github.com/twentyhq/twenty/pull/10010
Forgot to add lockfile
2025-02-05 10:28:11 +01:00
Paul RastoinandGitHub 0a8d75bc07 [FIX] Nx project's scope build dependency management (#10010)
# Introduction
Defined `dependsOn` for each nx project's configuration that has a
dependency to another local package ( ui, shared ).
As follows:
```json
"dependsOn": ["^build"]
``` 
Where the `^` symbol means "all dependencies of this project"

Now on a fresh repo, no built or install deps after install dependencies
you can directly hit `npx nx build PROJECT_NAME`
closes https://github.com/twentyhq/core-team-issues/issues/371

Related what was failing
[run](https://github.com/twentyhq/twenty-infra/actions/runs/13141544809/job/36669643182)
Cancelled before deploy, attested build was correct within the publish
and digest
2025-02-04 18:46:16 +01:00
Baptiste DevessierandGitHub 125a0c3419 Create variants for workflow visualizer nodes (#10006)
Closes https://github.com/twentyhq/core-team-issues/issues/332

- Create the success and failed variants
- Introduce the first responsive color
- Creating stories for the new variants

These components are not yet in use in the source code. If you want to
see them, launch Storybook.

| Success | Failure |
|--------|--------|
| ![CleanShot 2025-02-04 at 16 24
43@2x](https://github.com/user-attachments/assets/0dd68a8f-3914-4b6e-b2d8-43108c2f5e8c)
| ![CleanShot 2025-02-04 at 16 24
59@2x](https://github.com/user-attachments/assets/e4e408d3-29fb-4fbc-a277-044aec9b0f4b)
|
| ![CleanShot 2025-02-04 at 16 24
54@2x](https://github.com/user-attachments/assets/d565ee47-1476-475d-adf6-dadfff9c6719)
| ![CleanShot 2025-02-04 at 16 25
05@2x](https://github.com/user-attachments/assets/9a0aabcc-84d1-41e2-a5a1-7c8cb05f963f)
|
2025-02-04 18:38:38 +01:00
P A C · 先生andGitHub 5be22413c9 [3/n]: Migrate the PUT rest/* and PATCH rest/* to use TwentyORM (#10002)
# This PR

- Is addressing #3644 
- Migrates the PUT and PATCH rest/* endpoints to use twentyORM directly
- Adds integration tests
2025-02-04 17:25:02 +01:00
Thomas TrompetteandGitHub 7dfb9dd77f Fix send email error when empty connected account (#10005)
- Fix send email error when empty connected account
- Add a global util to valid uuid
- Add an util to check if object is workflow related
2025-02-04 17:13:29 +01:00
GuillimandGitHub 0113e40399 Dockerfile update (#10007)
partially fixes https://github.com/twentyhq/core-team-issues/issues/371
2025-02-04 17:11:50 +01:00
0675017b5e Fix:Empty link in record details isn't deleted by default (#9840)
Gives a fix for #9780 . I have added handleDelete inside the submit
function and created a state to manage the empty placeholder
[Screencast from 2025-01-24
01-45-22.webm](https://github.com/user-attachments/assets/87b209a0-7f60-4d72-ad06-c8b955cf6cd2)

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2025-02-04 16:04:34 +01:00
Thomas des FrancsandGitHub c9f45a30d6 0.41 release note -> Introducing Lab Tab (#9989) 2025-02-04 15:50:06 +01:00
cc14c7e0d0 Update cleaning suspended workspace job (#9999)
- split workspace metadata deletion into multiple queries
- delete all subscriptions when workspace is deleted

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-02-04 15:49:22 +01:00
Raphaël BosiandGitHub dfc1bb7c29 361 create a navigation stack for the command menu (#9995)
Closes https://github.com/twentyhq/core-team-issues/issues/361

- Created navigation stack state
- Created navigation functions inside the `useCommandMenu` hook
- Added tests
2025-02-04 15:47:43 +01:00
Lucas BordeauandGitHub b2e4d0d04d Fixed cannot close record table cell header filter (#9998)
With the introduction of dropdown focus id, the table header cell filter
dropdown wasn't taken into account by this new system that prevents
multiple dropdowns from conflicting.

The fix was to just add a dropdown focus id when opening the dropdown.

There was also a bug with the hotkey scope which was staying on table
hotkey scope, thus triggering table hotkeys, this PR also fixes that.
2025-02-04 15:41:26 +01:00
Thomas TrompetteandGitHub 2368dad9ad Enable workflow in lab (#9997)
Refresh of `objectmetadataitems` was not happening fast enough. Page was
breaking when enabling the feature flag. Instead of not storing worklow
objects in state, we will use the feature flag to block on read. This
way we avoid race conditions

<img width="1511" alt="Capture d’écran 2025-02-04 à 14 11 56"
src="https://github.com/user-attachments/assets/912cc59a-f422-48ab-84b7-7fdd7bbc35c1"
/>
2025-02-04 15:25:04 +01:00
GuillimandGitHub 53b51c8bba Fix-issue-370 (#9996)
Fixes the issue from introduced when alowing gmail and outlook.

fixes https://github.com/twentyhq/core-team-issues/issues/370
2025-02-04 14:20:35 +00:00
Félix MalfaitandGitHub b9b7700155 Improve translations with gpt (#10000)
Running GPT4 on all files to improve the quality of translations
2025-02-04 14:56:55 +01:00
P A C · 先生andGitHub a5e27aa751 [2/n]: Rest API -> TwentyORM migration POST rest/* (#9986)
# This PR

- Addressing #3644 
- Migrates the `POST rest/*` endpoint to use TwentyORM directly
- Adds integration tests
- Refactors common login in the v2 service file
- Refactors test utility files
2025-02-04 12:36:52 +01:00
WeikoandGitHub 40f43a4076 add createMany fields to fieldMetadataService to batch field creation (#9957)
## Context
Not exposed in the API yet, this new method allows us to reduce the time
to create multiple fields at once, mostly during seeding. This allows us
to batch transactions and avoid recomputing the cache everytime.

With this change, we recompute the cache 7 times instead of 35 during
seeding. We could do the same for objects.
2025-02-04 11:18:57 +01:00
nitinandGitHub 0e5c2cff42 optimize feature flag query in Lab (#9987)
addressing
https://github.com/twentyhq/twenty/pull/9981/files/ee4a321c71704216f2dee990db2f94d849e9d686#r1939711930
2025-02-04 09:45:14 +01:00
nitinandGitHub edeaecad05 hotfix for lab (#9981)
Issue:
When attempting to toggle a public feature flag that didn't exist in the
workspace's feature flags array, the LabService threw a
FeatureFlagException with code FEATURE_FLAG_NOT_FOUND. This prevented
users from enabling public feature flags for the first time in their
workspace.
Fix:
Modified the LabService to handle non-existent public feature flags by
creating them instead of throwing an error. When the flag doesn't exist,
the service now saves a new feature flag record with the provided key
and value, associates it with the workspace, and returns the newly
created flag.
2025-02-03 22:25:46 +01:00
Félix MalfaitandGitHub 7a0f2f8c0a Add logged out translations (#9983)
Add translation for logged in / sub pages
2025-02-03 22:00:54 +01:00
MarieandGitHub 351e768038 [Permissions] Implement getRoles (#9955)
In this PR

- introducing roles module to separate roles logic (assign a Role, get a
workspace's roles etc.) from permission logic (check if a user has a
permission)
- Introduces getRoles endpoint to fetch a workspace's roles
- introduces the first permission check: getRoles in only accessible to
users with permission on ROLE setting. Implemented
validatesUserHasWorkspaceSettingPermissionOrThrow
2025-02-03 19:14:18 +01:00
GuillimandGitHub caee5b1f89 allow people to connect their mailbox (#9982)
allow people to connect their mailbox independantly from the auth
possibilities
2025-02-03 17:53:19 +00:00
Antoine MoreauxandGitHub 47487f5d1c feat(sso): fix saml + allow to use public invite with sso + fix invite page with multiple sso provider (#9963)
- Fix SAML issue
- Fix the wrong state on the Invite page when multiple SSO provider
exists
- Allow to signup with SSO and public invite link
- For OIDC, use the property upn to guess email for Microsoft and enable
oidc with a specific context in azure
- Improve error in OIDC flow when email not found
2025-02-03 17:48:25 +00:00
Chandragupt GosaviandGitHub 253a3eb83f fix: prevent updating isCustom on object and field metadata (#9968)
This PR prevents updating isCustom on object and field metadata.

Example graphql mutation now cannot be used to modify existing field's
isCustom property as expected.

```
mutation UpdateOneFieldMetadataItem(
    $idToUpdate: UUID!
    $updatePayload: UpdateFieldInput!
  ) {
    updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {
      id
     isCustom
    }
  }
```

Example payload is not allowed now and it will return error in response.

```
{
  "idToUpdate": "80c6c8df-f719-42d2-985a-353468a3ed00",
  "updatePayload": {
    "isCustom": true
  }
}
```

fixes- #6581
2025-02-03 17:30:08 +01:00
Lucas BordeauandGitHub b29ff9b4e6 Removed availableFilterDefinitions as a state but kept its usage as a derived state of objectMetadataItems (#9972)
The global record filter refactor will derive everything at runtime from
objectMetadataItemsState, thus removing the need for a filter definition
concept.

Here we don't yet remove available filter definition usage but we
replace the available filter definitions states, we now derive the same
value from objectMetadataItemsState.

This will allow us to progressively remove the usage of the concept of
filter definition, at the end it will then be easy to just remove from
the codebase because nothing will use it anymore.
2025-02-03 17:29:57 +01:00
c8af90dc01 Environment variables in admin panel (read only) - backend (#9943)
Backend for https://github.com/twentyhq/core-team-issues/issues/293

POC - https://github.com/twentyhq/twenty/pull/9903

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-02-03 21:05:43 +05:30
Raphaël BosiandGitHub 49e4484937 324 add search records as a fallback action in case of no results (#9976)
Closes https://github.com/twentyhq/core-team-issues/issues/324

Fixed typos: 'No results' is used when multiple results are expected and
'No result' is used when only one result is expected.


https://github.com/user-attachments/assets/e3655ced-465a-44b1-92af-63878b9d8a94
2025-02-03 15:54:24 +01:00
Antoine MoreauxandGitHub eb0762dc58 refactor(auth): update SSO provider selection logic (#9975)
Refined the logic to check for SSO providers before setting the sign-in
step. Consolidated conditions to ensure clarity and avoid redundant
checks.
2025-02-03 14:12:56 +00:00
GuillimandGitHub 5a13d5ae29 Sort active workspaces (#9973)
Adding an order when fetching active workspaces so that when we run
command we can better understand the workspaces from the logs. Also, if
the command fails, we know we could start from a specific createdAt
later on
2025-02-03 13:33:00 +01:00
GuillimandGitHub 9d24bfb747 preparing version 0.42-caanry (#9938) 2025-02-03 10:53:05 +01:00
39e7f6cec3 setup localization for twenty-emails (#9806)
One of the steps to address #8128 

How to test:
Please change the locale in the settings and click on change password
button. A password reset email in the preferred locale will be sent.


![image](https://github.com/user-attachments/assets/2b0c2f81-5c4d-4e49-b021-8ee76e7872f2)

![image](https://github.com/user-attachments/assets/0453e321-e5aa-42ea-beca-86e2e97dbee2)

Todo:
- Remove the hardcoded locales for invitation, warn suspended workspace
email, clean suspended workspace emails
- Need to test invitation, email verification, warn suspended workspace
email, clean suspended workspace emails
- The duration variable `5 minutes` is always in english. Do we need to
do something about that? It does seems odd in case of chinese
translations.

Notes:
- Only tested the password reset , password update notify templates.
- Cant test email verification due to error during sign up `Internal
server error: New workspace setup is disabled`

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-02 21:01:34 +01:00
4b9414a002 Add Japanese language (#9956)
I added Japanese translation.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-02-01 12:49:18 +01:00
GuillimandGitHub c9ca87f49f Add instructions to upgrade to 0.41 in docs (#9964)
As per title

Also:
- making sure to destroy datasource in upgrade commands to lower memory
usage
2025-02-01 12:11:27 +01:00
Paul RastoinandGitHub 7fd89678b7 [CHORE] Avoid isDefined duplicated reference, move it to twenty-shared (#9967)
# Introduction
Avoid having multiple `isDefined` definition across our pacakges
Also avoid importing `isDefined` from `twenty-ui` which exposes a huge
barrel for a such little util function

## In a nutshell
Removed own `isDefined.ts` definition from `twenty-ui` `twenty-front`
and `twenty-server` to move it to `twenty-shared`.
Updated imports for each packages, and added explicit dependencies to
`twenty-shared` if not already in place

Related PR https://github.com/twentyhq/twenty/pull/9941
2025-02-01 12:10:10 +01:00
Raphaël BosiandGitHub d9b86475d3 323 add object name inside the search (#9962)
Closes https://github.com/twentyhq/core-team-issues/issues/323

Before:
<img width="495" alt="Capture d’écran 2025-01-31 à 18 11 56"
src="https://github.com/user-attachments/assets/dd1d3ac1-6c97-4398-b233-d323eeacdbb9"
/>

After:
<img width="500" alt="Capture d’écran 2025-01-31 à 18 09 58"
src="https://github.com/user-attachments/assets/68d23990-2d0b-437d-ad2e-a686cdb320e1"
/>
2025-02-01 08:18:48 +01:00
MarieandGitHub 58aa86cc0c [Permissions] Add userWorkspaceId to JWT token (#9954)
This information will be used to fetch a user's role and check their
permissions
2025-01-31 18:15:29 +01:00
f00e7cc670 Make variable nodes undeletable in a readonly tiptap editor (#9950)
In this PR:

- Refactor how we initialize the content of the tiptap editor; providing
a default value for the editor makes node appear instantly
- Hide the button to remove a variable tag when the editor is readonly

| Editable | Readonly |
|--------|--------|
| ![CleanShot 2025-01-31 at 15 04
25@2x](https://github.com/user-attachments/assets/54b90c80-aab1-4ff0-93f9-a0550f031d82)
| ![CleanShot 2025-01-31 at 15 05
51@2x](https://github.com/user-attachments/assets/0480a7dc-9d7a-4e3f-b1a5-0550548622c6)
|

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-01-31 18:00:40 +01:00
Charles BochetandGitHub fa3ea4bb68 Fix composite type update migration builder (#9959) 2025-01-31 16:34:42 +00:00
martmullandGitHub ae62789159 Serverless function follow up (#9924)
- remove asynchronous serverless function build
- build serverless function synchronously instead on activate workflow
or execute
- add a loader on workflow code step test tab test button
- add a new `ServerlessFunctionSyncStatus` `BUILDING`
- add a new route to build a serverless function draft version 
- delay artificially execution to avoid UI flashing



https://github.com/user-attachments/assets/8d958d9a-ef41-4261-999e-6ea374191e33
2025-01-31 17:12:42 +01:00
Antoine MoreauxandGitHub f47c0d45e3 fix(icon): update Microsoft Outlook icon import (#9953)
Replaced the incorrect Microsoft icon with the correct Microsoft Outlook
icon. This ensures the displayed icon matches the intended usage and
improves consistency.
2025-01-31 16:24:01 +01:00
66296a4787 [1/n]: Migrate deleteOne Rest API to use TwentyORM directly (#9784)
# This PR

- Addressing #3644 
- Migrates the `DELETE /rest/*` endpoint to use TwentyORM
- Factorizes common middleware logic into a common module

---------

Co-authored-by: martmull <martmull@hotmail.fr>
2025-01-31 16:12:20 +01:00
Félix MalfaitandGitHub d6788348ba More progress on translations (#9951) 2025-01-31 15:49:37 +01:00
Antoine MoreauxandGitHub b801307d92 refacto(auth): improve type + remove complexity (#9949)
- Improve type
- Remove unnecessary code
- Fix the issue that prevents the usage of invitations when a user signs
in with social media.
- Add Microsoft icon for sso list page
2025-01-31 15:39:45 +01:00
Charles BochetandGitHub 591301f7ce Remove demo from readme as users can create free trial on production (#9952)
We have recently introduced the possibility to sign up on Twenty cloud
without having to input a credit card which makes the demo.twenty.com
useless. Deprecating it!
2025-01-31 15:36:30 +01:00
2ab88a6cfe fix slow search resolver without searchTerm input (#9947)
### Issue
Empty searchTerm search resolver 
```
      .orderBy(
        `ts_rank_cd("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTerms))`,
        'DESC',
      )
      .addOrderBy(
        `ts_rank("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTermsOr))`,
        'DESC',
      )
```
builds the following SQL query:
```
ORDER BY
	ts_rank_cd("searchVector", to_tsquery('')) DESC, ts_rank("searchVector", to_tsquery('')) DESC
```

I haven't been able to find doc on this issue, but after testing,
`ts_rank_cd("searchVector", to_tsquery(''))` slows down/freezes my local
db.
As well, ordering by ts_rank is useless without a searchTerm.

### Result
In local, with the imported 300k rows appEvent custom object,
'combinedSearchRecords' gql request with empty searchTerm reduces exec
time from 3s to 300ms.

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-01-31 14:26:19 +01:00
Baptiste DevessierandGitHub d946cdcba4 Disable the fields of all CRUD workflow actions on readonly mode (#9939)
Fixes
https://discord.com/channels/1130383047699738754/1333822806504247467

In this PR:

- Make the workflow step title input readonly when the visualizer is in
readonly mode
- Make all the fields of the Update Record and Delete Record readonly
when the visualizer is in readonly mode
- Create stories for the Create Record, Updated Record and Delete Record
actions; I'm checking for the default mode and several variants of the
disabled mode
- Set up mocks for the workflows and use them in msw handlers

Follow up:

- We use `readonly` and `disabled` alternatively; these are two
different states when talking about a HTML `<input />` element. I think
we should settle on a single word.
- Refactor the `<WorkflowSingleRecordPicker />` component to behave as
other selects

| Current component | Should look like |
|--------|--------|
| ![CleanShot 2025-01-30 at 17 30
29@2x](https://github.com/user-attachments/assets/104f2e7f-d758-4121-987a-f62f2e138df2)
| ![CleanShot 2025-01-30 at 17 30
49@2x](https://github.com/user-attachments/assets/e74b318e-a41a-40b9-9db8-bcc8015a1d67)
|
2025-01-31 12:31:57 +01:00
Thomas TrompetteandGitHub 4e32fd1c98 Fix refetch on workflow status update (#9946)
Refetch was not set-up properly on activation/deactivation
2025-01-31 11:19:53 +01:00
WeikoandGitHub 502891a430 Add settings roles page (#9940)
## Context
This PR introduces a new Roles settings page, accessible through the
settings menu when the isPermissionsEnabled feature flag is enabled. The
page provides a foundation for managing user roles within the workspace.

This is not fetching the roles from the BE for now and will be done in a
followup PR.
2025-01-30 17:24:02 +00:00
e7ed9530ca Implement parallel code path for fieldMetadataItem instead of filterDefinition (#9931)
This PR doesn't remove or change the current behavior of the filter
definition used in filter dropdown, but adds a parallel code path where
we set the field metadata item used in filter dropdown, which is enough
to replace the filter definition.

The goal at the end is to compute dynamically the equivalent of filter
definition where needed, by deriving from objectMetadataItems global
state + fieldMetadataItemId used in dropdown, that way we don't create
any other source of truth for the concept of filter definition and
everything is easier to work with, especially with advanced filters.

The general spirit is that it's always better to derive everywhere from
a unique state as much as possible, and only create the equivalent of
selectors where needed that will only take the relevant chunk of state
for the small zone of the code operating some reading/writing.

- Added utils and hooks to get a FieldMetadataItem more easily
- Removed some properties from RecordFilterDefinition (the easiest to
remove) and replaced them with a dynamic logic, deriving what's needed
where it is needed
- Added a new fieldMetadataItemIdUsedInDropdownComponentState that is
set in parallel of filterDefinitionUsedInDropdown (to prepare the
removal of filter definition used in dropdown)
- Fixed some stories

---------

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2025-01-30 16:48:38 +00:00
Ana Sofia Marin AlexandreandGitHub d777f62651 clean code and normalize billing eventNames (#9928)
Solves :
https://github.com/twentyhq/private-issues/issues/251

**TLDR:**

Clean Billing Code using feedback of the previous PR (#9865). Normalized
the metadata and names of the products, prices, and meters in Stripe so
that they can be accessed in stripe's test mode and live mode.

**In order to test:**

1. Have the environment variable IS_BILLING_ENABLED set to true and add
the other required environment variables for Billing to work
2. Do a database reset (to ensure that the new feature flag is properly
added and that the billing tables are created)
3. Run the command: npx nx run twenty-server:command
billing:sync-plans-data (if you don't do that the products and prices
will not be present in the database)
4. Run the server , the frontend, the worker, and the stripe listen
command (stripe listen --forward-to
http://localhost:3000/billing/webhooks)
5. Buy a subscription for the Acme workspace
6. Create a workflow and run it
7. After the run has been finished check in sprite the quantity of
events in the CreditMeter, you should see that there is a new occurence
with value one.
2025-01-30 17:39:02 +01:00
625466f38d Fix optimistic effect for implicit and filters (#9935)
The optimistic effect for record creations and updates wasn't working
properly for `and filters` without explicit `and`.
The problem was located inside `isRecordMatchingFilter` which didn't
consider implicit `and filters` as `and filters`. This caused queries to
be updated by the optimistic effect even if they didn't match the root
query filter.

I also removed `fetchPolicy: 'cache-and-network'` from a query. This was
a temporary fix for this issue.

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-01-30 17:31:02 +01:00
Thomas TrompetteandGitHub 460253ab98 Use refetch instead of cache modify when activating/deactivating a workflow (#9937)
Workflow version statuses are too complex to handle in cache. You always
need to:
- modify entity in cache
- modify queries in cache
- do the same for all other version statuses that should also be updated

This complex behavior leads to bugs because of a cache wrongly updated.

Let's simply do a refetch versions on status update.
2025-01-30 17:30:50 +01:00
Thomas TrompetteandGitHub afed36ef5b Remove before-after + fix header label (#9932)
- Fix header label on step

Before
<img width="224" alt="Capture d’écran 2025-01-30 à 12 11 27"
src="https://github.com/user-attachments/assets/e04e2d83-2414-4a92-a307-159e1ca16c64"
/> <img width="224" alt="Capture d’écran 2025-01-30 à 13 27 40"
src="https://github.com/user-attachments/assets/72e2c567-7ee0-43ff-9ae2-898e46d677c1"
/>

After
<img width="224" alt="Capture d’écran 2025-01-30 à 13 29 00"
src="https://github.com/user-attachments/assets/e7e458ab-458d-4e84-98a2-63596f44e0e7"
/> <img width="224" alt="Capture d’écran 2025-01-30 à 13 29 25"
src="https://github.com/user-attachments/assets/df1b0cbd-c8f2-44f6-b2f6-944e5ec8e14f"
/>



- Remove before - after when there is only one of them
2025-01-30 16:19:47 +00:00
GuillimandGitHub a5273732b3 Icon microsoft (#9907)
Implementing the Outlook icon for CreatedBy, only for emails.

Not in this PR original scope : The similar feature for calendar created
records. Since it was straightforward, I added it to the scope of this
PR.

Fix https://github.com/twentyhq/core-team-issues/issues/252
2025-01-30 16:09:42 +00:00
Félix MalfaitGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
9ec524213c Localization of actions (#9934)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-01-30 17:00:35 +01:00
Paul RastoinandGitHub 385bf591cf [FIX] Storybook race condition (#9936)
Flaky test can be found
[here](https://github.com/twentyhq/twenty/actions/runs/13052735697/job/36416799488)
Action should be outside the waitFor callback

## Context
It seems to occurs since the Charles tooltip perf refactor
https://github.com/twentyhq/twenty/commit/ae4bf8d9298ca77cc45f6f4b0c25b66b51ad834f
Maybe the tests render being faster it changed order of execution ? idk
still interesting
2025-01-30 16:57:53 +01:00
GuillimandGitHub 4084f2114e creating the messagefolder entity (#9933)
first step for the
https://github.com/twentyhq/core-team-issues/issues/149 issue
2025-01-30 16:47:22 +01:00
MarieandGitHub 3a78e6f889 Introduce userWorkspaceRoles and Roles + seed standard admin role at workspace creation (#9929)
Closes https://github.com/twentyhq/core-team-issues/issues/303
2025-01-30 15:05:33 +00:00
Antoine MoreauxandGitHub e895aa27e6 feat(custom-domains): allow to register a custom domain (without UI) (#9879)
# In this PR
- Allow to register a custom domain
- Refacto subdomain generation

# In other PRs
- Add UI to deal with a custom domain
- Add logic to work with custom domain
2025-01-30 13:51:16 +01:00
Charles BochetandGitHub ae4bf8d929 Fix tooltip performances (#9930)
Rendering the Tooltip is creating performance issues on the whole app.

In this PR we only render the tooltip on hover
2025-01-30 12:37:56 +01:00
GuillimandGitHub a039987549 quick fix for microsoft : removing feature flag for microsoft (#9926)
after customer complain, we found a little bug. removing feature flag
for microsoft sync fixes the issue.
2025-01-30 11:46:16 +01:00
9635fe9222 [BUG] Handle optimistic cache deletion operation (#9914)
# Introduction
This PR is highly related to previous optimistic cache refactor:
https://github.com/twentyhq/twenty/pull/9881
Here we've added some logic within the
`triggerUpdateRelationsOptimisticEffect` which will now run if given
recordInput `deletedAt` field is defined.

If deletion, we will iterate over all the fields searching for
`RELATION` for which deletion might implies necessity to detach the
relation

## Known troubleshooting ( also on main )

![image](https://github.com/user-attachments/assets/10ad59cd-e87b-4f26-89df-0a028fcfa518)
We might have to refactor the `prefillRecord` to spread and
overrides`inputValue` over defaultOne as inputValue could be a partial
one for more info please a look to

# Conclusion
Any suggestions are welcomed !
fixes https://github.com/twentyhq/twenty/issues/9580

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-30 11:12:37 +01:00
Jérémy MandGitHub e7da9b6b87 feat: MenuItem disabled color (#9923)
Fix #9874
2025-01-30 10:23:06 +01:00
Félix MalfaitandGitHub 16c2d383ee Fix E2E tests (#9919)
Fortunately we're not using NODE_ENV=production for our deployment, but
we need to think of a better long-term fix. Not easy because Lingui
strips out messages in production environment
2025-01-29 22:01:10 +01:00
4edbb13706 Fix - Input box shadow, left icon focus and new size variant (#9902)
Favorite folder input needs to be 28px --- added a new sizeVariant 
Removed box shadow completely -- checked with @Bonapara 
The left icon used to be of color light on focus -- added a state to
check if input is focused

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-01-29 19:44:25 +01:00
Ana Sofia Marin AlexandreandGitHub 0d6f4a32a7 Add billing meter event service (#9865)
Solves :
https://github.com/twentyhq/private-issues/issues/241
https://github.com/twentyhq/private-issues/issues/254

**TLDR:**

- Add BillingMeterEventService and StripeBillingMeterEventService in
order to send billing meter events to stripe.
- Plugged the service into workflow node execution for testing purposes
(more improvements on this area will be done in the next PR's)

**In order to test:**

- Have the environment variable IS_BILLING_ENABLED set to true and add
the other required environment variables for Billing to work
- Do a database reset (to ensure that the new feature flag is properly
added and that the billing tables are created)
- Run the command: npx nx run twenty-server:command
billing:sync-plans-data (if you don't do that the products and prices
will not be present in the database)
- Run the server , the frontend, the worker, and the stripe listen
command (stripe listen --forward-to
http://localhost:3000/billing/webhooks)
- Buy a subscription for the Acme workspace 
- Create a workflow and run it
- After the run has been finished check in sprite the quantity of events
in the CreditMeter, you should see that there is a new occurence with
value one.

**Take into consideration:**

- I used an eventName that I have made a long time ago, so it hasn't a
significant naming. I'm updating the meters and associated prices in
stripe to use the correct meter with a more clearer eventName.
- I put some error handling in the execution of the workflow nodes, this
is still incomplete and needs some refinement, I would like the feedback
of the workflows track for a more cleaner approach
2025-01-29 19:36:01 +01:00
1b3181b14e add command to run cleaning suspended workspaces job (#9895)
Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-01-29 19:30:46 +01:00
4edeb7f991 feat(auth): enhance SSO handling and workspace auth logic (#9858)
- Return only SSO providers with an `activate` status
- If only 1 SSO provider is enabled for auth, redirect the user to the
provider login page.
- if only SSO auth is available set the step to SSO selection.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-29 19:28:21 +01:00
Baptiste DevessierandGitHub 85df6ada52 Prevent all workflow node and edge deletions made through the UI (#9918)
## Old

In the demo, I press the `Delete` key multiple times, and it deletes the
nodes.


https://github.com/user-attachments/assets/75bf84d3-b182-488c-a781-bbe236985142

## New


https://github.com/user-attachments/assets/4ae4f387-e143-4ce8-8140-6cb2c549f5d2
2025-01-29 18:29:01 +01:00
Raphaël BosiandGitHub ce296fae4f Add search records actions to the command menu (#9892)
Closes https://github.com/twentyhq/core-team-issues/issues/253 and
https://github.com/twentyhq/core-team-issues/issues/256.

- Created `CommandMenuList`, a component used at the root level of the
command menu and inside the search page of the command menu
- Refactored record agnostic actions
- Added shortcuts to the action menu entries (`/` key for the search)
and updated the design of the shortcuts
- Reordered actions at the root level of the command menu


https://github.com/user-attachments/assets/e1339cc4-ef5d-45c5-a159-6817a54b92e9
2025-01-29 17:23:40 +00:00
Baptiste DevessierandGitHub 03f3ccd060 Fix workflow edges color (#9917)
Fixes
https://discord.com/channels/1130383047699738754/1333831150535442462
2025-01-29 18:04:32 +01:00
Antoine MoreauxandGitHub ce8c6c4bac fix: correct typo in property name from "Entreprise" to "Enterprise" (#9916)
The property name "hasValidEntrepriseKey" was corrected to
"hasValidEnterpriseKey" across multiple files for consistency and
accuracy. This ensures proper alignment with naming conventions and
avoids potential issues in usage or understanding.
2025-01-29 17:39:04 +01:00
Raphaël BosiandGitHub 04bc42aa47 Improve command menu chip design (#9915)
Before:

<img width="236" alt="Capture d’écran 2025-01-29 à 17 07 13"
src="https://github.com/user-attachments/assets/edc277f3-3e50-405a-9981-3ac63e270f3f"
/>
<img width="259" alt="Capture d’écran 2025-01-29 à 17 07 23"
src="https://github.com/user-attachments/assets/81863c50-5b96-4cbf-bc15-ab437d839fa1"
/>
<img width="224" alt="Capture d’écran 2025-01-29 à 17 07 41"
src="https://github.com/user-attachments/assets/c0783544-6928-4b05-bf86-66d37ddca5e5"
/>

After:

<img width="230" alt="Capture d’écran 2025-01-29 à 17 03 49"
src="https://github.com/user-attachments/assets/c47be446-32fa-40d1-af25-207ed91c0ea2"
/>
<img width="254" alt="Capture d’écran 2025-01-29 à 17 04 15"
src="https://github.com/user-attachments/assets/99b9ab30-c77d-4392-8ef6-2a1a5fb00047"
/>
<img width="218" alt="Capture d’écran 2025-01-29 à 17 04 37"
src="https://github.com/user-attachments/assets/6e214a42-438d-495f-9855-fd5397fcc6d3"
/>
2025-01-29 16:37:59 +00:00
Félix MalfaitandGitHub 9d32e63111 Continue Frontend localization (#9909)
Translation more content on the frontend
2025-01-29 17:36:28 +01:00
Thomas TrompetteandGitHub ce0a6c1b17 Fix empty trigger on readonly + fix cache apollo on deactivation (#9912)
- On deactivation, we should not need to refresh so the workflow
disappear from cmd+k


https://github.com/user-attachments/assets/826fa4c6-3faa-49d1-b180-ed5d3ed187e5

- When readonly, step empty, we should not see the right drawer


https://github.com/user-attachments/assets/b557ef61-da81-446d-b160-f26c4c7a5191
2025-01-29 17:25:20 +01:00
Paul RastoinandGitHub 85ae7084ab [FiX] Optimistic cache useUpdateOneRecord (#9913)
# Introduction
~~this could be cool for the updateRecordFromCache to return the updated
record, so we could consume its return value more explicitly such as
`updatedRecordValue`~~

In fact this is nitpick as it would really be the same than returning
the received params

## Fixes duplicated optimistic cache upsert
<img
src="https://github.com/user-attachments/assets/289f4801-5b67-4e4e-a87b-a512deb5180c"
width="300px" height="300px">

We were before comparing the previously `cachedRecord` which is now, not
the latest as the optimistic is (should) be working successfully since
https://github.com/twentyhq/twenty/pull/9881

## Reproduction
1-  Open any Company
2- Attach an Opportunity it should appears only once
3bis- If you revert the last branch commit it will appear duplicated

Related to #9580
2025-01-29 17:11:36 +01:00
Jérémy MandGitHub 07197d1e6d feat: new relation in graphql-query-runner (#9883)
Fix https://github.com/twentyhq/core-team-issues/issues/300

Within GraphQLQueryRunner the new relation format will be used when the
feature flag `IsNewRelationEnabled` is set to true.
2025-01-29 17:04:39 +01:00
29745c6756 [BUG] Fix record relation optimistic mutation (#9881)
# Introduction
It seems like optimistic caching isn't working as expected for any
record relation mutation, CREATE UPDATE DELETE.
It should not have an impact on the destroy

We included a new `computeOptimisticRecordInput` that will calculate if
a relation is added or detach.

Updated the `triggerCreateRecordsOptimisticEffect` signature we should
have a look to each of its call to determine if it should be checking
cache or not

Related to #9580

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-29 16:00:59 +01:00
Thomas TrompetteandGitHub 7291a1ddcd Add event when creating draft from previous version (#9910)
We create draft without emitting creation event. So workflow statuses
are not properly updated

This service needs a refacto. Will do it once we get out from fast
follows rush
2025-01-29 15:48:22 +01:00
Baptiste DevessierandGitHub 5a34f6bfa7 Disable variables for the country code in the form phone field (#9897)
Fixes
https://discord.com/channels/1130383047699738754/1333820080651501691

We disable the variable picker for the country code for now. We'll
implement a proper solution later.
2025-01-29 15:40:59 +01:00
Thomas TrompetteandGitHub 7536a5a9a0 Add missing fields in update action (#9908)
Fix https://discord.com/channels/1130383047699738754/1333806855264145500
2025-01-29 14:23:21 +00:00
Jérémy MandGitHub c17dbe7115 fix: broken schema generation (#9905) 2025-01-29 13:46:36 +01:00
Jérémy MandGitHub fbb67d74c8 feat: new relation schema generation (#9882)
Fix https://github.com/twentyhq/core-team-issues/issues/295

Based on the feature-flag `IsNewRelationEnabled` the schema will be
marked as outdated and regenerated, this will cause an error on the
front-end on the first request on the following ones schema will be well
generated ans request will work.
2025-01-29 10:33:17 +01:00
Félix MalfaitandGitHub f74bb5a60b Add field description+label translations (#9899)
Add translations for field descriptions
2025-01-28 23:20:28 +01:00
Félix MalfaitandGitHub b1219ff107 Add POC for Field translation (#9898)
Similar to ObjectMetadata translation

Also fixed an issue linked to the migration from `t` to `message`
helper: we're forced to rebuild the ID ourselves
2025-01-28 21:25:09 +01:00
8754b7107d Fix Icon Padding and Size in Select Item (#9888)
This pull request addresses issue
[#9458](https://github.com/twentyhq/twenty/issues/9458) by making the
following changes:

Increase the icon size from 14x14px to 16x16px
Add 8px padding to the right side of the icon
Ensure the icon is not cropped and displays with rounded angles

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-28 19:50:59 +01:00
martmullandGitHub 5329dd0c63 Disable variable picker when no available variables (#9894)
- Disable variable picker when no available variables
(https://discord.com/channels/1130383047699738754/1331236819227906049)
<img width="496" alt="image"
src="https://github.com/user-attachments/assets/e4cb97c8-648c-4bdf-b230-49687113c9e6"
/>
2025-01-28 18:53:26 +01:00
Baptiste DevessierandGitHub 7195614926 Add history (undo/redo) to text variable editors (#9889)
https://github.com/user-attachments/assets/83689fd9-1b00-49ff-938a-748822baf15f
2025-01-28 18:35:36 +01:00
MarieandGitHub 180f6c28a4 Aggregates - Update Date label (#9862)
https://discord.com/channels/1130383047699738754/1333388007293849640/1333388007293849640
2025-01-28 17:18:29 +00:00
WeikoandGitHub 381c180ab9 Update bullmq queue option for job failure strategy (#9887)
## Context
This config tells bullmq what to do with jobs once they have been
processed.
We used to retain 100 completed jobs and 500 failed jobs. We never
really needed that much retention and keeping succeeded job is not
useful either in our case. I'm updating the strategy so all completed
jobs are removed from the queue and we now keep 100 failed jobs in case
we need to debug. (Although we should have good monitoring with
ExceptionHandler driver)
2025-01-28 17:39:10 +01:00
GuillimandGitHub 72d4619cd4 adding the predicate in the UPSERT function (#9891)
Bug fix : we are adding the predicate in the UPSERT function in order to
fix a postgres constraint
2025-01-28 16:51:37 +01:00
aa6d5c4c51 fix link validation and matching during csv import (#9890)
### Context
[Issue 9019](https://github.com/twentyhq/twenty/issues/9019) opens by
user having domain name not imported while importing through CSV.
@samyakpiya (thank you for your investigation !) has tested various
domain imports and has reported issue with its import test. Issues that
no longer exist : when I test your import, I get all records imported.

### Solution
- Remove "Link label" (cf screenshot - before fix) composite field in
matching options, not used in front that could mislead the user.
<img width="300" alt="Screenshot 2025-01-28 at 15 39 18"
src="https://github.com/user-attachments/assets/0ea24d9e-b339-42f3-b8d9-e271b33dbcfd"
/>

- Check links type fields validity in "Validate data" step


closes #9019

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-01-28 16:51:19 +01:00
Antoine MoreauxandGitHub eb88f6f584 feat(custom-domain): remove domainName + add migration for custom dom… (#9872)
…ain + feature flag

Blocked by #9849
2025-01-28 15:28:18 +01:00
Thomas TrompetteandGitHub 0e981bae0a Fix workflows empty states (#9878)
Versions and runs cannot be added manually. Updating message.

<img width="545" alt="Capture d’écran 2025-01-28 à 11 33 47"
src="https://github.com/user-attachments/assets/36b7ade8-3061-4b72-bfa5-c262e4d7d25e"
/>
<img width="614" alt="Capture d’écran 2025-01-28 à 11 34 02"
src="https://github.com/user-attachments/assets/9b524eb4-f40c-4330-a37f-fb33e08d22f4"
/>
2025-01-28 15:16:36 +01:00
Charles BochetandGitHub ac92aed87f Add command to remove duplicate messageChannelMessageAssociations (#9886)
In this PR:
- create a command to remove duplicate (groupBy messageId,
messageChannelId) within messageChannelmessageAssociaitions.

THis is needed to apply a constraint already present on
messageChannelmessageAssociaitions (to be executed before syncing
metadata)
2025-01-28 15:04:52 +01:00
Baptiste DevessierandGitHub 1e9dce3fd5 Fix the design of the Variable chip (#9871)
- Use a React component for variable tags in tiptap
- Fix the design of the variable chip
- Always display a button to delete the chip

![CleanShot 2025-01-28 at 12 35
55@2x](https://github.com/user-attachments/assets/d78ffa52-fcc3-4bbc-b427-68edde255408)
2025-01-28 14:59:45 +01:00
Andrii BodnarandGitHub ac8c0c72cc docs: add Lingui to the Readme (#9884)
This PR adds Lingui to the translation tools in the Stack section of the
Readme, based on the suggestion in the comment
https://github.com/lingui/js-lingui/issues/2159#issuecomment-2618922147
2025-01-28 14:48:46 +01:00
b63ae14318 RICH_TEXT_V2 backend (#9848)
- Add RICH_TEXT_V2 composite type to backend.
- Add `bodyV2` field to tasks and notes.
- Minimum required frontend changes to avoid errors when creating a note

[Testing
instructions](https://github.com/twentyhq/twenty/pull/9690#issuecomment-2602378218)

---------

Co-authored-by: ad-elias <elias@autodiligence.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-01-28 14:05:06 +01:00
Charles BochetandGitHub 6f72f1af33 Fix Messaging sync upsert in messageChannel association (#9880)
We have recently changed an .insert into a .upsert but we forgot to add
the unique index on the postgres schema.
2025-01-28 11:48:09 +01:00
Thomas TrompetteandGitHub 069c34cd7b Add link on snack bar (#9873)
Add link to workflow execution 

<img width="639" alt="Capture d’écran 2025-01-27 à 18 15 34"
src="https://github.com/user-attachments/assets/f2a1b3b5-7bf6-4b33-bba7-bf8907f6bcc6"
/>
2025-01-28 11:26:20 +01:00
af8d22ee99 Fix ObjectType casing and conflict between Relation and RelationMetadata (#9849)
Fixes #9827 

Also uncovered a conflict with `@objectType('Relation')` and
`@objectType('relation)`

I don't want to address it in this PR so I will create a followup issue
when we close this but I think there's a confusion between
Relation/RelationMetadata, it's unclear what is what

---------

Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
2025-01-28 10:06:18 +01:00
Félix MalfaitandGitHub 08a0e67477 Translate label and descriptions for objects (#9876)
Begin server-side translations
2025-01-28 09:54:59 +01:00
Charles BochetandGitHub 4542199e5c Fix object metadata view creation issue (#9875)
Fixes https://github.com/twentyhq/core-team-issues/issues/26
Fixes https://github.com/twentyhq/twenty/issues/9350
2025-01-27 21:25:02 +01:00
Félix MalfaitandGitHub 549c3faf71 Add server translation (#9847)
First proof of concept for server-side translation.

The goal was to translate one metadata item:

<img width="939" alt="Screenshot 2025-01-26 at 08 18 41"
src="https://github.com/user-attachments/assets/e42a3f7f-f5e3-4ee7-9be5-272a2adccb23"
/>
2025-01-27 21:07:49 +01:00
Antoine MoreauxandGitHub 2a911b4305 feat(auth): centralize SSO error handling logic (#9832)
Introduce `SsoErrorRedirectService` to handle SSO error redirection and
exception capturing across the authentication controllers. Refactor
Microsoft, Google, and SSO authentication controllers to utilize this
service, replacing the previous direct calls to `DomainManagerService`.
Added unit tests for the new service to ensure robust error handling.
2025-01-27 17:55:20 +00:00
10476fcb01 remove freeAccess user logic (#9866)
closes #9763

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-01-27 17:49:16 +01:00
martmullandGitHub 7362558e30 Reset selectedStep with proper step (#9867)
as title
fixes
https://discord.com/channels/1130383047699738754/1331215762840485908
2025-01-27 15:35:29 +01:00
martmullandGitHub 687b61849a Fix rightDrawer padding (#9869)
Fixes
https://discord.com/channels/1130383047699738754/1331236153713627157
## Before

![image](https://github.com/user-attachments/assets/3b265461-774b-480b-83dd-d3a410cfb31b)

## After

![image](https://github.com/user-attachments/assets/259122fa-3759-4cd6-8698-b94ccd5470bc)
2025-01-27 14:31:47 +00:00
martmullandGitHub 68c4f9cb0d Fix design to match figma (#9864)
As title
Fixes
https://discord.com/channels/1130383047699738754/1331212573902770228

## Before

![image](https://github.com/user-attachments/assets/4b556022-92c3-4197-ab18-91c089117936)

## After

![image](https://github.com/user-attachments/assets/67f97c60-d08b-4ae1-87b4-79c24ae79b93)
2025-01-27 15:23:28 +01:00
GuillimandGitHub 4bb8e3a50e updating SettingsAccountsListEmptyStateCard (#9868)
changing from "Connect a Google account" to  "Choose your provider"

updating all languages using 
`npx nx run twenty-front:lingui:extract`
2025-01-27 15:20:48 +01:00
GuillimandGitHub fded1cb5a0 Bug fix microsoft require approval (#9863)
Rolling back since apparently requiring "select_account" fails for
google
2025-01-27 13:58:42 +00:00
GuillimandGitHub 77c586db24 fix the bug "require approval" (#9859)
## The bug : 
"Approval required" even though admin consent has been granted for the
whole tenant"

Fix https://github.com/twentyhq/core-team-issues/issues/277 

### More details
described here in
[stackoverflow](https://stackoverflow.com/questions/79385342/approval-required-even-though-admin-consent-has-been-granted-for-the-whole-ten?noredirect=1#comment140006092_79385342)
2025-01-27 14:36:01 +01:00
martmullandGitHub c1e84a746c Fix design for select action (#9857)
as title 
fixes
https://discord.com/channels/1130383047699738754/1330945172489371771
2025-01-27 13:29:04 +01:00
martmullandGitHub fb0b342374 Fix background color in code step right drawer (#9856)
Fixes
https://discord.com/channels/1130383047699738754/1331211019430924380
as title
2025-01-27 12:16:21 +01:00
GuillimandGitHub 8df3214e84 decoupling isMicrosoftAuthEnabled and IsMicrosoftSyncEnabled (which has no reason to be used here) (#9854)
### what
decoupling auth and email synchro

### why
IsMicrosoftSyncEnabled (which has no reason to be used here) since we
now allow microsoft as a auth provider
2025-01-27 11:04:19 +01:00
GuillimandGitHub 98671b6cd2 bug fix urgent (#9851)
upsert messaging
2025-01-27 10:38:18 +01:00
Baptiste DevessierandGitHub 84c299f8d4 Create a reusable Label component (#9833)
As seen with @Bonapara, I'm creating a Label component and use it in the
workflows.
2025-01-24 19:25:04 +01:00
55be726105 Rename Unintuitive Function Names in Authentication Flow (#9706)
Resolves #9623

## Description

This PR renames the following functions to better reflect their purpose.

- Backend:
  - Verify → GetAuthTokensFromLoginToken
  - Challenge → GetLoginTokenFromCredentials

- Frontend:
  - challenge → getLoginTokenFromCredentials
  - verify → getAuthTokensFromLoginToken

## Testing
_Sign in works as expected:_


https://github.com/user-attachments/assets/7e8f73c7-2c7d-4cd2-9965-5ad9f5334cd3

_Sign up works as expected:_
  

https://github.com/user-attachments/assets/d1794ee4-8b59-4934-84df-d819eabd5224

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-24 19:19:14 +01:00
Lucas BordeauandGitHub 570b2e3530 Refactored record filter saving to view filters (#9844)
This PR refactors the record filter saving to view filters.

Before we used states to track the change of view filters, now we just
check if there's a difference between the current record filters and the
current view filters before saving.

We also use this check to show the reset and save buttons.

CRUD operations to perform on view filters are computed by utils , and .

Also added unit tests on those utils.
2025-01-24 18:48:59 +01:00
Baptiste DevessierandGitHub a4011676f0 Add e2e tests for the Use as Draft feature (#9845)
Test with and without a draft version as the last version of the
workflow
2025-01-24 18:38:35 +01:00
aacbf11435 add new emails in suspended workspace cleaning job (#9834)
From: Felix from Twenty <noreply@yourdomain.com>
Subject: Action needed to prevent workspace deletion
<img width="400" alt="Screenshot 2025-01-24 at 16 31 35"
src="https://github.com/user-attachments/assets/8350a499-6815-4b00-a4fb-615b2a3b60e0"
/>


From: Felix from Twenty <noreply@yourdomain.com>
Subject: Your workspace has been deleted
<img width="456" alt="Screenshot 2025-01-24 at 16 33 15"
src="https://github.com/user-attachments/assets/7e392e7c-519c-4b38-ae8c-ab3c9dd72c24"
/>



closes [284](https://github.com/twentyhq/core-team-issues/issues/284) &
[285](https://github.com/twentyhq/core-team-issues/issues/285) - [parent
issue](https://github.com/twentyhq/core-team-issues/issues/179)

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-01-24 18:37:06 +01:00
Thomas TrompetteandGitHub 07dec36976 Tt fast follows 24/01 (#9843)
- Add icons on steps
- Add search input on object selection
- Improve event label
<img width="503" alt="Capture d’écran 2025-01-24 à 17 59 34"
src="https://github.com/user-attachments/assets/4e5d31d6-6fe5-4f78-9112-3fbd6ee66743"
/>
<img width="503" alt="Capture d’écran 2025-01-24 à 17 59 54"
src="https://github.com/user-attachments/assets/7f37402f-3d1a-4bea-9082-05f50a711f35"
/>
<img width="503" alt="Capture d’écran 2025-01-24 à 18 00 08"
src="https://github.com/user-attachments/assets/fffabaff-9d5d-4584-9297-e21434333de0"
/>
2025-01-24 17:25:01 +00:00
17def223b6 Feat/2fa (#9634)
# Description
Closes #7003 
Implements 2FA with TOTP. 

>[!WARNING]
> This is a draft PR, with only partial changes, made as a mean of
discussion about #7003 (it's easier to reason about real code)

## Behaviour
- a `totpSecret` is stored for each user
- use [`otplib`](https://github.com/yeojz/otplib/tree/master) to create
a QR code and to validate an `otp` against an `totpSecret` (great [demo
website](https://otplib.yeojz.dev/) by `otplib`)
- OTP is asked upon each login attempt

## Source
Inspired by:
- [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
- Cal.com's implementation of 2FA, namely
- [raising a
401](https://github.com/calcom/cal.com/blob/c21ba636d2bec4ed55775f0b058f70fdc371c410/packages/features/auth/lib/next-auth-options.ts#L188-L190)
when missing OTP and 2FA is enabled, with a [specific error
code](https://github.com/calcom/cal.com/blob/c21ba636d2bec4ed55775f0b058f70fdc371c410/packages/features/auth/lib/ErrorCode.ts#L9)
- [catching the
401](https://github.com/calcom/cal.com/blob/c21ba636d2bec4ed55775f0b058f70fdc371c410/apps/web/modules/auth/login-view.tsx#L160)
in the frontend and
[displaying](https://github.com/calcom/cal.com/blob/c21ba636d2bec4ed55775f0b058f70fdc371c410/apps/web/modules/auth/login-view.tsx#L276)
the OTP input

## Remaining
- [ ] encrypt `totpSecret` at rest using a symetric algorithm

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-01-24 18:23:57 +01:00
Ana Sofia Marin AlexandreandGitHub f58f84114c update subscription interval and quantity for hybrid susbscription (#9822)
Solves https://github.com/twentyhq/private-issues/issues/253

**TLDR:**

Can update the billing subscription interval for a subscription with a
base product and metered product. It also updates correctly as the
quantity of base products depending on how many people are in the
workspace.

**In order to test:**

1. Have the environment variable IS_BILLING_ENABLED set to true and add
the other required environment variables for Billing to work
2. Do a database reset (to ensure that the new feature flag is properly
added and that the billing tables are created)
3. Run the command: npx nx run twenty-server:command
billing:sync-plans-data (if you don't do that the products and prices
will not be present in the database)
4. Run the server , the frontend, the worker, and the stripe listen
command (stripe listen --forward-to
http://localhost:3000/billing/webhooks)
5. Buy a subscription for the Acme workspace , change the interval in
the Billing Settings
6. Add another person to the workspace, you should see all the previous
changes reflected in the database

**Doing**
Moving the BillingSubscriptionsService.getUpdatedSubscriptionItems to an
util (for a less cluttered service)
2025-01-24 18:19:09 +01:00
Paul RastoinandGitHub c89cc38729 [MISC] Twenty-shared build declaration and declarationMap tsconfig (#9838)
# Motivations
By adding the declaration and declaration mapping this allows a better
`go to source` in our IDE
Should also be done for
https://github.com/twentyhq/core-team-issues/issues/281

## FROM


https://github.com/user-attachments/assets/5cc307d0-b2dc-46bf-b61f-0731015c4a30


## TO


https://github.com/user-attachments/assets/5ed1d7af-2716-435d-a1b8-6738b5a77956

## Notes
Might be interesting to add global commands for:
- watch mode for `twenty-ui` and `twenty-shared`
- clean, output folders such as `dist` and `node_modules` and so on
2025-01-24 17:56:42 +01:00
martmullandGitHub 03197226f4 Fix Cmd enter to test code step (#9841) 2025-01-24 16:46:56 +00:00
Paul RastoinandGitHub 95c772664e Refactor triggerUpdateRelationsOptimisticEffect to compute relationship from Metadatas (#9815)
# Introduction
At the moment the relationships are inferred from the record data
structure instead of its metadatas
We should refactor the code that computes or not the necessity to detach
a relation on a mutation

We've refactored the `isObjectRecordConnection` method to be consuming a
`relationDefintion` instead of "typeChecking" at the runtime the data
structure using zod validation schema

Related to #9580
2025-01-24 17:24:23 +01:00
Kevin KimandGitHub a8552a6a67 Fix Typo in Filename (#9835)
- Rename `mircosoft-apis-oauth-request-code.guard.ts` to
`microsoft-apis-oauth-request-code.guard.ts `
- Update import statement in
`packages/twenty-server/src/engine/core-modules/auth/controllers/microsoft-apis-auth.controller.ts`
to reflect the updated filename.
2025-01-24 16:24:41 +01:00
25cb909e17 Improve Docker-Compose Install Experience (#9781)
This PR updates the docker-compose installation documentation and env
sample to improve the setup experience.

- Updates the URLs for raw files to reference main branch, which is
likely where new users will be pulling from initially. This seems to be
the most straightforward option; assume that advanced users who want to
retrieve it from a particular tag will know to change the URL for their
scenario.
- Fixes an improperly stated curl command.
- Adds a note that the PGPASSWORD_SUPERUSER should be URL-safe. This is
required since the value is later concat into a PG_DATABASE_URL as a
URL, and expected to be in proper URL format. Touches on #8597.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-01-24 16:13:28 +01:00
Raphaël BosiandGitHub ff41768e8f 250 implement restore context button on command menu (#9836)
Closes https://github.com/twentyhq/core-team-issues/issues/250



https://github.com/user-attachments/assets/9c120188-497d-4273-9137-f8d0de3bd884
2025-01-24 16:10:02 +01:00
Thomas TrompetteandGitHub 29df6e64a0 Fix icon matching + small other fixes on workflows (#9814)
- Record Fields label
- body height fix
- Icons on object picker
- Fix icon matching between nodes and right drawer

<img width="1296" alt="Capture d’écran 2025-01-23 à 18 51 12"
src="https://github.com/user-attachments/assets/ecd5fb00-49cd-416e-96af-9200418294e0"
/>
2025-01-24 15:47:09 +01:00
martmullandGitHub 1a42483aa9 Fix wrong label formatting (#9830)
Fixes
https://discord.com/channels/1130383047699738754/1324785862830985266
## Before
<img width="451" alt="image"
src="https://github.com/user-attachments/assets/53ae96f9-4bbd-40e3-beaa-a5702f2adee4"
/>

## After
<img width="388" alt="image"
src="https://github.com/user-attachments/assets/e36da162-bc60-4596-823b-ffb6ddfed98d"
/>
2025-01-24 15:11:30 +01:00
Antoine MoreauxandGitHub f23de2fa22 chore(auth): update SAML strategy configuration (#9829)
Added `disableRequestedAuthnContext` flag to SAML auth strategy to align
with compatibility requirements. Adjustments ensure seamless integration
with certain Identity Providers. No functional impact on existing flows.
2025-01-24 14:52:42 +01:00
edd7212f0b feat: add clean suspended workspaces command (#9808)
closes [283
sub-issue](https://github.com/twentyhq/core-team-issues/issues/283) -
[parent issue
](https://github.com/orgs/twentyhq/projects/1/views/3?filterQuery=sprint%3A%40current+assignee%3Aetiennejouan&pane=issue&itemId=93520456&issue=twentyhq%7Ccore-team-issues%7C179)

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-01-24 11:10:52 +01:00
Félix MalfaitandGitHub 15814d465a Fix missing events during account connection (#9825)
Fix #8765
2025-01-24 10:10:42 +00:00
Jérémy MandGitHub 8d794374f1 feat: new relation resolver (#9794)
Fix [#240](https://github.com/twentyhq/core-team-issues/issues/240)
2025-01-24 10:38:50 +01:00
Antoine MoreauxandGitHub 5783c41df2 fix(auth): Improve error management with sso + fix microsoft saml (#9799)
Fix #9760 #9758
2025-01-24 10:36:18 +01:00
3c85516f77 fixed ellipsis for calendar events (#9823)
Fixed Missing ellipsis for long calendar event names #5267 introducing
width for the title

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-24 10:33:46 +01:00
6af42b9b9b Fix: Remove default value for name field in CustomWorkspaceEntity (#9805) (#9824)
### Summary
This pull request addresses issue #9805 by removing the default value
`'Untitled'` for the `name` field in the `CustomWorkspaceEntity` class.

### Details
- Removed the default value `'Untitled'` for the `name` field.
- This change ensures that new records do not have an empty or default
name.
- Ensures consistency across different standard object records.

### Issue
This pull request resolves issue #9805.

### Testing
Tested the creation of new records to ensure that the `name` field is
now correctly handled without a default value.

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-01-23 19:01:03 +01:00
Baptiste DevessierandGitHub 23400e9679 Normalize the spacing between nodes (#9820)
## Old

![CleanShot 2025-01-23 at 16 38
25@2x](https://github.com/user-attachments/assets/0c8e3b26-5ef4-455a-978e-b430e54b63ba)

![CleanShot 2025-01-23 at 16 38
51@2x](https://github.com/user-attachments/assets/929b73d3-2d7f-45b9-a0fb-62516969bd63)

## New

![CleanShot 2025-01-23 at 17 12
25@2x](https://github.com/user-attachments/assets/670ba386-c643-408d-a682-d279bc81f5c1)
2025-01-23 18:26:51 +01:00
martmullandGitHub daa8ff1299 148 cant access note without title from kanban board (#9817)
closes https://github.com/twentyhq/core-team-issues/issues/148

- fixes not openable kanban card when identifier empty
- update card behavior (onClick open recordPage)
- fixes right click actionDropdown position

## Before


https://github.com/user-attachments/assets/696194b8-d7fa-4fc1-a6f9-b46241a262e5

![image](https://github.com/user-attachments/assets/63a5f634-2688-41ba-9ac4-51292ef09b7a)

## After


https://github.com/user-attachments/assets/41e296e5-ae16-47f8-b174-7dd21d74188d

![image](https://github.com/user-attachments/assets/7f859764-946e-40d3-9b66-5460edb215f2)
2025-01-23 17:05:35 +00:00
Félix MalfaitandGitHub afa2282ea3 Fix left menu highlight (#9821)
Fix #9777

getAppPath doesn't return object/pet/ but actually object/pet so it
needed a small adjustment (pet - 1 = PE is included in PEople)
2025-01-23 17:59:30 +01:00
Jérémy MandGitHub bf564788c3 fix: board hide button not working (#9818)
Fix #9816
2025-01-23 16:17:46 +00:00
1d7cbcabee fix: scrolling inside the table with scrollbar (#9753)
Fixed- when scrolling inside the table with scrollbar, cursor also
selects rows. Now on scrolling with scrollbar content is scrolled in
respective direction as expected and table rows don't get selected.

fixes issue #6773

---------

Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
Co-authored-by: ehconitin <nitinkoche03@gmail.com>
2025-01-23 15:41:57 +00:00
bbd3af108b bugfix: escape destroyed objects on workers (#9719)
# This PR

- Fixes #9358 

@FelixMalfait please check this workaround

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-01-23 16:29:54 +01:00
Baptiste DevessierandGitHub bbb0c9a761 Improve the design of workflow nodes (#9810)
- Go over every node in the workflows and fix the styles to conform to
Figma
- Create stories for every node type
2025-01-23 15:12:37 +00:00
Raphaël BosiandGitHub 337b6a86ab 251 create top bar chips inside the command menu (#9809)
Closes #https://github.com/twentyhq/core-team-issues/issues/251



https://github.com/user-attachments/assets/065c97fe-1daf-4b48-9d57-6bbb96d24ede
2025-01-23 14:44:21 +01:00
Ana Sofia Marin AlexandreandGitHub cc53cb3b7b Add metered product to checkout session (#9788)
Solves https://github.com/twentyhq/private-issues/issues/238

**TLDR:**
Add metered product in the checkout session when purchasing a
subscription

**In order to test:**

1. Have the environment variable IS_BILLING_ENABLED set to true and add
the other required environment variables for Billing to work
2. Do a database reset (to ensure that the new feature flag is properly
added and that the billing tables are created)
3. Run the command: npx nx run twenty-server:command
billing:sync-plans-data (if you don't do that the products and prices
will not be present in the database)
4. Run the server , the frontend, the worker, and the stripe listen
command (stripe listen --forward-to
http://localhost:3000/billing/webhooks)
5. Buy a subscription for the Acme workspace , in the checkout session
you should see that there is two products
2025-01-23 14:28:13 +01:00
GuillimandGitHub e7ba1c82b4 isMicrosoftAuthEnabled = true (#9812) 2025-01-23 12:22:03 +01:00
GuillimandGitHub 2f0fa7ae3e Microsoft-multi-tenant (#9801)
Microsoft fixes
2025-01-23 12:08:08 +01:00
Lucas BordeauandGitHub bddca09451 Refactored table filters to consume new currentRecordFilters component state (#9652)
This PR implements a first real use case, now currentRecordFilters
component state acts as the global record filter reference.

It is set by the view initially and can be reset to view filters state
at any point.

This new state is also modified by two new upsertRecordFilter /
removeRecordFilter hooks that will be drop-in replacement of the actual
upsertCombinedViewFilter and removeCombinediewFilter hooks.

This PR implements the logic to manipulate record filters but only reads
it to make the table find many request, all other features are still
relying on the old view filter implementation.

Advanced filters are ignored because they are hidden and because this
effort is made precisely to allow the completion of the advanced filters
feature.
2025-01-23 11:09:44 +01:00
martmullandGitHub 3ab193f298 Remove isServerlessFunctionSettingsEnabled feature flag (#9797)
Removes `isFunctionSettingsEnabled` feature flag
We consider this featureFlag as false for everyone. We decided to keep
the code in the code base for now
2025-01-22 22:39:52 +01:00
Charles BochetandGitHub e881616822 Fix captcha token not being loaded early enough (#9800)
I've re-investigated the captchaToken being invalid on workspace domain
url in case where email is passed in the URL.
We need to be a bit more granular
2025-01-22 18:11:21 +01:00
WeikoandGitHub 5902dbd0b4 Fix signup submit disabled when waiting for captcha without provider (#9796) 2025-01-22 17:49:11 +01:00
Charles BochetandGitHub d340605578 Fix View Picker broken (#9798)
In this PR:
- fixing a regression introduced in a recent PR (#9735)
- fixing a typing issue in ViewPicker @ehconitin FYI
2025-01-22 17:48:27 +01:00
Jérémy MandGitHub b662609948 feat: add targetFieldMetadataId and migration script for relations (#9793)
Fix https://github.com/twentyhq/core-team-issues/issues/238 and
https://github.com/twentyhq/core-team-issues/issues/239
2025-01-22 17:01:54 +01:00
Paul RastoinandGitHub 984dc4dec0 [MISC] Twenty-emails as package (#9770)
Charles has done everything within
https://github.com/twentyhq/twenty/pull/9754
Last thing to be done is removing the custom module `path` configured in
the `tsconfig.json`

close https://github.com/twentyhq/core-team-issues/issues/282
2025-01-22 16:33:48 +01:00
8ab01ebef4 [BUG] Record settings not saved (#9762)
# Introduction
By initially fixing this Fixes #9381, discovered other behavior that
have been fix.
Overall we encountered a bug that corrupts a workspace and make the
browser + api crash
This issue https://github.com/twentyhq/core-team-issues/issues/25
suggests a refactor that has final save button instead of auto-save

## `labelIdentifierFieldMetadataId` form default value
The default value resulted in being undefined, resulting in react hook
form `labelIdentifierFieldMetadataId` is required field error.

### Fix
Setting default value fallback to `null`  as field is `nullable`

## `SettingsDataModelObjectSettingsFormCard` never triggers form
Unless I'm mistaken in production touching any fields within
`SettingsDataModelObjectSettingsFormCard` would never trigger form
submission until you also modify `SettingsDataModelObjectAboutForm`
fields

### Fix
Provide and apply `onblur` that triggers the form on both
`SettingsDataModelObjectSettingsFormCard` inputs

## Wrong default `labelIdentifierFieldMetadataItem` on first page render
When landing on the page for the first time, if a custom
`labelIdentifierFieldMetadataItem` has been set it won't be computed
within the `PreviewCard`.
Occurs when `labelIdentifierFieldMetadataId` form default value is
undefined, due to `any` injection.

### Fix
In the `getLabelIdentifierFieldMetadataItem` check the
`labelIdentifierFieldMetadataIdFormValue` definition, if undefined
fallback to current `objectMetadata` identifier

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-01-22 16:32:57 +01:00
martmullandGitHub 80c9ebfd4e Remove isGmailSendEmailScopeEnabled featureFlag (#9787)
as title
2025-01-22 15:53:40 +01:00
MarieandGitHub d759559506 Fix COUNT operation on view group aggregate header (#9789)
Fixes
[sentry](https://twenty-v7.sentry.io/issues/6235128210/?referrer=discord&notification_uuid=898a081c-f8c7-42b8-b598-7660470a1975&alert_rule_id=15135099&alert_type=issue)

In a [previous work](https://github.com/twentyhq/twenty/pull/9749) I set
the default field to run totalCount aggregate operation on to the "name"
field, which I was wrong think was present on all objects.
2025-01-22 15:05:38 +01:00
Raphaël BosiandGitHub 8213995887 Open showpage on workflow creation (#9714)
- Created an new component state
`isRecordEditableNameRenamingComponentState`
- Updated `useCreateNewTableRecord` to open the ShowPage on workflow
creation
- Refactored `RecordEditableName` and its components to remove the
useEffect (This was causing the recordName state to be updated after the
focus on `NavigationDrawerInput`, but we want the text so be selected
after the update).
- Introduced a new component `EditableBreadcrumbItem`
- Created an autosizing text input: This is done by a hack using a span
inside a div and the input position is set to absolute and takes the
size of the div. There are two problems that I didn't manage to fix:
If the text is too long, the title overflows, and the letter spacing is
different between the span and the input creating a small offset.


https://github.com/user-attachments/assets/4aa1e177-7458-4691-b0c8-96567b482206


New text input component:


https://github.com/user-attachments/assets/94565546-fe2b-457d-a1d8-907007e0e2ce
2025-01-22 14:44:10 +01:00
Thomas TrompetteandGitHub 441b88b7e1 Seed workflow views and favorites in upgrade to 0.41 (#9785)
- Sync metadata to create workflow entities, since those are not behind
a flag anymore
- Seed workflow views
- Seed workspace favorite for workflow
- Put all steps in upgrade command
2025-01-22 14:40:44 +01:00
Baptiste DevessierandGitHub 26aca9508b Align the workflow visualizer's nodes on the left (#9776)
**This PR implements a new layout for the visualizer, but the dimensions
of the nodes will change soon. I used hard-coded dimensions, like
`40px`, but I'll update them when I work on fixing the nodes' design. I
think we can merge this PR first and then fix the nodes' design.**



https://github.com/user-attachments/assets/580fa812-ee8e-4452-b6ac-ca55ecb31759
2025-01-22 11:03:36 +01:00
Ana Sofia Marin AlexandreandGitHub 7d30b7577d add fetch billing products from tables instead of env variables (#9601)
Solves https://github.com/twentyhq/private-issues/issues/237

**TLDR:**

- Fetches billing products and prices from the tables BilllingProducts
and BillingPrices instead of fetching the product from the environment
variables and the prices from the stripe API.
- Adds new feature flag for this feature
- Fixes calls used to fetch stripe products and prices for the command
Billing Sync Plans Data.


**In order to test:**

1. Have the environment variable IS_BILLING_ENABLED set to true and add
the other required environment variables for Billing to work
2. Do a database reset (to ensure that the new feature flag is properly
added and that the billing tables are created)
3. Run the command: `npx nx run twenty-server:command
billing:sync-plans-data` (if you don't do that the products and prices
will not be present in the database)
4. Run the server , the frontend, the worker, and the stripe listen
command (`stripe listen --forward-to
http://localhost:3000/billing/webhooks`)
5. Buy a subscription for the Acme workspace and play with the project

**Doing**

I think there is some room of progress for the function
formatProductPrices, I used a similar version that was done before, I'll
look into that.
2025-01-21 20:19:29 +01:00
GuillimandGitHub 3d2bb03c6d microsoft domain publisher (#9771)
for MS auth and outlook recognition
2025-01-21 18:17:16 +01:00
Antoine MoreauxandGitHub aef01816d7 fix(recaptcha|sso): recaptcha not generated issue (#9764)
Fix https://github.com/twentyhq/twenty/issues/9759
https://github.com/twentyhq/twenty/issues/9694
https://github.com/twentyhq/twenty/issues/9323
2025-01-21 17:07:20 +00:00
Antoine MoreauxandGitHub f4779a02ca fix(workspace): ensure proper handling of updates and errors (#9752)
Added `await` to `updateWorkspaceById` in resolver for proper async
handling. Enhanced workspace settings UI with specific error handling
for subdomain conflicts and improved feedback for invalid form values.


Fix
https://github.com/twentyhq/twenty/issues/9709#issuecomment-2597919251
2025-01-21 17:17:58 +01:00
2696 changed files with 228484 additions and 52178 deletions
+1 -1
View File
@@ -10,6 +10,7 @@ module.exports = {
'lingui',
],
rules: {
'lingui/no-single-variables-to-translate': 'off',
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
'no-console': ['warn', { allow: ['group', 'groupCollapsed', 'groupEnd'] }],
'no-control-regex': 0,
@@ -81,7 +82,6 @@ module.exports = {
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-empty-interface': [
'error',
{
+2 -2
View File
@@ -81,7 +81,7 @@ jobs:
run: NODE_ENV=production NODE_OPTIONS="--max-old-space-size=10240" npx nx build twenty-front
- name: Build server
run: NODE_ENV=production npx nx build twenty-server
run: npx nx build twenty-server
- name: Create and setup database
run: |
@@ -103,7 +103,7 @@ jobs:
- name: Start worker
run: |
npx nx run twenty-server:worker:ci &
npx nx run twenty-server:worker &
echo "Worker started"
- name: Run Playwright tests
+20 -1
View File
@@ -78,7 +78,15 @@ jobs:
npx nx run twenty-server:database:init:prod
npx nx run twenty-server:database:migrate:prod
- name: Worker / Run
run: npx nx run twenty-server:worker:ci
run: |
timeout 30s npx nx run twenty-server:worker || exit_code=$?
if [ $exit_code -eq 124 ]; then
# If timeout was reached (exit code 124), consider it a success
exit 0
elif [ $exit_code -ne 0 ]; then
# If worker failed for other reasons, fail the build
exit $exit_code
fi
- name: Server / Check for Pending Migrations
run: |
METADATA_MIGRATION_OUTPUT=$(npx nx run twenty-server:typeorm migration:generate metadata-migration-check -d src/database/typeorm/metadata/metadata.datasource.ts || true)
@@ -180,6 +188,17 @@ jobs:
uses: ./.github/workflows/actions/restore-cache
with:
key: ${{ env.SERVER_SETUP_CACHE_KEY }}
- name: Server / Build
run: npx nx build twenty-server
- name: Build dependencies
run: |
npx nx build twenty-shared
npx nx build twenty-emails
- name: Server / Create Test DB
env:
NODE_ENV: test
run: |
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
- name: Server / Run Integration Tests
uses: ./.github/workflows/actions/nx-affected
with:
@@ -39,7 +39,7 @@ jobs:
echo "Generating secrets..."
echo "# === Randomly generated secrets ===" >>.env
echo "APP_SECRET=$(openssl rand -base64 32)" >>.env
echo "PGPASSWORD_SUPERUSER=$(openssl rand -base64 32)" >>.env
echo "PGPASSWORD_SUPERUSER=$(openssl rand -hex 16)" >>.env
echo "Docker compose up..."
docker compose up -d || {
+131
View File
@@ -0,0 +1,131 @@
# Pull down translations from Crowdin every two hours or when triggered manually.
# When force_pull input is true, translations will be pulled regardless of compilation status.
name: 'Pull translations from Crowdin'
on:
schedule:
- cron: '0 */2 * * *' # Every two hours.
workflow_dispatch:
inputs:
force_pull:
description: 'Force pull translations regardless of compilation status'
required: false
type: boolean
default: false
workflow_call:
inputs:
force_pull:
description: 'Force pull translations regardless of compilation status'
required: false
type: boolean
default: false
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
pull_translations:
name: Pull translations
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ github.token }}
ref: ${{ github.head_ref || github.ref_name }}
- name: Setup i18n branch
run: |
git fetch origin i18n || true
git checkout -B i18n origin/i18n || git checkout -b i18n
- name: Install dependencies
uses: ./.github/workflows/actions/yarn-install
- name: Build twenty-shared
run: npx nx build twenty-shared
# Strict mode fails if there are missing translations.
- name: Compile translations
id: compile_translations_strict
run: |
npx nx run twenty-server:lingui:compile --strict
npx nx run twenty-emails:lingui:compile --strict
npx nx run twenty-front:lingui:compile --strict
continue-on-error: true
- name: Stash any changes before pulling translations
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@twenty.com'
git add .
git stash
- name: Pull translations from Crowdin
if: inputs.force_pull || steps.compile_translations_strict.outcome == 'failure'
uses: crowdin/github-action@v2
with:
upload_sources: false
upload_translations: false
download_translations: true
export_only_approved: false
localization_branch_name: i18n
base_url: 'https://twenty.api.crowdin.com'
auto_approve_imported: false
import_eq_suggestions: false
download_sources: false
push_sources: false
skip_untranslated_strings: false
skip_untranslated_files: false
push_translations: true
create_pull_request: false
skip_ref_checkout: true
dryrun_action: false
env:
GITHUB_TOKEN: ${{ github.token }}
CROWDIN_PROJECT_ID: '1'
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
# As the files are extracted from a Docker container, they belong to root:root
# We need to fix this before the next steps
- name: Fix file permissions
run: sudo chown -R runner:docker .
- name: Compile translations
id: compile_translations
if: inputs.force_pull || steps.compile_translations_strict.outcome == 'failure'
run: |
npx nx run twenty-server:lingui:compile
npx nx run twenty-emails:lingui:compile
npx nx run twenty-front:lingui:compile
git status
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@twenty.com'
git add .
if ! git diff --staged --quiet --exit-code; then
git commit -m "chore: compile translations"
echo "changes_detected=true" >> $GITHUB_OUTPUT
else
echo "changes_detected=false" >> $GITHUB_OUTPUT
fi
- name: Push changes
if: steps.compile_translations.outputs.changes_detected == 'true'
run: git push origin HEAD:i18n
- name: Create pull request
if: steps.compile_translations.outputs.changes_detected == 'true'
run: |
if git diff --name-only origin/main..HEAD | grep -q .; then
gh pr create -B main -H i18n --title 'i18n - translations' --body 'Created by Github action' || true
else
echo "No file differences between branches, skipping PR creation"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+104
View File
@@ -0,0 +1,104 @@
name: 'Push translations to Crowdin'
on:
workflow_dispatch:
workflow_call:
push:
branches: ['main']
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
extract_translations:
name: Extract and upload translations
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ github.token }}
ref: main
- name: Setup i18n branch
run: |
git fetch origin i18n || true
git checkout -B i18n origin/i18n || git checkout -b i18n
- name: Install dependencies
uses: ./.github/workflows/actions/yarn-install
- name: Build dependencies
run: npx nx build twenty-shared
- name: Extract translations
run: |
npx nx run twenty-server:lingui:extract
npx nx run twenty-emails:lingui:extract
npx nx run twenty-front:lingui:extract
- name: Check and commit extracted files
id: check_extract_changes
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@twenty.com'
git add .
if ! git diff --staged --quiet --exit-code; then
git commit -m "chore: extract translations"
echo "changes_detected=true" >> $GITHUB_OUTPUT
else
echo "changes_detected=false" >> $GITHUB_OUTPUT
fi
- name: Compile translations
run: |
npx nx run twenty-server:lingui:compile
npx nx run twenty-emails:lingui:compile
npx nx run twenty-front:lingui:compile
- name: Check and commit compiled files
id: check_compile_changes
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@twenty.com'
git add .
if ! git diff --staged --quiet --exit-code; then
git commit -m "chore: compile translations"
echo "changes_detected=true" >> $GITHUB_OUTPUT
else
echo "changes_detected=false" >> $GITHUB_OUTPUT
fi
- name: Push changes and create remote branch if needed
if: steps.check_extract_changes.outputs.changes_detected == 'true' || steps.check_compile_changes.outputs.changes_detected == 'true'
run: git push origin HEAD:i18n
- name: Upload missing translations
if: steps.check_extract_changes.outputs.changes_detected == 'true'
uses: crowdin/github-action@v2
with:
upload_sources: true
upload_translations: true
download_translations: false
localization_branch_name: i18n
base_url: 'https://twenty.api.crowdin.com'
env:
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
CROWDIN_PROJECT_ID: 1
# Visit https://crowdin.com/settings#api-key to create this token
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Create a pull request
if: steps.check_extract_changes.outputs.changes_detected == 'true' || steps.check_compile_changes.outputs.changes_detected == 'true'
run: |
if git diff --name-only origin/main..HEAD | grep -q .; then
gh pr create -B main -H i18n --title 'i18n - translations' --body 'Created by Github action' || true
else
echo "No file differences between branches, skipping PR creation"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2
View File
@@ -41,3 +41,5 @@ dump.rdb
/devenv.nix
/flake.lock
/flake.nix
.crowdin.yml
+21
View File
@@ -60,6 +60,27 @@
"cwd": "${workspaceFolder}",
"internalConsoleOptions": "neverOpen",
"envFile": "${workspaceFolder}/packages/twenty-e2e-testing/.env"
},
{
"type": "node",
"request": "launch",
"name": "twenty-server - debug integration test file (to launch with test file open)",
"runtimeExecutable": "npx",
"runtimeArgs": [
"nx",
"run",
"twenty-server:jest",
"--",
"--config",
"./jest-integration.config.ts",
"${relativeFile}"
],
"cwd": "${workspaceFolder}/packages/twenty-server",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"env": {
"NODE_ENV": "test"
},
}
]
}
+2
View File
@@ -1,3 +1,5 @@
enableHardenedMode: true
enableInlineHunks: true
nodeLinker: node-modules
+4 -10
View File
@@ -24,16 +24,9 @@
<br>
# Demo
# Installation
Go to <a href="https://demo.twenty.com/">demo.twenty.com</a> and login with the following credentials:
```
email: tim@apple.dev
password: Applecar2025
```
See also:
See:
🚀 [Self-hosting](https://twenty.com/developers/section/self-hosting)
🖥️ [Local Setup](https://twenty.com/developers/local-setup)
@@ -152,7 +145,7 @@ Below are a few features we have implemented to date:
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
- [React](https://reactjs.org/), with [Recoil](https://recoiljs.org/) and [Emotion](https://emotion.sh/)
- [Greptile](https://greptile.com) for code reviews.
- [TranslationIO](https://translation.io/) for translations.
- [Lingui](https://lingui.dev/) and [Crowdin](https://twenty.crowdin.com/twenty) for translations.
# Join the Community
@@ -161,6 +154,7 @@ Below are a few features we have implemented to date:
- Subscribe to releases (watch -> custom -> releases)
- Follow us on [Twitter](https://twitter.com/twentycrm) or [LinkedIn](https://www.linkedin.com/company/twenty/)
- Join our [Discord](https://discord.gg/cx5n4Jzs57)
- Improve translations on [Crowdin](https://twenty.crowdin.com/twenty)
- [Contributions](https://github.com/twentyhq/twenty/contribute) are, of course, most welcome!
+31
View File
@@ -0,0 +1,31 @@
#
# Basic Crowdin CLI configuration
# See https://crowdin.github.io/crowdin-cli/configuration for more information
# See https://support.crowdin.com/developer/configuration-file/ for all available options
#
#
# Defines whether to preserve the original directory structure in the Crowdin project
# Recommended to set to true
#
"preserve_hierarchy": true
#
# Files configuration.
# See https://support.crowdin.com/developer/configuration-file/ for all available options
#
files: [
{
#
# Source files filter
# e.g. "/resources/en/*.json"
#
"source": "**/en.po",
#
# Translation files filter
# e.g. "/resources/%two_letters_code%/%original_file_name%"
#
"translation": "%original_path%/%locale%.po",
}
]
+11 -8
View File
@@ -5,9 +5,11 @@
"@apollo/server": "^4.7.3",
"@aws-sdk/client-lambda": "^3.614.0",
"@aws-sdk/client-s3": "^3.363.0",
"@aws-sdk/client-sts": "^3.744.0",
"@aws-sdk/credential-providers": "^3.363.0",
"@blocknote/mantine": "^0.22.0",
"@blocknote/react": "^0.22.0",
"@blocknote/server-util": "0.17.1",
"@codesandbox/sandpack-react": "^2.13.5",
"@dagrejs/dagre": "^1.1.2",
"@emotion/react": "^11.11.1",
@@ -37,14 +39,14 @@
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/serve-static": "^4.0.1",
"@nestjs/terminus": "^9.2.2",
"@nestjs/terminus": "^11.0.0",
"@nestjs/typeorm": "^10.0.0",
"@nx/eslint-plugin": "^17.2.8",
"@octokit/graphql": "^7.0.2",
"@ptc-org/nestjs-query-core": "^4.2.0",
"@ptc-org/nestjs-query-typeorm": "4.2.1-alpha.2",
"@react-email/components": "0.0.12",
"@react-email/render": "0.0.10",
"@react-email/components": "0.0.32",
"@react-email/render": "0.0.17",
"@sentry/node": "^8",
"@sentry/profiling-node": "^8",
"@sentry/react": "^8",
@@ -61,7 +63,7 @@
"@types/nodemailer": "^6.4.14",
"@types/passport-microsoft": "^1.0.3",
"@wyw-in-js/vite": "^0.5.3",
"@xyflow/react": "^12.3.5",
"@xyflow/react": "^12.4.2",
"add": "^2.0.6",
"addressparser": "^1.0.1",
"afterframe": "^1.0.2",
@@ -72,10 +74,11 @@
"bcrypt": "^5.1.1",
"better-sqlite3": "^9.2.2",
"body-parser": "^1.20.2",
"bullmq": "^4.15.0",
"bullmq": "^5.40.0",
"bytes": "^3.1.2",
"class-transformer": "^0.5.1",
"clsx": "^2.1.1",
"cron-validate": "^1.4.5",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
"danger-plugin-todos": "^1.3.1",
@@ -164,7 +167,7 @@
"react-hotkeys-hook": "^4.4.4",
"react-icons": "^4.12.0",
"react-imask": "^7.6.0",
"react-intersection-observer": "^9.5.2",
"react-intersection-observer": "^9.15.1",
"react-loading-skeleton": "^3.3.1",
"react-phone-number-input": "^3.3.4",
"react-responsive": "^9.0.2",
@@ -205,7 +208,7 @@
"@graphql-codegen/typescript-operations": "^3.0.4",
"@graphql-codegen/typescript-react-apollo": "^3.3.7",
"@lingui/cli": "^5.1.2",
"@lingui/swc-plugin": "^5.0.2",
"@lingui/swc-plugin": "^5.1.0",
"@lingui/vite-plugin": "^5.1.2",
"@microsoft/microsoft-graph-types": "^2.40.0",
"@nestjs/cli": "^9.0.0",
@@ -240,7 +243,7 @@
"@stylistic/eslint-plugin": "^1.5.0",
"@swc-node/register": "1.8.0",
"@swc/cli": "^0.3.12",
"@swc/core": "~1.3.100",
"@swc/core": "1.7.42",
"@swc/helpers": "~0.5.2",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/react": "14.0.0",
@@ -6,5 +6,8 @@
"type": "module",
"scripts": {
"build": "npx vite build"
},
"dependencies": {
"twenty-shared": "workspace:*"
}
}
@@ -8,7 +8,8 @@
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "{projectRoot}/dist"
}
},
"dependsOn": ["^build"]
},
"start": {
"executor": "nx:run-commands",
@@ -1,4 +1,4 @@
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
// Open options page programmatically in a new tab.
// chrome.runtime.onInstalled.addListener((details) => {
@@ -1,4 +1,4 @@
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
interface CustomDiv extends HTMLDivElement {
onClickHandler: (newHandler: () => void) => void;
@@ -1,10 +1,10 @@
import { isDefined } from 'twenty-shared';
import { createDefaultButton } from '~/contentScript/createButton';
import changeSidePanelUrl from '~/contentScript/utils/changeSidepanelUrl';
import extractCompanyLinkedinLink from '~/contentScript/utils/extractCompanyLinkedinLink';
import extractDomain from '~/contentScript/utils/extractDomain';
import { createCompany, fetchCompany } from '~/db/company.db';
import { CompanyInput } from '~/db/types/company.types';
import { isDefined } from '~/utils/isDefined';
export const checkIfCompanyExists = async () => {
const { tab: activeTab } = await chrome.runtime.sendMessage({
@@ -1,9 +1,9 @@
import { isDefined } from 'twenty-shared';
import { createDefaultButton } from '~/contentScript/createButton';
import changeSidePanelUrl from '~/contentScript/utils/changeSidepanelUrl';
import extractFirstAndLastName from '~/contentScript/utils/extractFirstAndLastName';
import { createPerson, fetchPerson } from '~/db/person.db';
import { PersonInput } from '~/db/types/person.types';
import { isDefined } from '~/utils/isDefined';
export const checkIfPersonExists = async () => {
const { tab: activeTab } = await chrome.runtime.sendMessage({
@@ -1,6 +1,6 @@
import { isDefined } from 'twenty-shared';
import { insertButtonForCompany } from '~/contentScript/extractCompanyProfile';
import { insertButtonForPerson } from '~/contentScript/extractPersonProfile';
import { isDefined } from '~/utils/isDefined';
// Inject buttons into the DOM when SPA is reloaded on the resource url.
// e.g. reload the page when on https://www.linkedin.com/in/mabdullahabaid/
@@ -1,4 +1,4 @@
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
const btn = document.getElementById('twenty-settings-btn');
if (!isDefined(btn)) {
@@ -1,4 +1,4 @@
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
const changeSidePanelUrl = async (url: string) => {
if (isDefined(url)) {
@@ -1,6 +1,6 @@
// Extract "https://www.linkedin.com/company/twenty/" from any of the following urls, which the user can visit while on the company page.
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
// "https://www.linkedin.com/company/twenty/" "https://www.linkedin.com/company/twenty/about/" "https://www.linkedin.com/company/twenty/people/".
const extractCompanyLinkedinLink = (activeTabUrl: string) => {
@@ -1,10 +1,10 @@
import { isDefined } from 'twenty-shared';
import {
ExchangeAuthCodeInput,
ExchangeAuthCodeResponse,
Tokens,
} from '~/db/types/auth.types';
import { EXCHANGE_AUTHORIZATION_CODE } from '~/graphql/auth/mutations';
import { isDefined } from '~/utils/isDefined';
import { callMutation } from '~/utils/requestDb';
export const exchangeAuthorizationCode = async (
@@ -1,3 +1,4 @@
import { isDefined } from 'twenty-shared';
import {
CompanyInput,
CreateCompanyResponse,
@@ -6,7 +7,6 @@ import {
import { Company, CompanyFilterInput } from '~/generated/graphql';
import { CREATE_COMPANY } from '~/graphql/company/mutations';
import { FIND_COMPANY } from '~/graphql/company/queries';
import { isDefined } from '~/utils/isDefined';
import { callMutation, callQuery } from '../utils/requestDb';
@@ -1,3 +1,4 @@
import { isDefined } from 'twenty-shared';
import {
CreatePersonResponse,
FindPersonResponse,
@@ -6,7 +7,6 @@ import {
import { Person, PersonFilterInput } from '~/generated/graphql';
import { CREATE_PERSON } from '~/graphql/person/mutations';
import { FIND_PERSON } from '~/graphql/person/queries';
import { isDefined } from '~/utils/isDefined';
import { callMutation, callQuery } from '../utils/requestDb';
@@ -1,8 +1,8 @@
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { isDefined } from 'twenty-shared';
import { Tokens } from '~/db/types/auth.types';
import { RENEW_TOKEN } from '~/graphql/auth/mutations';
import { isDefined } from '~/utils/isDefined';
export const renewToken = async (
appToken: string,
@@ -1,8 +1,8 @@
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared';
import Settings from '~/options/Settings';
import Sidepanel from '~/options/Sidepanel';
import { isDefined } from '~/utils/isDefined';
const App = () => {
const [currentScreen, setCurrentScreen] = useState('');
@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { MainButton } from '@/ui/input/button/MainButton';
import { TextInput } from '@/ui/input/components/TextInput';
import { isDefined } from 'twenty-shared';
import { clearStore } from '~/utils/apolloClient';
import { isDefined } from '~/utils/isDefined';
const StyledWrapper = styled.div`
align-items: center;
@@ -1,8 +1,8 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import styled from '@emotion/styled';
import { useCallback, useEffect, useRef, useState } from 'react';
import { MainButton } from '@/ui/input/button/MainButton';
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
const StyledIframe = styled.iframe`
display: block;
@@ -1,8 +1,8 @@
import { useEffect, useState } from 'react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
export type ToggleSize = 'small' | 'medium';
@@ -2,7 +2,7 @@ import { ApolloClient, from, HttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
import { isDefined } from '~/utils/isDefined';
import { isDefined } from 'twenty-shared';
export const clearStore = () => {
chrome.storage.local.remove([
@@ -1,8 +1,8 @@
import { OperationVariables } from '@apollo/client';
import { DocumentNode } from 'graphql';
import { isDefined } from 'twenty-shared';
import getApolloClient from '~/utils/apolloClient';
import { isDefined } from '~/utils/isDefined';
export const callQuery = async <T>(
query: DocumentNode,
+3 -2
View File
@@ -1,8 +1,9 @@
TAG=latest
#PGUSER_SUPERUSER=postgres
#PGPASSWORD_SUPERUSER=replace_me_with_a_strong_password
#PG_DATABASE_USER=postgres
#PG_DATABASE_PASSWORD=replace_me_with_a_strong_password_without_special_characters
#PG_DATABASE_HOST=db
#PG_DATABASE_PORT=5432
#REDIS_URL=redis://redis:6379
SERVER_URL=http://localhost:3000
+6 -6
View File
@@ -20,8 +20,8 @@ services:
ports:
- "3000:3000"
environment:
PORT: 3000
PG_DATABASE_URL: postgres://${PGUSER_SUPERUSER:-postgres}:${PGPASSWORD_SUPERUSER:-postgres}@${PG_DATABASE_HOST:-db:5432}/default
NODE_PORT: 3000
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
SERVER_URL: ${SERVER_URL}
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
@@ -47,7 +47,7 @@ services:
image: twentycrm/twenty:${TAG:-latest}
command: ["yarn", "worker:prod"]
environment:
PG_DATABASE_URL: postgres://${PGUSER_SUPERUSER:-postgres}:${PGPASSWORD_SUPERUSER:-postgres}@${PG_DATABASE_HOST:-db:5432}/default
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
SERVER_URL: ${SERVER_URL}
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
DISABLE_DB_MIGRATIONS: "true" # it already runs on the server
@@ -70,12 +70,12 @@ services:
volumes:
- db-data:/home/postgres/pgdata
environment:
PGUSER_SUPERUSER: ${PGUSER_SUPERUSER:-postgres}
PGPASSWORD_SUPERUSER: ${PGPASSWORD_SUPERUSER:-postgres}
PGUSER_SUPERUSER: ${PG_DATABASE_USER:-postgres}
PGPASSWORD_SUPERUSER: ${PG_DATABASE_PASSWORD:-postgres}
ALLOW_NOSSL: "true"
SPILO_PROVIDER: "local"
healthcheck:
test: pg_isready -U ${PGUSER_SUPERUSER:-postgres} -h localhost -d postgres
test: pg_isready -U ${PG_DATABASE_USER:-postgres} -h localhost -d postgres
interval: 5s
timeout: 5s
retries: 10
@@ -33,7 +33,7 @@ spec:
image: twentycrm/twenty:latest
imagePullPolicy: Always
env:
- name: PORT
- name: NODE_PORT
value: 3000
- name: SERVER_URL
value: "https://crm.example.com:443"
@@ -45,8 +45,6 @@ spec:
value: "false"
- name: STORAGE_TYPE
value: "local"
- name: "MESSAGE_QUEUE_TYPE"
value: "bull-mq"
- name: "ACCESS_TOKEN_EXPIRES_IN"
value: "7d"
- name: "LOGIN_TOKEN_EXPIRES_IN"
@@ -34,10 +34,6 @@ spec:
value: "false" # it already runs on the server
- name: STORAGE_TYPE
value: "local"
- name: "MESSAGE_QUEUE_TYPE"
value: "bull-mq"
- name: "CACHE_STORAGE_TYPE"
value: "redis"
- name: "REDIS_URL"
value: "redis://twentycrm-redis.twentycrm.svc.cluster.local:6379"
- name: APP_SECRET
@@ -38,13 +38,9 @@ resource "kubernetes_deployment" "twentycrm_server" {
tty = true
env {
name = "PORT"
name = "NODE_PORT"
value = "3000"
}
# env {
# name = "DEBUG_MODE"
# value = false
# }
env {
name = "SERVER_URL"
@@ -68,10 +64,6 @@ resource "kubernetes_deployment" "twentycrm_server" {
name = "STORAGE_TYPE"
value = "local"
}
env {
name = "MESSAGE_QUEUE_TYPE"
value = "bull-mq"
}
env {
name = "ACCESS_TOKEN_EXPIRES_IN"
value = "7d"
@@ -48,11 +48,6 @@ resource "kubernetes_deployment" "twentycrm_worker" {
value = "postgres://twenty:${var.twentycrm_pgdb_admin_password}@${kubernetes_service.twentycrm_db.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local/default"
}
env {
name = "CACHE_STORAGE_TYPE"
value = "redis"
}
env {
name = "REDIS_URL"
value = "redis://${kubernetes_service.twentycrm_redis.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local:6379"
@@ -67,10 +62,6 @@ resource "kubernetes_deployment" "twentycrm_worker" {
name = "STORAGE_TYPE"
value = "local"
}
env {
name = "MESSAGE_QUEUE_TYPE"
value = "bull-mq"
}
env {
name = "APP_SECRET"
+4 -5
View File
@@ -90,12 +90,11 @@ else
fi
# Generate random strings for secrets
echo "# === Randomly generated secret ===" >>.env
echo "APP_SECRET=$(openssl rand -base64 32)" >>.env
echo "# === Randomly generated secret ===" >> .env
echo "APP_SECRET=$(openssl rand -base64 32)" >> .env
# Issue with Postgres spilo?
#echo "" >>.env
#echo "PGPASSWORD_SUPERUSER=$(openssl rand -hex 16)" >>.env
echo "" >> .env
echo "PG_DATABASE_PASSWORD=$(openssl rand -hex 16)" >> .env
echo -e "\t• .env configuration completed"
@@ -17,9 +17,12 @@ export class WorkflowVisualizerPage {
readonly deactivateWorkflowButton: Locator;
readonly addTriggerButton: Locator;
readonly commandMenu: Locator;
readonly workflowNameButton: Locator;
readonly workflowNameLabel: Locator;
readonly triggerNode: Locator;
readonly background: Locator;
readonly useAsDraftButton: Locator;
readonly overrideDraftButton: Locator;
readonly discardDraftButton: Locator;
#actionNames: Record<WorkflowActionType, string> = {
'create-record': 'Create Record',
@@ -65,11 +68,18 @@ export class WorkflowVisualizerPage {
});
this.addTriggerButton = page.getByText('Add a Trigger');
this.commandMenu = page.getByTestId('command-menu');
this.workflowNameButton = page.getByRole('button', {
name: this.workflowName,
});
this.workflowNameLabel = page
.getByTestId('top-bar-title')
.getByText(this.workflowName);
this.triggerNode = this.#page.getByTestId('rf__node-trigger');
this.background = page.locator('.react-flow__pane');
this.useAsDraftButton = page.getByRole('button', { name: 'Use as draft' });
this.overrideDraftButton = page.getByRole('button', {
name: 'Override Draft',
});
this.discardDraftButton = page.getByRole('button', {
name: 'Discard Draft',
});
}
async createOneWorkflow() {
@@ -90,7 +100,7 @@ export class WorkflowVisualizerPage {
}
async waitForWorkflowVisualizerLoad() {
await expect(this.workflowNameButton).toBeVisible();
await expect(this.workflowNameLabel).toBeVisible();
}
async goToWorkflowVisualizerPage() {
@@ -122,8 +132,10 @@ export class WorkflowVisualizerPage {
const actionToCreateOption = this.commandMenu.getByText(actionName);
const [createWorkflowStepResponse] = await Promise.all([
this.#page.waitForResponse((response) => {
await actionToCreateOption.click();
const createWorkflowStepResponse = await this.#page.waitForResponse(
(response) => {
if (!response.url().endsWith('/graphql')) {
return false;
}
@@ -131,19 +143,14 @@ export class WorkflowVisualizerPage {
const requestBody = response.request().postDataJSON();
return requestBody.operationName === 'CreateWorkflowVersionStep';
}),
},
);
actionToCreateOption.click(),
]);
const createWorkflowStepResponseBody =
await createWorkflowStepResponse.json();
const createdStepId =
createWorkflowStepResponseBody.data.createWorkflowVersionStep.id;
await expect(
this.#page.getByTestId('command-menu').getByRole('textbox').first(),
).toHaveValue(createdActionName);
const createdActionNode = this.#page
.locator('.react-flow__node.selected')
.getByText(createdActionName);
@@ -221,6 +228,14 @@ export class WorkflowVisualizerPage {
this.getDeleteNodeButton(this.triggerNode).click(),
]);
}
async closeSidePanel() {
const closeButton = this.#page.getByTestId(
'page-header-command-menu-button',
);
await closeButton.click();
}
}
export const test = base.extend<{ workflowVisualizer: WorkflowVisualizerPage }>(
@@ -35,7 +35,7 @@ export class LoginPage {
name: 'Continue with Google',
});
this.loginWithEmailButton = page.getByRole('button', {
name: 'Continue With Email',
name: 'Continue with Email',
});
this.termsOfServiceLink = page.getByRole('link', {
name: 'Terms of Service',
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "twenty-e2e-testing",
"version": "0.41.1",
"version": "0.42.18",
"description": "",
"author": "",
"private": true,
@@ -1,10 +1,10 @@
import { test as base } from '../../lib/fixtures/screenshot';
import { LoginPage } from '../../lib/pom/loginPage';
import { ConfirmationModal } from '../../lib/pom/helper/confirmationModal';
import { LeftMenu } from '../../lib/pom/leftMenu';
import { SettingsPage } from '../../lib/pom/settingsPage';
import { LoginPage } from '../../lib/pom/loginPage';
import { MembersSection } from '../../lib/pom/settings/membersSection';
import { ProfileSection } from '../../lib/pom/settings/profileSection';
import { ConfirmationModal } from '../../lib/pom/helper/confirmationModal';
import { SettingsPage } from '../../lib/pom/settingsPage';
type Fixtures = {
confirmationModal: ConfirmationModal;
@@ -1,15 +0,0 @@
import { expect, test } from '@playwright/test';
test('Check if demo account is working properly @demo-only', async ({
page,
}) => {
await page.goto('https://demo.twenty.com/');
await page.getByRole('button', { name: 'Continue With Email' }).click();
await page.getByRole('button', { name: 'Continue', exact: true }).click();
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page.getByText('Welcome to Twenty')).not.toBeVisible();
await page.waitForTimeout(5000);
await expect(page.getByText('Servers on a coffee break')).not.toBeVisible({
timeout: 5000,
});
});
@@ -19,7 +19,7 @@ test('Login test', async ({ loginPage, page }) => {
async () => {
await page.waitForLoadState('networkidle');
if (
page.url().includes('demo.twenty.com') ||
page.url().includes('app.twenty-next.com') ||
!page.url().includes('app.localhost:3001')
) {
await loginPage.clickLoginWithEmail();
@@ -23,37 +23,29 @@ test('Create workflow', async ({ page }) => {
return requestBody.operationName === 'CreateOneWorkflow';
}),
await createWorkflowButton.click(),
createWorkflowButton.click(),
]);
const nameInputClosedState = page.getByText('Name').first();
await nameInputClosedState.click();
const recordName = page.getByTestId('top-bar-title').getByTestId('tooltip');
await recordName.click();
const nameInput = page.getByRole('textbox');
const nameInput = page.getByTestId('top-bar-title').getByRole('textbox');
await nameInput.fill(NEW_WORKFLOW_NAME);
await nameInput.press('Enter');
const workflowDiagramContainer = page.locator('.react-flow__renderer');
await workflowDiagramContainer.click();
const body = await createWorkflowResponse.json();
const newWorkflowId = body.data.createWorkflow.id;
try {
const newWorkflowRowEntryName = page
.getByTestId(`row-id-${newWorkflowId}`)
.locator('div')
.filter({ hasText: NEW_WORKFLOW_NAME })
.nth(2);
await Promise.all([
page.waitForURL(
(url) => url.pathname === `/object/workflow/${newWorkflowId}`,
),
newWorkflowRowEntryName.click(),
]);
const workflowName = page.getByRole('button', { name: NEW_WORKFLOW_NAME });
const workflowName = page
.getByTestId('top-bar-title')
.getByText(NEW_WORKFLOW_NAME);
await expect(workflowName).toBeVisible();
await expect(page).toHaveURL(`/object/workflow/${newWorkflowId}`);
} finally {
await deleteWorkflow({
page,
@@ -0,0 +1,60 @@
import { expect } from '@playwright/test';
import { test } from '../lib/fixtures/blank-workflow';
test('The workflow run visualizer shows the executed draft version without the last draft changes', async ({
workflowVisualizer,
page,
}) => {
await workflowVisualizer.createInitialTrigger('manual');
const manualTriggerAvailabilitySelect = page.getByRole('button', {
name: 'When record(s) are selected',
});
await manualTriggerAvailabilitySelect.click();
const alwaysAvailableOption = page.getByText(
'When no record(s) are selected',
);
await alwaysAvailableOption.click();
await workflowVisualizer.closeSidePanel();
const { createdStepId: firstStepId } =
await workflowVisualizer.createStep('create-record');
await workflowVisualizer.closeSidePanel();
const launchTestButton = page.getByRole('button', { name: 'Test' });
await launchTestButton.click();
const goToExecutionPageLink = page.getByRole('link', {
name: 'View execution details',
});
const executionPageUrl = await goToExecutionPageLink.getAttribute('href');
expect(executionPageUrl).not.toBeNull();
await workflowVisualizer.deleteStep(firstStepId);
await page.goto(executionPageUrl!);
const workflowRunName = page.getByText('Execution of v1');
await expect(workflowRunName).toBeVisible();
const flowTab = page.getByText('Flow', { exact: true });
await flowTab.click();
const executedFirstStepNode = workflowVisualizer.getStepNode(firstStepId);
await expect(executedFirstStepNode).toBeVisible();
await executedFirstStepNode.click();
await expect(
workflowVisualizer.commandMenu.getByRole('textbox').first(),
).toHaveValue('Create Record');
});
@@ -0,0 +1,177 @@
import { expect } from '@playwright/test';
import { test } from '../lib/fixtures/blank-workflow';
test('Use an old version as draft', async ({ workflowVisualizer, page }) => {
await workflowVisualizer.createInitialTrigger('record-created');
await workflowVisualizer.createStep('create-record');
await workflowVisualizer.background.click();
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Active'),
workflowVisualizer.activateWorkflowButton.click(),
]);
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Draft'),
workflowVisualizer.createStep('delete-record'),
]);
await workflowVisualizer.background.click();
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Active'),
workflowVisualizer.activateWorkflowButton.click(),
]);
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
);
await expect(workflowVisualizer.getAllStepNodes()).toContainText([
'Create Record',
'Delete Record',
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(2);
await expect(workflowVisualizer.useAsDraftButton).not.toBeVisible();
const workflowsLink = page.getByRole('link', { name: 'Workflows' });
await workflowsLink.click();
const recordTableRowForWorkflow = page.getByRole('row', {
name: workflowVisualizer.workflowName,
});
const linkToWorkflow = recordTableRowForWorkflow.getByRole('link', {
name: workflowVisualizer.workflowName,
});
expect(linkToWorkflow).toBeVisible();
const linkToFirstWorkflowVersion = recordTableRowForWorkflow.getByRole(
'link',
{
name: 'v1',
},
);
await linkToFirstWorkflowVersion.click();
await expect(workflowVisualizer.workflowStatus).toHaveText('Archived');
await expect(workflowVisualizer.useAsDraftButton).toBeVisible();
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
);
await expect(workflowVisualizer.getAllStepNodes()).toContainText([
'Create Record',
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(1);
await Promise.all([
page.waitForURL(`/object/workflow/${workflowVisualizer.workflowId}`),
workflowVisualizer.useAsDraftButton.click(),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Draft');
await expect(workflowVisualizer.useAsDraftButton).not.toBeVisible();
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
);
await expect(workflowVisualizer.getAllStepNodes()).toContainText([
'Create Record',
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(1);
});
test('Use an old version as draft while having a pending draft version', async ({
workflowVisualizer,
page,
}) => {
await workflowVisualizer.createInitialTrigger('record-created');
await workflowVisualizer.createStep('create-record');
await workflowVisualizer.background.click();
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Active'),
workflowVisualizer.activateWorkflowButton.click(),
]);
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Draft'),
workflowVisualizer.createStep('delete-record'),
]);
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
);
await expect(workflowVisualizer.getAllStepNodes()).toContainText([
'Create Record',
'Delete Record',
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(2);
await expect(workflowVisualizer.useAsDraftButton).not.toBeVisible();
await workflowVisualizer.closeSidePanel();
const workflowsLink = page.getByRole('link', { name: 'Workflows' });
await workflowsLink.click();
const recordTableRowForWorkflow = page.getByRole('row', {
name: workflowVisualizer.workflowName,
});
const linkToWorkflow = recordTableRowForWorkflow.getByRole('link', {
name: workflowVisualizer.workflowName,
});
expect(linkToWorkflow).toBeVisible();
const linkToFirstWorkflowVersion = recordTableRowForWorkflow.getByRole(
'link',
{
name: 'v1',
},
);
await linkToFirstWorkflowVersion.click();
await expect(workflowVisualizer.workflowStatus).toHaveText('Active');
await expect(workflowVisualizer.useAsDraftButton).toBeVisible();
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
);
await expect(workflowVisualizer.getAllStepNodes()).toContainText([
'Create Record',
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(1);
await Promise.all([
expect(workflowVisualizer.overrideDraftButton).toBeVisible(),
workflowVisualizer.useAsDraftButton.click(),
]);
await Promise.all([
page.waitForURL(`/object/workflow/${workflowVisualizer.workflowId}`),
workflowVisualizer.overrideDraftButton.click(),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Draft');
await expect(workflowVisualizer.useAsDraftButton).not.toBeVisible();
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
);
await expect(workflowVisualizer.getAllStepNodes()).toContainText([
'Create Record',
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(1);
await expect(workflowVisualizer.activateWorkflowButton).toBeVisible();
await expect(workflowVisualizer.discardDraftButton).toBeVisible();
});
@@ -184,3 +184,35 @@ test('Replace the trigger of an active version', async ({
'Create Record',
]);
});
test("Nodes can't be deleted by pressing Backspace or Delete keys", async ({
workflowVisualizer,
page,
}) => {
await workflowVisualizer.triggerNode.click();
await page.keyboard.press('Backspace');
await page.keyboard.press('Delete');
await expect(workflowVisualizer.triggerNode).toBeVisible();
const { createdStepId: firstStepId } =
await workflowVisualizer.createStep('create-record');
const firstStep = workflowVisualizer.getStepNode(firstStepId);
await firstStep.click();
await expect(workflowVisualizer.getDeleteNodeButton(firstStep)).toBeVisible();
await page.keyboard.press('Backspace');
await page.keyboard.press('Delete');
await expect(firstStep).toBeVisible();
await workflowVisualizer.addStepButton.click();
await page.keyboard.press('Backspace');
await page.keyboard.press('Delete');
await expect(workflowVisualizer.addStepButton).toBeVisible();
});
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"experimental": {
"plugins": [
[
"@lingui/swc-plugin",
{
"runtimeModules": {
"i18n": ["@lingui/core", "i18n"],
"trans": ["@lingui/react", "Trans"]
},
"stripNonEssentialFields": false
}
]
]
}
}
}
+24
View File
@@ -0,0 +1,24 @@
import { defineConfig } from '@lingui/cli';
import { formatter } from '@lingui/format-po';
import { APP_LOCALES } from 'twenty-shared';
export default defineConfig({
sourceLocale: 'en',
locales: Object.values(APP_LOCALES),
pseudoLocale: 'pseudo-en',
fallbackLocales: {
'pseudo-en': 'en',
},
extractorParserOptions: {
tsExperimentalDecorators: true,
},
catalogs: [
{
path: '<rootDir>/src/locales/{locale}',
include: ['src'],
},
],
catalogsMergePath: '<rootDir>/src/locales/generated/{locale}',
compileNamespace: 'ts',
format: formatter({ lineNumbers: false }),
});
+6 -2
View File
@@ -1,6 +1,6 @@
{
"name": "twenty-emails",
"version": "0.41.1",
"version": "0.42.18",
"description": "",
"author": "",
"private": true,
@@ -9,10 +9,14 @@
"scripts": {
"build": "npx vite build"
},
"dependencies": {
"twenty-shared": "workspace:*"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"engines": {
+16 -1
View File
@@ -8,7 +8,8 @@
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "{projectRoot}/dist"
}
},
"dependsOn": ["^build"]
},
"typecheck": {},
"lint": {
@@ -30,6 +31,20 @@
"configurations": {
"fix": {}
}
},
"lingui:extract": {
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"command": "lingui extract --overwrite"
}
},
"lingui:compile": {
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"command": "lingui compile --typescript"
}
}
}
}
@@ -1,21 +1,102 @@
import { PropsWithChildren } from 'react';
import { i18n, Messages } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { Container, Html } from '@react-email/components';
import { PropsWithChildren } from 'react';
import { BaseHead } from 'src/components/BaseHead';
import { Logo } from 'src/components/Logo';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared';
import { messages as afMessages } from '../locales/generated/af-ZA';
import { messages as arMessages } from '../locales/generated/ar-SA';
import { messages as caMessages } from '../locales/generated/ca-ES';
import { messages as csMessages } from '../locales/generated/cs-CZ';
import { messages as daMessages } from '../locales/generated/da-DK';
import { messages as deMessages } from '../locales/generated/de-DE';
import { messages as elMessages } from '../locales/generated/el-GR';
import { messages as enMessages } from '../locales/generated/en';
import { messages as esMessages } from '../locales/generated/es-ES';
import { messages as fiMessages } from '../locales/generated/fi-FI';
import { messages as frMessages } from '../locales/generated/fr-FR';
import { messages as heMessages } from '../locales/generated/he-IL';
import { messages as huMessages } from '../locales/generated/hu-HU';
import { messages as itMessages } from '../locales/generated/it-IT';
import { messages as jaMessages } from '../locales/generated/ja-JP';
import { messages as koMessages } from '../locales/generated/ko-KR';
import { messages as nlMessages } from '../locales/generated/nl-NL';
import { messages as noMessages } from '../locales/generated/no-NO';
import { messages as plMessages } from '../locales/generated/pl-PL';
import { messages as pseudoEnMessages } from '../locales/generated/pseudo-en';
import { messages as ptBRMessages } from '../locales/generated/pt-BR';
import { messages as ptPTMessages } from '../locales/generated/pt-PT';
import { messages as roMessages } from '../locales/generated/ro-RO';
import { messages as ruMessages } from '../locales/generated/ru-RU';
import { messages as srMessages } from '../locales/generated/sr-Cyrl';
import { messages as svMessages } from '../locales/generated/sv-SE';
import { messages as trMessages } from '../locales/generated/tr-TR';
import { messages as ukMessages } from '../locales/generated/uk-UA';
import { messages as viMessages } from '../locales/generated/vi-VN';
import { messages as zhHansMessages } from '../locales/generated/zh-CN';
import { messages as zhHantMessages } from '../locales/generated/zh-TW';
type BaseEmailProps = PropsWithChildren<{
width?: number;
locale: keyof typeof APP_LOCALES;
}>;
export const BaseEmail = ({ children, width }: BaseEmailProps) => {
const messages: Record<keyof typeof APP_LOCALES, Messages> = {
en: enMessages,
'pseudo-en': pseudoEnMessages,
'af-ZA': afMessages,
'ar-SA': arMessages,
'ca-ES': caMessages,
'cs-CZ': csMessages,
'da-DK': daMessages,
'de-DE': deMessages,
'el-GR': elMessages,
'es-ES': esMessages,
'fi-FI': fiMessages,
'fr-FR': frMessages,
'he-IL': heMessages,
'hu-HU': huMessages,
'it-IT': itMessages,
'ja-JP': jaMessages,
'ko-KR': koMessages,
'nl-NL': nlMessages,
'no-NO': noMessages,
'pl-PL': plMessages,
'pt-BR': ptBRMessages,
'pt-PT': ptPTMessages,
'ro-RO': roMessages,
'ru-RU': ruMessages,
'sr-Cyrl': srMessages,
'sv-SE': svMessages,
'tr-TR': trMessages,
'uk-UA': ukMessages,
'vi-VN': viMessages,
'zh-CN': zhHansMessages,
'zh-TW': zhHantMessages,
};
(Object.entries(messages) as [keyof typeof APP_LOCALES, any][]).forEach(
([locale, message]) => {
i18n.load(locale, message);
},
);
i18n.activate(SOURCE_LOCALE);
export const BaseEmail = ({ children, width, locale }: BaseEmailProps) => {
i18n.activate(locale);
return (
<Html lang="en">
<BaseHead />
<Container width={width || 290}>
<Logo />
{children}
</Container>
</Html>
<I18nProvider i18n={i18n}>
<Html lang={locale}>
<BaseHead />
<Container width={width || 290}>
<Logo />
{children}
</Container>
</Html>
</I18nProvider>
);
};
@@ -1,48 +0,0 @@
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { HighlightedText } from 'src/components/HighlightedText';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
type CleanInactiveWorkspaceEmailProps = {
daysLeft: number;
userName: string;
workspaceDisplayName: string;
};
export const CleanInactiveWorkspaceEmail = ({
daysLeft,
userName,
workspaceDisplayName,
}: CleanInactiveWorkspaceEmailProps) => {
const dayOrDays = daysLeft > 1 ? 'days' : 'day';
const remainingDays = daysLeft > 1 ? `${daysLeft} ` : '';
const helloString = userName?.length > 1 ? `Hello ${userName}` : 'Hello';
return (
<BaseEmail>
<Title value="Inactive Workspace 😴" />
<HighlightedText value={`${daysLeft} ${dayOrDays} left`} />
<MainText>
{helloString},
<br />
<br />
It appears that there has been a period of inactivity on your{' '}
<b>{workspaceDisplayName}</b> workspace.
<br />
<br />
Please note that the account is due for deactivation soon, and all
associated data within this workspace will be deleted.
<br />
<br />
No need for concern, though! Simply create or edit a record within the
next {remainingDays}
{dayOrDays} to retain access.
</MainText>
<CallToAction href="https://app.twenty.com" value="Connect to Twenty" />
</BaseEmail>
);
};
export default CleanInactiveWorkspaceEmail;
@@ -0,0 +1,46 @@
import { Trans } from '@lingui/react/macro';
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
type CleanSuspendedWorkspaceEmailProps = {
daysSinceInactive: number;
userName: string;
workspaceDisplayName: string | undefined;
};
export const CleanSuspendedWorkspaceEmail = ({
daysSinceInactive,
userName,
workspaceDisplayName,
}: CleanSuspendedWorkspaceEmailProps) => {
const helloString = userName?.length > 1 ? `Hello ${userName}` : 'Hello';
return (
<BaseEmail width={333} locale="en">
<Title value="Deleted Workspace 🥺" />
<MainText>
{helloString},
<br />
<br />
<Trans>
Your workspace <b>{workspaceDisplayName}</b> has been deleted as your
subscription expired {daysSinceInactive} days ago.
</Trans>
<br />
<br />
<Trans>All data in this workspace has been permanently deleted.</Trans>
<br />
<br />
<Trans>
If you wish to use Twenty again, you can create a new workspace.
</Trans>
</MainText>
<CallToAction
href="https://app.twenty.com/"
value="Create a new workspace"
/>
</BaseEmail>
);
};
@@ -1,40 +0,0 @@
import { Column, Row, Section } from '@react-email/components';
import { BaseEmail } from 'src/components/BaseEmail';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
type DeleteInactiveWorkspaceEmailData = {
daysSinceInactive: number;
workspaceId: string;
};
export const DeleteInactiveWorkspaceEmail = (
workspacesToDelete: DeleteInactiveWorkspaceEmailData[],
) => {
const minDaysSinceInactive = Math.min(
...workspacesToDelete.map(
(workspaceToDelete) => workspaceToDelete.daysSinceInactive,
),
);
return (
<BaseEmail width={350}>
<Title value="Dead Workspaces 😵 that should be deleted" />
<MainText>
List of <b>workspaceIds</b> inactive since at least{' '}
<b>{minDaysSinceInactive} days</b>:
<Section>
{workspacesToDelete.map((workspaceToDelete) => {
return (
<Row key={workspaceToDelete.workspaceId}>
<Column>{workspaceToDelete.workspaceId}</Column>
</Row>
);
})}
</Section>
</MainText>
</BaseEmail>
);
};
export default DeleteInactiveWorkspaceEmail;
@@ -1,25 +1,32 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { Link } from 'src/components/Link';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
import { APP_LOCALES } from 'twenty-shared';
type PasswordResetLinkEmailProps = {
duration: string;
link: string;
locale: keyof typeof APP_LOCALES;
};
export const PasswordResetLinkEmail = ({
duration,
link,
locale,
}: PasswordResetLinkEmailProps) => {
return (
<BaseEmail>
<Title value="Reset your password 🗝" />
<CallToAction href={link} value="Reset" />
<BaseEmail locale={locale}>
<Title value={t`Reset your password 🗝`} />
<CallToAction href={link} value={t`Reset`} />
<MainText>
This link is only valid for the next {duration}. If link does not work,
you can use the login verification link directly:
<Trans>
This link is only valid for the next {duration}. If the link does not
work, you can use the login verification link directly:
</Trans>
<br />
<Link href={link} value={link} />
</MainText>
@@ -1,38 +1,49 @@
import { format } from 'date-fns';
import { i18n } from '@lingui/core';
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
import { APP_LOCALES } from 'twenty-shared';
type PasswordUpdateNotifyEmailProps = {
userName: string;
email: string;
link: string;
locale: keyof typeof APP_LOCALES;
};
export const PasswordUpdateNotifyEmail = ({
userName,
email,
link,
locale,
}: PasswordUpdateNotifyEmailProps) => {
const helloString = userName?.length > 1 ? `Dear ${userName}` : 'Dear';
const helloString = userName?.length > 1 ? t`Dear ${userName}` : t`Hello`;
const formattedDate = i18n.date(new Date());
return (
<BaseEmail>
<Title value="Password updated" />
<BaseEmail locale={locale}>
<Title value={t`Password updated`} />
<MainText>
{helloString},
<br />
<br />
This is a confirmation that password for your account ({email}) was
successfully changed on {format(new Date(), 'MMMM d, yyyy')}.
<Trans>
This is a confirmation that password for your account ({email}) was
successfully changed on {formattedDate}.
</Trans>
<br />
<br />
If you did not initiate this change, please contact your workspace owner
immediately.
<Trans>
If you did not initiate this change, please contact your workspace
owner immediately.
</Trans>
<br />
</MainText>
<CallToAction value="Connect to Twenty" href={link} />
<CallToAction value={t`Connect to Twenty`} href={link} />
</BaseEmail>
);
};
@@ -1,26 +1,34 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { Footer } from 'src/components/Footer';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
import { APP_LOCALES } from 'twenty-shared';
type SendEmailVerificationLinkEmailProps = {
link: string;
locale: keyof typeof APP_LOCALES;
};
export const SendEmailVerificationLinkEmail = ({
link,
locale,
}: SendEmailVerificationLinkEmailProps) => {
return (
<BaseEmail width={333}>
<Title value="Confirm your email address" />
<CallToAction href={link} value="Verify Email" />
<BaseEmail width={333} locale={locale}>
<Title value={t`Confirm your email address`} />
<CallToAction href={link} value={t`Verify Email`} />
<br />
<br />
<MainText>
Thanks for registering for an account on Twenty! Before we get started,
we just need to confirm that this is you. Click above to verify your
email address.
<Trans>
Thanks for registering for an account on Twenty! Before we get
started, we just need to confirm that this is you. Click above to
verify your email address.
</Trans>
</MainText>
<Footer />
</BaseEmail>
@@ -1,3 +1,5 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { Img } from '@react-email/components';
import { emailTheme } from 'src/common-style';
@@ -10,7 +12,7 @@ import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
import { WhatIsTwenty } from 'src/components/WhatIsTwenty';
import { capitalize } from 'src/utils/capitalize';
import { getImageAbsoluteURI } from 'twenty-shared';
import { APP_LOCALES, getImageAbsoluteURI } from 'twenty-shared';
type SendInviteLinkEmailProps = {
link: string;
@@ -21,6 +23,7 @@ type SendInviteLinkEmailProps = {
lastName: string;
};
serverUrl: string;
locale: keyof typeof APP_LOCALES;
};
export const SendInviteLinkEmail = ({
@@ -28,14 +31,15 @@ export const SendInviteLinkEmail = ({
workspace,
sender,
serverUrl,
locale,
}: SendInviteLinkEmailProps) => {
const workspaceLogo = workspace.logo
? getImageAbsoluteURI({ imageUrl: workspace.logo, baseUrl: serverUrl })
: null;
return (
<BaseEmail width={333}>
<Title value="Join your team on Twenty" />
<BaseEmail width={333} locale={locale}>
<Title value={t`Join your team on Twenty`} />
<MainText>
{capitalize(sender.firstName)} (
<Link
@@ -43,13 +47,14 @@ export const SendInviteLinkEmail = ({
value={sender.email}
color={emailTheme.font.colors.blue}
/>
) has invited you to join a workspace called <b>{workspace.name}</b>
)<Trans>has invited you to join a workspace called </Trans>
<b>{workspace.name}</b>
<br />
</MainText>
<HighlightedContainer>
{workspaceLogo && <Img src={workspaceLogo} width={40} height={40} />}
{workspace.name && <HighlightedText value={workspace.name} />}
<CallToAction href={link} value="Accept invite" />
<CallToAction href={link} value={t`Accept invite`} />
</HighlightedContainer>
<WhatIsTwenty />
</BaseEmail>
@@ -0,0 +1,67 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { Img } from '@react-email/components';
import { emailTheme } from 'src/common-style';
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { HighlightedContainer } from 'src/components/HighlightedContainer';
import { HighlightedText } from 'src/components/HighlightedText';
import { Link } from 'src/components/Link';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
import { WhatIsTwenty } from 'src/components/WhatIsTwenty';
import { capitalize } from 'src/utils/capitalize';
import { APP_LOCALES, getImageAbsoluteURI } from 'twenty-shared';
type SendApprovedAccessDomainValidationProps = {
link: string;
domain: string;
workspace: { name: string | undefined; logo: string | undefined };
sender: {
email: string;
firstName: string;
lastName: string;
};
serverUrl: string;
locale: keyof typeof APP_LOCALES;
};
export const SendApprovedAccessDomainValidation = ({
link,
domain,
workspace,
sender,
serverUrl,
locale,
}: SendApprovedAccessDomainValidationProps) => {
const workspaceLogo = workspace.logo
? getImageAbsoluteURI({ imageUrl: workspace.logo, baseUrl: serverUrl })
: null;
return (
<BaseEmail width={333} locale={locale}>
<Title value={t`Validate domain`} />
<MainText>
{capitalize(sender.firstName)} (
<Link
href={`mailto:${sender.email}`}
value={sender.email}
color={emailTheme.font.colors.blue}
/>
)
<Trans>
Please validate this domain to allow users with <b>@{domain}</b> email
addresses to join your workspace without requiring an invitation.
</Trans>
<br />
</MainText>
<HighlightedContainer>
{workspaceLogo && <Img src={workspaceLogo} width={40} height={40} />}
{workspace.name && <HighlightedText value={workspace.name} />}
<CallToAction href={link} value={t`Validate domain`} />
</HighlightedContainer>
<WhatIsTwenty />
</BaseEmail>
);
};
@@ -0,0 +1,57 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { BaseEmail } from 'src/components/BaseEmail';
import { CallToAction } from 'src/components/CallToAction';
import { MainText } from 'src/components/MainText';
import { Title } from 'src/components/Title';
type WarnSuspendedWorkspaceEmailProps = {
daysSinceInactive: number;
inactiveDaysBeforeDelete: number;
userName: string;
workspaceDisplayName: string | undefined;
};
export const WarnSuspendedWorkspaceEmail = ({
daysSinceInactive,
inactiveDaysBeforeDelete,
userName,
workspaceDisplayName,
}: WarnSuspendedWorkspaceEmailProps) => {
const daysLeft = inactiveDaysBeforeDelete - daysSinceInactive;
const dayOrDays = daysLeft > 1 ? 'days' : 'day';
const remainingDays = daysLeft > 0 ? `${daysLeft} ` : '';
const helloString = userName?.length > 1 ? `Hello ${userName}` : 'Hello';
return (
<BaseEmail width={333} locale="en">
<Title value="Suspended Workspace 😴" />
<MainText>
{helloString},
<br />
<br />
<Trans>
It appears that your workspace <b>{workspaceDisplayName}</b> has been
suspended for {daysSinceInactive} days.
</Trans>
<br />
<br />
<Trans>
The workspace will be deactivated in {remainingDays} {dayOrDays}, and
all its data will be deleted.
</Trans>
<br />
<br />
<Trans>
If you wish to continue using Twenty, please update your subscription
within the next {remainingDays} {dayOrDays}.
</Trans>
</MainText>
<CallToAction
href="https://app.twenty.com/settings/billing"
value={t`Update your subscription`}
/>
</BaseEmail>
);
};
+3 -2
View File
@@ -1,6 +1,7 @@
export * from './emails/clean-inactive-workspaces.email';
export * from './emails/delete-inactive-workspaces.email';
export * from './emails/clean-suspended-workspace.email';
export * from './emails/password-reset-link.email';
export * from './emails/password-update-notify.email';
export * from './emails/send-email-verification-link.email';
export * from './emails/send-invite-link.email';
export * from './emails/warn-suspended-workspace.email';
export * from './emails/validate-approved-access-domain.email';
+124
View File
@@ -0,0 +1,124 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: aa\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Afar\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: aa\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "crwdns1:0crwdne1:0"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "crwdns3:0crwdne3:0"
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "crwdns5:0crwdne5:0"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "crwdns7:0crwdne7:0"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "crwdns9:0{userName}crwdne9:0"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "crwdns11:0crwdne11:0"
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "crwdns13:0crwdne13:0"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "crwdns15:0crwdne15:0"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "crwdns17:0{remainingDays}crwdnd17:0{dayOrDays}crwdne17:0"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "crwdns19:0crwdne19:0"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "crwdns21:0{workspaceDisplayName}crwdnd21:0{daysSinceInactive}crwdne21:0"
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "crwdns23:0crwdne23:0"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "crwdns25:0crwdne25:0"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "crwdns27:0crwdne27:0"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "crwdns29:0crwdne29:0"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "crwdns31:0crwdne31:0"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "crwdns33:0{remainingDays}crwdnd33:0{dayOrDays}crwdne33:0"
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "crwdns35:0{email}crwdnd35:0{formattedDate}crwdne35:0"
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "crwdns37:0{duration}crwdne37:0"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "crwdns39:0crwdne39:0"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "crwdns41:0crwdne41:0"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "crwdns4845:0{workspaceDisplayName}crwdnd4845:0{daysSinceInactive}crwdne4845:0"
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: af\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Afrikaans\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: af\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Aanvaar uitnodiging"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Alle data in hierdie werkruimte is permanent verwyder."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Bevestig jou e-posadres"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Konnekteer met Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Liewe {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "het jou genooi om aan te sluit by 'n werkruimte genaamd "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hallo"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "As jy nie hierdie verandering geïnisieer het nie, kontak asseblief jou werkruimte-eienaar dadelik."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "As jy wil voortgaan om Twenty te gebruik, moet asseblief jou intekening binne die volgende {remainingDays} {dayOrDays} opdateer."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "As jy Twenty weer wil gebruik, kan jy 'n nuwe werkruimte skep."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Dit blyk dat jou werkruimte <0>{workspaceDisplayName}</0> vir {daysSinceInactive} dae opgeskort is."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Sluit aan by jou span op Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Wagwoord opgedateer"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Stel terug"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Stel jou wagwoord terug 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Dankie dat jy registreer het vir 'n rekening op Twenty! Voordat ons begin, moet ons net bevestig dat dit jy is. Klik bo om jou e-posadres te verifieer."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "Die werkruimte sal gedeaktiveer word in {remainingDays} {dayOrDays}, en al sy data sal verwyder word."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Dit is 'n bevestiging dat die wagwoord vir jou rekening ({email}) suksesvol verander is op {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Hierdie skakel is slegs geldig vir die volgende {duration}. Indien die skakel nie werk nie, kan jy die aanmeldverifiëringskakel direk gebruik:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Opdateer jou intekening"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verifieer E-pos"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Jou werkruimte <0>{workspaceDisplayName}</0> is verwyder aangesien jou intekening {daysSinceInactive} dae gelede verval het."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: ar\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "قبول الدعوة"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "تم حذف جميع البيانات في هذه الواجهة بشكل دائم."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "تأكد من عنوان بريدك الإلكتروني"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "الاتصال بـ Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "عزيزي {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "لقد دُعيت للإنضمام إلى مساحة عمل تسمى "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "مرحبًا"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "إذا لم تكن قد بدأت هذا التغيير، يرجى الاتصال بمالك مساحة العمل الخاصة بك على الفور."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "إذا كنت ترغب في الاستمرار في استخدام Twenty، يُرجى تحديث اشتراكك خلال الأيام {remainingDays} {dayOrDays} القادمة."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "إذا كنت ترغب في استخدام Twenty مرة أخرى، يمكنك إنشاء مساحة عمل جديدة."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "يبدو أن مساحة عملك <0>{workspaceDisplayName}</0> قد تم تعليقها لمدة {daysSinceInactive} أيام."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "انضم إلى فريقك في Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "تم تحديث كلمة المرور"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "إعادة تعيين"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "إعادة تعيين كلمة مرورك 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "شكراً لتسجيلك لحساب على Twenty! قبل أن نبدأ، نحتاج فقط إلى التأكد من أن هذا أنت. انقر أعلاه للتحقق من عنوان بريدك الإلكتروني."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "سيتم تعطيل مساحة العمل في غضون {remainingDays} {dayOrDays}، وسيتم حذف كل بياناتها."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "هذا تأكيد أن كلمة مرور حسابك ({email}) قد تم تغييرها بنجاح في {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "هذا الرابط صالح فقط للمدة {duration} القادمة. إذا لم يعمل الرابط، يمكنك استخدام رابط التحقق من تسجيل الدخول مباشرة:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "تحديث الاشتراك"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "تحقق من البريد الإلكتروني"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "تم حذف مساحة العمل <0>{workspaceDisplayName}</0> كون اشتراكك قد انتهى منذ {daysSinceInactive} أيام."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: ca\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Catalan\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: ca\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accepta la invitació"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Totes les dades en aquest espai de treball s'han esborrat permanentment."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirma la teva adreça de correu electrònic"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Connecta't a Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Benvolgut {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "t'ha convidat a unir-te a un espai de treball anomenat "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hola"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Si no has iniciat aquest canvi, si us plau contacta amb el propietari de l'espai de treball immediatament."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Si vols continuar utilitzant Twenty, si us plau actualitza la teva subscripció en els propers {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Si vols utilitzar Twenty de nou, pots crear un nou espai de treball."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Sembla que el teu espai de treball <0>{workspaceDisplayName}</0> ha estat suspès per {daysSinceInactive} dies."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Uneix-te al teu equip a Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Contrasenya actualitzada"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Restableix"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Restableix la teva contrasenya 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Gràcies per registrar-te a Twenty! Abans de començar, només necessitem confirmar que ets tu. Clica a sobre per verificar la teva adreça de correu electrònic."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "L'espai de treball es desactivarà en {remainingDays} {dayOrDays}, i totes les seves dades seran eliminades."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Això és una confirmació que la contrasenya del teu compte ({email}) s'ha canviat amb èxit el {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Aquest enllaç només és vàlid durant els propers {duration}. Si l'enllaç no funciona, pots fer servir directament l'enllaç de verificació d'inici de sessió:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Actualitza la teva subscripció"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verifica el correu electrònic"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "El teu espai de treball <0>{workspaceDisplayName}</0> s'ha eliminat ja que la teva subscripció va caducar fa {daysSinceInactive} dies."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: cs\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: cs\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Přijměte pozvánku"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Všechna data v tomto pracovním prostoru byla trvale odstraněna."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Potvrďte svou e-mailovou adresu"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Připojte se k Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Vážený {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "vás pozval do pracovního prostoru s názvem "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Dobrý den"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Pokud jste tuto změnu neiniciovali, prosím, okamžitě kontaktujte majitele vašeho pracovního prostoru."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Pokud si přejete dále používat Twenty, prosím, aktualizujte své předplatné během příštích {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Pokud si přejete znovu použít Twenty, můžete vytvořit nový pracovní prostor."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Zdá se, že váš pracovní prostor <0>{workspaceDisplayName}</0> byl pozastaven na dobu {daysSinceInactive} dní."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Připojte se k svému týmu na Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Heslo bylo aktualizováno"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Obnovit"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Obnovte své heslo 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Děkujeme za registraci účtu na Twenty! Než začneme, musíme potvrdit, že jste to vy. Klikněte výše k ověření vaší e-mailové adresy."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "Pracovní prostor bude deaktivován za {remainingDays} {dayOrDays} a všechna jeho data budou smazána."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Toto je potvrzení, že heslo k vašemu účtu ({email}) bylo úspěšně změněno {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Tento odkaz je platný pouze následující {duration}. Pokud odkaz nefunguje, můžete přímo použít odkaz pro ověření přihlášení:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Aktualizujte své předplatné"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Ověřit e-mail"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Váš pracovní prostor <0>{workspaceDisplayName}</0> byl odstraněn, protože vaše předplatné vypršelo před {daysSinceInactive} dny."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: da\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Danish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: da\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accepter invitation"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Alle data i dette arbejdsområde er blevet permanent slettet."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Bekræft din e-mailadresse"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Forbind til Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Kære {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "har inviteret dig til at deltage i et arbejdsområde kaldet "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hej"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Hvis du ikke har initieret denne ændring, bedes du straks kontakte ejeren af dit arbejdsområde."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Hvis du ønsker at fortsætte med at bruge Twenty, opdater da dit abonnement inden for de næste {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Hvis du ønsker at bruge Twenty igen, kan du oprette et nyt arbejdsområde."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Det ser ud til, at dit arbejdsområde <0>{workspaceDisplayName}</0> er blevet suspenderet i {daysSinceInactive} dage."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Deltag i dit team på Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Adgangskode opdateret"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Nulstil"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Nulstil din adgangskode 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Tak fordi du har registreret en konto på Twenty! Før vi starter, skal vi bare bekræfte, at det er dig. Klik ovenfor for at bekræfte din e-mailadresse."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "Arbejdsområdet vil blive deaktiveret om {remainingDays} {dayOrDays}, og alle dens data vil blive slettet."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Dette er en bekræftelse på, at adgangskoden til din konto ({email}) er blevet ændret den {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Dette link er kun gyldigt i de næste {duration}. Hvis linket ikke fungerer, kan du bruge loginbekræftelseslinket direkte:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Opdater dit abonnement"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Bekræft e-mail"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Dit arbejdsområde <0>{workspaceDisplayName}</0> er blevet slettet, da dit abonnement udløb for {daysSinceInactive} dage siden."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: de\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Einladung akzeptieren"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Alle Daten in diesem Workspace wurden dauerhaft gelöscht."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Bestätigen Sie Ihre E-Mail-Adresse"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Mit Twenty verbinden"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Sehr geehrte/r {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "hat Sie eingeladen, einem Workspace namens "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hallo"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Wenn Sie diese Änderung nicht veranlasst haben, kontaktieren Sie bitte umgehend den Eigentümer Ihres Workspaces."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Wenn Sie Twenty weiterhin nutzen möchten, aktualisieren Sie bitte Ihr Abonnement innerhalb der nächsten {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Wenn Sie Twenty erneut nutzen möchten, können Sie einen neuen Workspace erstellen."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Es scheint, dass Ihr Workspace <0>{workspaceDisplayName}</0> seit {daysSinceInactive} Tagen gesperrt ist."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Treten Sie Ihrem Team auf Twenty bei"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Passwort wurde aktualisiert"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Zurücksetzen"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Setzen Sie Ihr Passwort zurück 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Vielen Dank für Ihre Registrierung bei Twenty! Bevor wir beginnen, müssen wir nur bestätigen, dass Sie es sind. Klicken Sie oben, um Ihre E-Mail-Adresse zu verifizieren."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "Der Workspace wird in {remainingDays} {dayOrDays} deaktiviert, und alle Daten werden gelöscht."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Dies ist eine Bestätigung, dass das Passwort für Ihr Konto ({email}) am {formattedDate} erfolgreich geändert wurde."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Dieser Link ist nur für die nächsten {duration} gültig. Wenn der Link nicht funktioniert, können Sie den Anmeldebestätigungslink direkt verwenden:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Aktualisieren Sie Ihr Abonnement"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "E-Mail verifizieren"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Ihr Workspace <0>{workspaceDisplayName}</0> wurde gelöscht, da Ihr Abonnement vor {daysSinceInactive} Tagen abgelaufen ist."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: el\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Greek\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: el\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Αποδοχή πρόσκλησης"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Όλα τα δεδομένα σε αυτόν τον χώρο εργασίας έχουν διαγραφεί μόνιμα."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Επιβεβαιώστε τη διεύθυνση email σας"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Συνδεθείτε στο Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Αγαπητέ/ή {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "σας έχει προσκαλέσει να συμμετάσχετε σε έναν χώρο εργασίας με την ονομασία "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Γεια σας"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Εάν δεν έχετε προβεί εσείς σε αυτήν την αλλαγή, παρακαλώ επικοινωνήστε άμεσα με τον ιδιοκτήτη του χώρου εργασίας σας."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Εάν επιθυμείτε να συνεχίστε να χρησιμοποιείτε το Twenty, παρακαλούμε ενημερώστε την συνδρομή σας εντός των επόμενων {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Εάν επιθυμείτε να χρησιμοποιήσετε ξανά το Twenty, μπορείτε να δημιουργήσετε ένα νέο χώρο εργασίας."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Φαίνεται ότι ο χώρος εργασίας σας <0>{workspaceDisplayName}</0> έχει ανασταλεί για {daysSinceInactive} ημέρες."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Συμμετέχετε στην ομάδα σας στο Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Ο κωδικός έχει ενημερωθεί"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Επαναφορά"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Επαναφέρετε τον κωδικό σας 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Σας ευχαριστούμε που εγγραφήκατε για έναν λογαριασμό στο Twenty! Πριν ξεκινήσουμε, πρέπει μόνο να επιβεβαιώσουμε ότι αυτός είστε εσείς. Κάντε κλικ παραπάνω για να επαληθεύσετε τη διεύθυνση email σας."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "Ο χώρος εργασίας θα απενεργοποιηθεί σε {remainingDays} {dayOrDays}, και όλα τα δεδομένα του θα διαγραφούν."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Αυτό είναι ένα επιβεβαίωση ότι ο κωδικός για τον λογαριασμό σας ({email}) άλλαξε με επιτυχία στις {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Αυτός ο σύνδεσμος ισχύει μόνο για τις επόμενες {duration}. Εάν ο σύνδεσμος δεν λειτουργεί, μπορείτε να χρησιμοποιήσετε τον σύνδεσμο επαλήθευσης σύνδεσης απευθείας:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Ενημερώστε τη συνδρομή σας"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Επαληθεύστε το Email"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Ο χώρος εργασίας σας <0>{workspaceDisplayName}</0> έχει διαγραφεί καθώς η συνδρομή σας έληξε {daysSinceInactive} ημέρες πριν."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+118
View File
@@ -0,0 +1,118 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accept invite"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "All data in this workspace has been permanently deleted."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirm your email address"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Connect to Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Dear {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "has invited you to join a workspace called "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hello"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "If you wish to use Twenty again, you can create a new workspace."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Join your team on Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Password updated"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Reset"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Reset your password 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Update your subscription"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verify Email"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: es\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: es\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Aceptar invitación"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Todos los datos de este espacio de trabajo han sido eliminados permanentemente."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirma tu dirección de correo electrónico"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Conéctate a Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Estimado/a {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "te ha invitado a unirte a un espacio de trabajo llamado "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hola"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Si no iniciaste este cambio, contacta inmediatamente al propietario de tu espacio de trabajo."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Si deseas seguir usando Twenty, actualiza tu suscripción en los próximos {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Si deseas usar Twenty nuevamente, puedes crear un nuevo espacio de trabajo."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Parece que tu espacio de trabajo <0>{workspaceDisplayName}</0> ha estado suspendido durante {daysSinceInactive} días."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Únete a tu equipo en Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Contraseña actualizada"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Restablecer"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Restablece tu contraseña 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "¡Gracias por registrarte en Twenty! Antes de comenzar, solo necesitamos confirmar que eres tú. Haz clic arriba para verificar tu dirección de correo electrónico."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "El espacio de trabajo se desactivará en {remainingDays} {dayOrDays} y se eliminarán todos sus datos."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Esta es una confirmación de que la contraseña de tu cuenta ({email}) se cambió correctamente el {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Este enlace solo es válido por los próximos {duration}. Si el enlace no funciona, puedes usar directamente el enlace de verificación de inicio de sesión:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Actualiza tu suscripción"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verificar correo electrónico"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Tu espacio de trabajo <0>{workspaceDisplayName}</0> ha sido eliminado porque tu suscripción caducó hace {daysSinceInactive} días."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: fi\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: Finnish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: fi\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Hyväksy kutsu"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Kaikki tiedot tässä työtilassa on pysyvästi poistettu."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Vahvista sähköpostiosoitteesi"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Yhdistä Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Hyvä {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "on kutsunut sinut liittymään työtilaan nimeltä "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hei"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Jos et itse aloittanut tätä muutosta, ota heti yhteyttä työtilasi omistajaan."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Jos haluat jatkaa Twenty:n käyttöä, päivitä tilauksesi seuraavan {remainingDays} {dayOrDays} kuluessa."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Jos haluat käyttää Twentyä uudelleen, voit luoda uuden työtilan."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Vaikuttaa siltä, että työtilasi <0>{workspaceDisplayName}</0> on ollut jäädytettynä {daysSinceInactive} päivää."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Liity tiimiisi Twentyssä"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Salasana päivitetty"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Nollaa"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Nollaa salasanasi 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Kiitos, että rekisteröidyit Twenty:n käyttäjäksi! Ennen kuin aloitamme, meidän täytyy vahvistaa, että kyseessä on sinä. Klikkaa yllä vahvistaaksesi sähköpostiosoitteesi."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "Työtila poistetaan käytöstä {remainingDays} {dayOrDays} kuluttua, ja kaikki sen tiedot poistetaan."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Tämä on vahvistus siitä, että tilisi ({email}) salasana on vaihdettu onnistuneesti {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Tämä linkki on voimassa vain seuraavat {duration}. Jos linkki ei toimi, voit käyttää suoraan kirjautumisen varmennuslinkkiä:"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Päivitä tilauksesi"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Vahvista sähköposti"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Työtilasi <0>{workspaceDisplayName}</0> on poistettu, koska tilauksesi vanheni {daysSinceInactive} päivää sitten."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
+123
View File
@@ -0,0 +1,123 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-02-01 18:53+0100\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: fr\n"
"Project-Id-Version: cf448e737e0d6d7b78742f963d761c61\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-01 00:00\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Crowdin-Project: cf448e737e0d6d7b78742f963d761c61\n"
"X-Crowdin-Project-ID: 1\n"
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accepter l'invitation"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Toutes les données de cet espace de travail ont été supprimées définitivement."
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirmez votre adresse e-mail"
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Connectez-vous à Twenty"
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
#~ msgstr "Dear"
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Cher {userName}"
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "vous a invité à rejoindre un espace de travail appelé "
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Bonjour"
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Si vous n'avez pas initié ce changement, veuillez contacter immédiatement le propriétaire de votre espace de travail."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
msgstr "Pour continuer à utiliser Twenty, veuillez mettre à jour votre abonnement dans les {remainingDays} {dayOrDays}."
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Pour réutiliser Twenty, vous pouvez créer un nouvel espace de travail."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr "Il semble que votre espace de travail <0>{workspaceDisplayName}</0> soit suspendu depuis {daysSinceInactive} jours."
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Rejoignez votre équipe sur Twenty"
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Mot de passe mis à jour"
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Réinitialiser"
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Réinitialisez votre mot de passe 🗝"
#: src/emails/send-email-verification-link.email.tsx
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address."
msgstr "Merci de vous être inscrit sur Twenty ! Avant de commencer, nous devons confirmer votre identité. Cliquez ci-dessus pour vérifier votre adresse e-mail."
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr "L'espace de travail sera désactivé dans {remainingDays} {dayOrDays} et toutes ses données seront supprimées."
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr "Ceci est une confirmation que le mot de passe de votre compte ({email}) a été modifié avec succès le {formattedDate}."
#: src/emails/password-update-notify.email.tsx
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}.<0/><1/>If you did not initiate this change, please contact your workspace owner immediately."
#: src/emails/password-reset-link.email.tsx
msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
msgstr "Ce lien n'est valable que pour les {duration} suivants. Si le lien ne fonctionne pas, vous pouvez utiliser directement le lien de vérification de connexion :"
#: src/emails/password-reset-link.email.tsx
#~ msgid "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#~ msgstr "This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:<0/>"
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Mettez à jour votre abonnement"
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Vérifiez l'e-mail"
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr "Votre espace de travail <0>{workspaceDisplayName}</0> a été supprimé car votre abonnement a expiré il y a {daysSinceInactive} jours."
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
#~ msgstr "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {inactiveDaysBeforeDelete} days ago."
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Aanvaar uitnodiging\"],\"Yxj+Uc\":[\"Alle data in hierdie werkruimte is permanent verwyder.\"],\"RPHFhC\":[\"Bevestig jou e-posadres\"],\"nvkBPN\":[\"Konnekteer met Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Liewe \",[\"userName\"]],\"tGme7M\":[\"het jou genooi om aan te sluit by 'n werkruimte genaamd \"],\"uzTaYi\":[\"Hallo\"],\"eE1nG1\":[\"As jy nie hierdie verandering geïnisieer het nie, kontak asseblief jou werkruimte-eienaar dadelik.\"],\"Gz91L8\":[\"As jy wil voortgaan om Twenty te gebruik, moet asseblief jou intekening binne die volgende \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" opdateer.\"],\"0weyko\":[\"As jy Twenty weer wil gebruik, kan jy 'n nuwe werkruimte skep.\"],\"7JuhZQ\":[\"Dit blyk dat jou werkruimte <0>\",[\"workspaceDisplayName\"],\"</0> vir \",[\"daysSinceInactive\"],\" dae opgeskort is.\"],\"PviVyk\":[\"Sluit aan by jou span op Twenty\"],\"ogtYkT\":[\"Wagwoord opgedateer\"],\"OfhWJH\":[\"Stel terug\"],\"RE5NiU\":[\"Stel jou wagwoord terug 🗝\"],\"7yDt8q\":[\"Dankie dat jy registreer het vir 'n rekening op Twenty! Voordat ons begin, moet ons net bevestig dat dit jy is. Klik bo om jou e-posadres te verifieer.\"],\"igorB1\":[\"Die werkruimte sal gedeaktiveer word in \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", en al sy data sal verwyder word.\"],\"7OEHy1\":[\"Dit is 'n bevestiging dat die wagwoord vir jou rekening (\",[\"email\"],\") suksesvol verander is op \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Hierdie skakel is slegs geldig vir die volgende \",[\"duration\"],\". Indien die skakel nie werk nie, kan jy die aanmeldverifiëringskakel direk gebruik:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Opdateer jou intekening\"],\"wCKkSr\":[\"Verifieer E-pos\"],\"9MqLGX\":[\"Jou werkruimte <0>\",[\"workspaceDisplayName\"],\"</0> is verwyder aangesien jou intekening \",[\"daysSinceInactive\"],\" dae gelede verval het.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"قبول الدعوة\"],\"Yxj+Uc\":[\"تم حذف جميع البيانات في هذه الواجهة بشكل دائم.\"],\"RPHFhC\":[\"تأكد من عنوان بريدك الإلكتروني\"],\"nvkBPN\":[\"الاتصال بـ Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"عزيزي \",[\"userName\"]],\"tGme7M\":[\"لقد دُعيت للإنضمام إلى مساحة عمل تسمى \"],\"uzTaYi\":[\"مرحبًا\"],\"eE1nG1\":[\"إذا لم تكن قد بدأت هذا التغيير، يرجى الاتصال بمالك مساحة العمل الخاصة بك على الفور.\"],\"Gz91L8\":[\"إذا كنت ترغب في الاستمرار في استخدام Twenty، يُرجى تحديث اشتراكك خلال الأيام \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" القادمة.\"],\"0weyko\":[\"إذا كنت ترغب في استخدام Twenty مرة أخرى، يمكنك إنشاء مساحة عمل جديدة.\"],\"7JuhZQ\":[\"يبدو أن مساحة عملك <0>\",[\"workspaceDisplayName\"],\"</0> قد تم تعليقها لمدة \",[\"daysSinceInactive\"],\" أيام.\"],\"PviVyk\":[\"انضم إلى فريقك في Twenty\"],\"ogtYkT\":[\"تم تحديث كلمة المرور\"],\"OfhWJH\":[\"إعادة تعيين\"],\"RE5NiU\":[\"إعادة تعيين كلمة مرورك 🗝\"],\"7yDt8q\":[\"شكراً لتسجيلك لحساب على Twenty! قبل أن نبدأ، نحتاج فقط إلى التأكد من أن هذا أنت. انقر أعلاه للتحقق من عنوان بريدك الإلكتروني.\"],\"igorB1\":[\"سيتم تعطيل مساحة العمل في غضون \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\"، وسيتم حذف كل بياناتها.\"],\"7OEHy1\":[\"هذا تأكيد أن كلمة مرور حسابك (\",[\"email\"],\") قد تم تغييرها بنجاح في \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"هذا الرابط صالح فقط للمدة \",[\"duration\"],\" القادمة. إذا لم يعمل الرابط، يمكنك استخدام رابط التحقق من تسجيل الدخول مباشرة:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"تحديث الاشتراك\"],\"wCKkSr\":[\"تحقق من البريد الإلكتروني\"],\"9MqLGX\":[\"تم حذف مساحة العمل <0>\",[\"workspaceDisplayName\"],\"</0> كون اشتراكك قد انتهى منذ \",[\"daysSinceInactive\"],\" أيام.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Accepta la invitació\"],\"Yxj+Uc\":[\"Totes les dades en aquest espai de treball s'han esborrat permanentment.\"],\"RPHFhC\":[\"Confirma la teva adreça de correu electrònic\"],\"nvkBPN\":[\"Connecta't a Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Benvolgut \",[\"userName\"]],\"tGme7M\":[\"t'ha convidat a unir-te a un espai de treball anomenat \"],\"uzTaYi\":[\"Hola\"],\"eE1nG1\":[\"Si no has iniciat aquest canvi, si us plau contacta amb el propietari de l'espai de treball immediatament.\"],\"Gz91L8\":[\"Si vols continuar utilitzant Twenty, si us plau actualitza la teva subscripció en els propers \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Si vols utilitzar Twenty de nou, pots crear un nou espai de treball.\"],\"7JuhZQ\":[\"Sembla que el teu espai de treball <0>\",[\"workspaceDisplayName\"],\"</0> ha estat suspès per \",[\"daysSinceInactive\"],\" dies.\"],\"PviVyk\":[\"Uneix-te al teu equip a Twenty\"],\"ogtYkT\":[\"Contrasenya actualitzada\"],\"OfhWJH\":[\"Restableix\"],\"RE5NiU\":[\"Restableix la teva contrasenya 🗝\"],\"7yDt8q\":[\"Gràcies per registrar-te a Twenty! Abans de començar, només necessitem confirmar que ets tu. Clica a sobre per verificar la teva adreça de correu electrònic.\"],\"igorB1\":[\"L'espai de treball es desactivarà en \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", i totes les seves dades seran eliminades.\"],\"7OEHy1\":[\"Això és una confirmació que la contrasenya del teu compte (\",[\"email\"],\") s'ha canviat amb èxit el \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Aquest enllaç només és vàlid durant els propers \",[\"duration\"],\". Si l'enllaç no funciona, pots fer servir directament l'enllaç de verificació d'inici de sessió:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Actualitza la teva subscripció\"],\"wCKkSr\":[\"Verifica el correu electrònic\"],\"9MqLGX\":[\"El teu espai de treball <0>\",[\"workspaceDisplayName\"],\"</0> s'ha eliminat ja que la teva subscripció va caducar fa \",[\"daysSinceInactive\"],\" dies.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Přijměte pozvánku\"],\"Yxj+Uc\":[\"Všechna data v tomto pracovním prostoru byla trvale odstraněna.\"],\"RPHFhC\":[\"Potvrďte svou e-mailovou adresu\"],\"nvkBPN\":[\"Připojte se k Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Vážený \",[\"userName\"]],\"tGme7M\":[\"vás pozval do pracovního prostoru s názvem \"],\"uzTaYi\":[\"Dobrý den\"],\"eE1nG1\":[\"Pokud jste tuto změnu neiniciovali, prosím, okamžitě kontaktujte majitele vašeho pracovního prostoru.\"],\"Gz91L8\":[\"Pokud si přejete dále používat Twenty, prosím, aktualizujte své předplatné během příštích \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Pokud si přejete znovu použít Twenty, můžete vytvořit nový pracovní prostor.\"],\"7JuhZQ\":[\"Zdá se, že váš pracovní prostor <0>\",[\"workspaceDisplayName\"],\"</0> byl pozastaven na dobu \",[\"daysSinceInactive\"],\" dní.\"],\"PviVyk\":[\"Připojte se k svému týmu na Twenty\"],\"ogtYkT\":[\"Heslo bylo aktualizováno\"],\"OfhWJH\":[\"Obnovit\"],\"RE5NiU\":[\"Obnovte své heslo 🗝\"],\"7yDt8q\":[\"Děkujeme za registraci účtu na Twenty! Než začneme, musíme potvrdit, že jste to vy. Klikněte výše k ověření vaší e-mailové adresy.\"],\"igorB1\":[\"Pracovní prostor bude deaktivován za \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" a všechna jeho data budou smazána.\"],\"7OEHy1\":[\"Toto je potvrzení, že heslo k vašemu účtu (\",[\"email\"],\") bylo úspěšně změněno \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Tento odkaz je platný pouze následující \",[\"duration\"],\". Pokud odkaz nefunguje, můžete přímo použít odkaz pro ověření přihlášení:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Aktualizujte své předplatné\"],\"wCKkSr\":[\"Ověřit e-mail\"],\"9MqLGX\":[\"Váš pracovní prostor <0>\",[\"workspaceDisplayName\"],\"</0> byl odstraněn, protože vaše předplatné vypršelo před \",[\"daysSinceInactive\"],\" dny.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Accepter invitation\"],\"Yxj+Uc\":[\"Alle data i dette arbejdsområde er blevet permanent slettet.\"],\"RPHFhC\":[\"Bekræft din e-mailadresse\"],\"nvkBPN\":[\"Forbind til Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Kære \",[\"userName\"]],\"tGme7M\":[\"har inviteret dig til at deltage i et arbejdsområde kaldet \"],\"uzTaYi\":[\"Hej\"],\"eE1nG1\":[\"Hvis du ikke har initieret denne ændring, bedes du straks kontakte ejeren af dit arbejdsområde.\"],\"Gz91L8\":[\"Hvis du ønsker at fortsætte med at bruge Twenty, opdater da dit abonnement inden for de næste \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Hvis du ønsker at bruge Twenty igen, kan du oprette et nyt arbejdsområde.\"],\"7JuhZQ\":[\"Det ser ud til, at dit arbejdsområde <0>\",[\"workspaceDisplayName\"],\"</0> er blevet suspenderet i \",[\"daysSinceInactive\"],\" dage.\"],\"PviVyk\":[\"Deltag i dit team på Twenty\"],\"ogtYkT\":[\"Adgangskode opdateret\"],\"OfhWJH\":[\"Nulstil\"],\"RE5NiU\":[\"Nulstil din adgangskode 🗝\"],\"7yDt8q\":[\"Tak fordi du har registreret en konto på Twenty! Før vi starter, skal vi bare bekræfte, at det er dig. Klik ovenfor for at bekræfte din e-mailadresse.\"],\"igorB1\":[\"Arbejdsområdet vil blive deaktiveret om \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", og alle dens data vil blive slettet.\"],\"7OEHy1\":[\"Dette er en bekræftelse på, at adgangskoden til din konto (\",[\"email\"],\") er blevet ændret den \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Dette link er kun gyldigt i de næste \",[\"duration\"],\". Hvis linket ikke fungerer, kan du bruge loginbekræftelseslinket direkte:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Opdater dit abonnement\"],\"wCKkSr\":[\"Bekræft e-mail\"],\"9MqLGX\":[\"Dit arbejdsområde <0>\",[\"workspaceDisplayName\"],\"</0> er blevet slettet, da dit abonnement udløb for \",[\"daysSinceInactive\"],\" dage siden.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Einladung akzeptieren\"],\"Yxj+Uc\":[\"Alle Daten in diesem Workspace wurden dauerhaft gelöscht.\"],\"RPHFhC\":[\"Bestätigen Sie Ihre E-Mail-Adresse\"],\"nvkBPN\":[\"Mit Twenty verbinden\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Sehr geehrte/r \",[\"userName\"]],\"tGme7M\":[\"hat Sie eingeladen, einem Workspace namens \"],\"uzTaYi\":[\"Hallo\"],\"eE1nG1\":[\"Wenn Sie diese Änderung nicht veranlasst haben, kontaktieren Sie bitte umgehend den Eigentümer Ihres Workspaces.\"],\"Gz91L8\":[\"Wenn Sie Twenty weiterhin nutzen möchten, aktualisieren Sie bitte Ihr Abonnement innerhalb der nächsten \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Wenn Sie Twenty erneut nutzen möchten, können Sie einen neuen Workspace erstellen.\"],\"7JuhZQ\":[\"Es scheint, dass Ihr Workspace <0>\",[\"workspaceDisplayName\"],\"</0> seit \",[\"daysSinceInactive\"],\" Tagen gesperrt ist.\"],\"PviVyk\":[\"Treten Sie Ihrem Team auf Twenty bei\"],\"ogtYkT\":[\"Passwort wurde aktualisiert\"],\"OfhWJH\":[\"Zurücksetzen\"],\"RE5NiU\":[\"Setzen Sie Ihr Passwort zurück 🗝\"],\"7yDt8q\":[\"Vielen Dank für Ihre Registrierung bei Twenty! Bevor wir beginnen, müssen wir nur bestätigen, dass Sie es sind. Klicken Sie oben, um Ihre E-Mail-Adresse zu verifizieren.\"],\"igorB1\":[\"Der Workspace wird in \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" deaktiviert, und alle Daten werden gelöscht.\"],\"7OEHy1\":[\"Dies ist eine Bestätigung, dass das Passwort für Ihr Konto (\",[\"email\"],\") am \",[\"formattedDate\"],\" erfolgreich geändert wurde.\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Dieser Link ist nur für die nächsten \",[\"duration\"],\" gültig. Wenn der Link nicht funktioniert, können Sie den Anmeldebestätigungslink direkt verwenden:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Aktualisieren Sie Ihr Abonnement\"],\"wCKkSr\":[\"E-Mail verifizieren\"],\"9MqLGX\":[\"Ihr Workspace <0>\",[\"workspaceDisplayName\"],\"</0> wurde gelöscht, da Ihr Abonnement vor \",[\"daysSinceInactive\"],\" Tagen abgelaufen ist.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Αποδοχή πρόσκλησης\"],\"Yxj+Uc\":[\"Όλα τα δεδομένα σε αυτόν τον χώρο εργασίας έχουν διαγραφεί μόνιμα.\"],\"RPHFhC\":[\"Επιβεβαιώστε τη διεύθυνση email σας\"],\"nvkBPN\":[\"Συνδεθείτε στο Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Αγαπητέ/ή \",[\"userName\"]],\"tGme7M\":[\"σας έχει προσκαλέσει να συμμετάσχετε σε έναν χώρο εργασίας με την ονομασία \"],\"uzTaYi\":[\"Γεια σας\"],\"eE1nG1\":[\"Εάν δεν έχετε προβεί εσείς σε αυτήν την αλλαγή, παρακαλώ επικοινωνήστε άμεσα με τον ιδιοκτήτη του χώρου εργασίας σας.\"],\"Gz91L8\":[\"Εάν επιθυμείτε να συνεχίστε να χρησιμοποιείτε το Twenty, παρακαλούμε ενημερώστε την συνδρομή σας εντός των επόμενων \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Εάν επιθυμείτε να χρησιμοποιήσετε ξανά το Twenty, μπορείτε να δημιουργήσετε ένα νέο χώρο εργασίας.\"],\"7JuhZQ\":[\"Φαίνεται ότι ο χώρος εργασίας σας <0>\",[\"workspaceDisplayName\"],\"</0> έχει ανασταλεί για \",[\"daysSinceInactive\"],\" ημέρες.\"],\"PviVyk\":[\"Συμμετέχετε στην ομάδα σας στο Twenty\"],\"ogtYkT\":[\"Ο κωδικός έχει ενημερωθεί\"],\"OfhWJH\":[\"Επαναφορά\"],\"RE5NiU\":[\"Επαναφέρετε τον κωδικό σας 🗝\"],\"7yDt8q\":[\"Σας ευχαριστούμε που εγγραφήκατε για έναν λογαριασμό στο Twenty! Πριν ξεκινήσουμε, πρέπει μόνο να επιβεβαιώσουμε ότι αυτός είστε εσείς. Κάντε κλικ παραπάνω για να επαληθεύσετε τη διεύθυνση email σας.\"],\"igorB1\":[\"Ο χώρος εργασίας θα απενεργοποιηθεί σε \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", και όλα τα δεδομένα του θα διαγραφούν.\"],\"7OEHy1\":[\"Αυτό είναι ένα επιβεβαίωση ότι ο κωδικός για τον λογαριασμό σας (\",[\"email\"],\") άλλαξε με επιτυχία στις \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Αυτός ο σύνδεσμος ισχύει μόνο για τις επόμενες \",[\"duration\"],\". Εάν ο σύνδεσμος δεν λειτουργεί, μπορείτε να χρησιμοποιήσετε τον σύνδεσμο επαλήθευσης σύνδεσης απευθείας:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Ενημερώστε τη συνδρομή σας\"],\"wCKkSr\":[\"Επαληθεύστε το Email\"],\"9MqLGX\":[\"Ο χώρος εργασίας σας <0>\",[\"workspaceDisplayName\"],\"</0> έχει διαγραφεί καθώς η συνδρομή σας έληξε \",[\"daysSinceInactive\"],\" ημέρες πριν.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Accept invite\"],\"Yxj+Uc\":[\"All data in this workspace has been permanently deleted.\"],\"RPHFhC\":[\"Confirm your email address\"],\"nvkBPN\":[\"Connect to Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Dear \",[\"userName\"]],\"tGme7M\":[\"has invited you to join a workspace called \"],\"uzTaYi\":[\"Hello\"],\"eE1nG1\":[\"If you did not initiate this change, please contact your workspace owner immediately.\"],\"Gz91L8\":[\"If you wish to continue using Twenty, please update your subscription within the next \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"If you wish to use Twenty again, you can create a new workspace.\"],\"7JuhZQ\":[\"It appears that your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been suspended for \",[\"daysSinceInactive\"],\" days.\"],\"PviVyk\":[\"Join your team on Twenty\"],\"ogtYkT\":[\"Password updated\"],\"OfhWJH\":[\"Reset\"],\"RE5NiU\":[\"Reset your password 🗝\"],\"7yDt8q\":[\"Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click above to verify your email address.\"],\"igorB1\":[\"The workspace will be deactivated in \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", and all its data will be deleted.\"],\"7OEHy1\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Update your subscription\"],\"wCKkSr\":[\"Verify Email\"],\"9MqLGX\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"daysSinceInactive\"],\" days ago.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Aceptar invitación\"],\"Yxj+Uc\":[\"Todos los datos de este espacio de trabajo han sido eliminados permanentemente.\"],\"RPHFhC\":[\"Confirma tu dirección de correo electrónico\"],\"nvkBPN\":[\"Conéctate a Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Estimado/a \",[\"userName\"]],\"tGme7M\":[\"te ha invitado a unirte a un espacio de trabajo llamado \"],\"uzTaYi\":[\"Hola\"],\"eE1nG1\":[\"Si no iniciaste este cambio, contacta inmediatamente al propietario de tu espacio de trabajo.\"],\"Gz91L8\":[\"Si deseas seguir usando Twenty, actualiza tu suscripción en los próximos \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Si deseas usar Twenty nuevamente, puedes crear un nuevo espacio de trabajo.\"],\"7JuhZQ\":[\"Parece que tu espacio de trabajo <0>\",[\"workspaceDisplayName\"],\"</0> ha estado suspendido durante \",[\"daysSinceInactive\"],\" días.\"],\"PviVyk\":[\"Únete a tu equipo en Twenty\"],\"ogtYkT\":[\"Contraseña actualizada\"],\"OfhWJH\":[\"Restablecer\"],\"RE5NiU\":[\"Restablece tu contraseña 🗝\"],\"7yDt8q\":[\"¡Gracias por registrarte en Twenty! Antes de comenzar, solo necesitamos confirmar que eres tú. Haz clic arriba para verificar tu dirección de correo electrónico.\"],\"igorB1\":[\"El espacio de trabajo se desactivará en \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" y se eliminarán todos sus datos.\"],\"7OEHy1\":[\"Esta es una confirmación de que la contraseña de tu cuenta (\",[\"email\"],\") se cambió correctamente el \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Este enlace solo es válido por los próximos \",[\"duration\"],\". Si el enlace no funciona, puedes usar directamente el enlace de verificación de inicio de sesión:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Actualiza tu suscripción\"],\"wCKkSr\":[\"Verificar correo electrónico\"],\"9MqLGX\":[\"Tu espacio de trabajo <0>\",[\"workspaceDisplayName\"],\"</0> ha sido eliminado porque tu suscripción caducó hace \",[\"daysSinceInactive\"],\" días.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Hyväksy kutsu\"],\"Yxj+Uc\":[\"Kaikki tiedot tässä työtilassa on pysyvästi poistettu.\"],\"RPHFhC\":[\"Vahvista sähköpostiosoitteesi\"],\"nvkBPN\":[\"Yhdistä Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Hyvä \",[\"userName\"]],\"tGme7M\":[\"on kutsunut sinut liittymään työtilaan nimeltä \"],\"uzTaYi\":[\"Hei\"],\"eE1nG1\":[\"Jos et itse aloittanut tätä muutosta, ota heti yhteyttä työtilasi omistajaan.\"],\"Gz91L8\":[\"Jos haluat jatkaa Twenty:n käyttöä, päivitä tilauksesi seuraavan \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" kuluessa.\"],\"0weyko\":[\"Jos haluat käyttää Twentyä uudelleen, voit luoda uuden työtilan.\"],\"7JuhZQ\":[\"Vaikuttaa siltä, että työtilasi <0>\",[\"workspaceDisplayName\"],\"</0> on ollut jäädytettynä \",[\"daysSinceInactive\"],\" päivää.\"],\"PviVyk\":[\"Liity tiimiisi Twentyssä\"],\"ogtYkT\":[\"Salasana päivitetty\"],\"OfhWJH\":[\"Nollaa\"],\"RE5NiU\":[\"Nollaa salasanasi 🗝\"],\"7yDt8q\":[\"Kiitos, että rekisteröidyit Twenty:n käyttäjäksi! Ennen kuin aloitamme, meidän täytyy vahvistaa, että kyseessä on sinä. Klikkaa yllä vahvistaaksesi sähköpostiosoitteesi.\"],\"igorB1\":[\"Työtila poistetaan käytöstä \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" kuluttua, ja kaikki sen tiedot poistetaan.\"],\"7OEHy1\":[\"Tämä on vahvistus siitä, että tilisi (\",[\"email\"],\") salasana on vaihdettu onnistuneesti \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Tämä linkki on voimassa vain seuraavat \",[\"duration\"],\". Jos linkki ei toimi, voit käyttää suoraan kirjautumisen varmennuslinkkiä:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Päivitä tilauksesi\"],\"wCKkSr\":[\"Vahvista sähköposti\"],\"9MqLGX\":[\"Työtilasi <0>\",[\"workspaceDisplayName\"],\"</0> on poistettu, koska tilauksesi vanheni \",[\"daysSinceInactive\"],\" päivää sitten.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Accepter l'invitation\"],\"Yxj+Uc\":[\"Toutes les données de cet espace de travail ont été supprimées définitivement.\"],\"RPHFhC\":[\"Confirmez votre adresse e-mail\"],\"nvkBPN\":[\"Connectez-vous à Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Cher \",[\"userName\"]],\"tGme7M\":[\"vous a invité à rejoindre un espace de travail appelé \"],\"uzTaYi\":[\"Bonjour\"],\"eE1nG1\":[\"Si vous n'avez pas initié ce changement, veuillez contacter immédiatement le propriétaire de votre espace de travail.\"],\"Gz91L8\":[\"Pour continuer à utiliser Twenty, veuillez mettre à jour votre abonnement dans les \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Pour réutiliser Twenty, vous pouvez créer un nouvel espace de travail.\"],\"7JuhZQ\":[\"Il semble que votre espace de travail <0>\",[\"workspaceDisplayName\"],\"</0> soit suspendu depuis \",[\"daysSinceInactive\"],\" jours.\"],\"PviVyk\":[\"Rejoignez votre équipe sur Twenty\"],\"ogtYkT\":[\"Mot de passe mis à jour\"],\"OfhWJH\":[\"Réinitialiser\"],\"RE5NiU\":[\"Réinitialisez votre mot de passe 🗝\"],\"7yDt8q\":[\"Merci de vous être inscrit sur Twenty ! Avant de commencer, nous devons confirmer votre identité. Cliquez ci-dessus pour vérifier votre adresse e-mail.\"],\"igorB1\":[\"L'espace de travail sera désactivé dans \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" et toutes ses données seront supprimées.\"],\"7OEHy1\":[\"Ceci est une confirmation que le mot de passe de votre compte (\",[\"email\"],\") a été modifié avec succès le \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Ce lien n'est valable que pour les \",[\"duration\"],\" suivants. Si le lien ne fonctionne pas, vous pouvez utiliser directement le lien de vérification de connexion :\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Mettez à jour votre abonnement\"],\"wCKkSr\":[\"Vérifiez l'e-mail\"],\"9MqLGX\":[\"Votre espace de travail <0>\",[\"workspaceDisplayName\"],\"</0> a été supprimé car votre abonnement a expiré il y a \",[\"daysSinceInactive\"],\" jours.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"קבל הזמנה\"],\"Yxj+Uc\":[\"כל הנתונים במרחב העבודה הזה נמחקו לצמיתות.\"],\"RPHFhC\":[\"אמת את כתובת האימייל שלך\"],\"nvkBPN\":[\"התחבר ל-Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"יקר \",[\"userName\"]],\"tGme7M\":[\"הזמין אותך להצטרף למרחב עבודה בשם \"],\"uzTaYi\":[\"שלום\"],\"eE1nG1\":[\"אם לא אתה התחלת את השינוי הזה, אנא פנה מיד לבעל מרחב העבודה שלך.\"],\"Gz91L8\":[\"אם תרצה להמשיך ולהשתמש ב-Twenty, אנא עדכן את המנוי שלך במהלך \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" הבאים.\"],\"0weyko\":[\"אם תרצה להשתמש ב-Twenty שוב, תוכל ליצור מרחב עבודה חדש.\"],\"7JuhZQ\":[\"נראה כי מרחב העבודה שלך <0>\",[\"workspaceDisplayName\"],\"</0> הושעה ל-\",[\"daysSinceInactive\"],\" ימים.\"],\"PviVyk\":[\"הצטרף לצוות שלך ב-Twenty\"],\"ogtYkT\":[\"הסיסמה עודכנה\"],\"OfhWJH\":[\"איפוס\"],\"RE5NiU\":[\"אפס את הסיסמה שלך 🗝\"],\"7yDt8q\":[\"תודה שנרשמת לחשבון ב-Twenty! לפני שנתחיל, אנחנו רק צריכים לוודא שזה אתה. לחץ למעלה כדי לאמת את כתובת המייל שלך.\"],\"igorB1\":[\"המרחב ייסגר בעוד \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", וכל הנתונים שלו ימחקו.\"],\"7OEHy1\":[\"זוהי אישור שהסיסמה לחשבון שלך (\",[\"email\"],\") שונתה בהצלחה ב-\",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"הקישור הזה תקף רק ל-\",[\"duration\"],\" הבאים. אם הקישור לא עובד, תוכל להשתמש בקישור אימות ההתחברות ישירות:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"עדכן את המנוי שלך\"],\"wCKkSr\":[\"אמת דוא\\\"ל\"],\"9MqLGX\":[\"המרחב שלך <0>\",[\"workspaceDisplayName\"],\"</0> נמחק כי המנוי שלך פג תוקף לפני \",[\"daysSinceInactive\"],\" ימים.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Meghívó elfogadása\"],\"Yxj+Uc\":[\"A munkaterület minden adata végérvényesen törlésre került.\"],\"RPHFhC\":[\"Erősítse meg email címét\"],\"nvkBPN\":[\"Csatlakozás a Twentyhez\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Kedves \",[\"userName\"]],\"tGme7M\":[\"meghívta Önt, hogy csatlakozzon egy munkaterülethez, amelynek a neve: \"],\"uzTaYi\":[\"Üdvözlöm\"],\"eE1nG1\":[\"Amennyiben Ön nem kezdeményezte ezt a változtatást, kérjük azonnal lépjen kapcsolatba a munkaterület tulajdonosával.\"],\"Gz91L8\":[\"Amennyiben szeretné folytatni a Twenty használatát, kérjük frissítse előfizetését a következő \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" időszakban.\"],\"0weyko\":[\"Ha újra szeretné használni a Twenty-t, létrehozhat egy új munkaterületet.\"],\"7JuhZQ\":[\"Úgy tűnik, hogy a munkaterülete <0>\",[\"workspaceDisplayName\"],\"</0> felfüggesztésre került \",[\"daysSinceInactive\"],\" napja.\"],\"PviVyk\":[\"Csatlakozzon a csapatához a Twentyn\"],\"ogtYkT\":[\"Jelszó frissítve\"],\"OfhWJH\":[\"Visszaállítás\"],\"RE5NiU\":[\"Jelszó visszaállítása 🗝\"],\"7yDt8q\":[\"Köszönjük, hogy regisztrált a Twenty szolgáltatására! Mielőtt elkezdenénk, csak meg kell erősítenünk, hogy ez Ön. Kattintson a fenti hivatkozásra email címének megerősítéséhez.\"],\"igorB1\":[\"A munkaterület \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" múlva lesz deaktiválva, és minden adat törlésre kerül.\"],\"7OEHy1\":[\"Ez egy megerősítés arról, hogy fiókja jelszavát (\",[\"email\"],\") sikeresen megváltoztatták \",[\"formattedDate\"],\" napján.\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Ez a hivatkozás csak a következő \",[\"duration\"],\" ideig érvényes. Ha a hivatkozás nem működik, használhatja közvetlenül a belépés ellenőrző hivatkozását:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Előfizetés frissítése\"],\"wCKkSr\":[\"Email ellenőrzése\"],\"9MqLGX\":[\"Munkaterülete <0>\",[\"workspaceDisplayName\"],\"</0> törlésre került, mivel előfizetése \",[\"daysSinceInactive\"],\" nappal ezelőtt lejárt.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Accetta l'invito\"],\"Yxj+Uc\":[\"Tutti i dati in questo spazio di lavoro sono stati eliminati definitivamente.\"],\"RPHFhC\":[\"Conferma il tuo indirizzo e-mail\"],\"nvkBPN\":[\"Accedi a Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Gentile \",[\"userName\"]],\"tGme7M\":[\"ti ha invitato a unirti a uno spazio di lavoro chiamato \"],\"uzTaYi\":[\"Salve\"],\"eE1nG1\":[\"Se non hai avviato questa modifica, contatta immediatamente il proprietario dello spazio di lavoro.\"],\"Gz91L8\":[\"Se desideri continuare a utilizzare Twenty, aggiorna il tuo abbonamento entro i prossimi \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Se desideri utilizzare nuovamente Twenty, puoi creare un nuovo spazio di lavoro.\"],\"7JuhZQ\":[\"Sembra che il tuo spazio di lavoro <0>\",[\"workspaceDisplayName\"],\"</0> sia stato sospeso per \",[\"daysSinceInactive\"],\" giorni.\"],\"PviVyk\":[\"Unisciti al tuo team su Twenty\"],\"ogtYkT\":[\"Password aggiornata\"],\"OfhWJH\":[\"Reimposta\"],\"RE5NiU\":[\"Reimposta la tua password 🗝\"],\"7yDt8q\":[\"Grazie per esserti registrato su Twenty! Prima di iniziare, dobbiamo solo confermare che sei tu. Clicca sopra per verificare il tuo indirizzo e-mail.\"],\"igorB1\":[\"L'area di lavoro sarà disattivata in \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" e tutti i suoi dati saranno cancellati.\"],\"7OEHy1\":[\"Questa è la conferma che la password del tuo account (\",[\"email\"],\") è stata modificata con successo in data \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Questo link è valido solo per i prossimi \",[\"duration\"],\". Se il link non funziona, puoi utilizzare direttamente il link di verifica del login:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Aggiorna il tuo abbonamento\"],\"wCKkSr\":[\"Verifica e-mail\"],\"9MqLGX\":[\"Il tuo spazio di lavoro <0>\",[\"workspaceDisplayName\"],\"</0> è stato cancellato poiché il tuo abbonamento è scaduto \",[\"daysSinceInactive\"],\" giorni fa.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"招待を承諾\"],\"Yxj+Uc\":[\"このワークスペースのすべてのデータは永久に削除されました。\"],\"RPHFhC\":[\"メールアドレスを確認\"],\"nvkBPN\":[\"Twentyに接続\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[[\"userName\"],\"様\"],\"tGme7M\":[\"というワークスペースに招待されました。\"],\"uzTaYi\":[\"こんにちは\"],\"eE1nG1\":[\"この変更を行っていない場合は、すぐにワークスペースの所有者に連絡してください。\"],\"Gz91L8\":[\"Twentyを引き続きご利用になる場合は、\",[\"remainingDays\"],\" \",[\"dayOrDays\"],\"以内にサブスクリプションを更新してください。\"],\"0weyko\":[\"Twentyを再度使用するには、新しいワークスペースを作成してください。\"],\"7JuhZQ\":[\"ワークスペース<0>\",[\"workspaceDisplayName\"],\"</0>は\",[\"daysSinceInactive\"],\"日間停止されているようです。\"],\"PviVyk\":[\"Twentyでチームに参加\"],\"ogtYkT\":[\"パスワードが更新されました\"],\"OfhWJH\":[\"リセット\"],\"RE5NiU\":[\"パスワードをリセット\"],\"7yDt8q\":[\"Twentyにご登録いただきありがとうございます!開始する前に、ご本人確認のために上記をクリックしてメールアドレスを確認してください。\"],\"igorB1\":[\"ワークスペースは\",[\"remainingDays\"],\" \",[\"dayOrDays\"],\"後に無効化され、すべてのデータが削除されます。\"],\"7OEHy1\":[\"アカウント(\",[\"email\"],\")のパスワードが\",[\"formattedDate\"],\"に正常に変更されたことを確認しました。\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"このリンクは\",[\"duration\"],\"のみ有効です。リンクが機能しない場合は、ログイン認証リンクを直接ご利用ください:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"サブスクリプションを更新\"],\"wCKkSr\":[\"メールを確認\"],\"9MqLGX\":[\"サブスクリプションが\",[\"daysSinceInactive\"],\"日前に期限切れとなったため、ワークスペース<0>\",[\"workspaceDisplayName\"],\"</0>が削除されました。\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"초대 수락\"],\"Yxj+Uc\":[\"이 워크스페이스의 모든 데이터가 영구적으로 삭제되었습니다.\"],\"RPHFhC\":[\"이메일 주소를 확인하세요\"],\"nvkBPN\":[\"Twenty에 연결하세요\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[[\"userName\"],\"님께\"],\"tGme7M\":[\"라는 워크스페이스에 초대되었습니다 \"],\"uzTaYi\":[\"안녕하세요\"],\"eE1nG1\":[\"이 변경을 시작하지 않으셨다면 즉시 워크스페이스 소유자에게 문의하세요.\"],\"Gz91L8\":[\"Twenty를 계속 사용하려면 다음 \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" 내에 구독을 업데이트하세요.\"],\"0weyko\":[\"Twenty를 다시 사용하려면 새 워크스페이스를 생성하세요.\"],\"7JuhZQ\":[\"워크스페이스 <0>\",[\"workspaceDisplayName\"],\"</0>이(가) \",[\"daysSinceInactive\"],\"일 동안 일시 중단된 것 같습니다.\"],\"PviVyk\":[\"Twenty에서 팀에 합류하세요\"],\"ogtYkT\":[\"비밀번호가 업데이트되었습니다\"],\"OfhWJH\":[\"초기화\"],\"RE5NiU\":[\"비밀번호를 재설정하세요 🗝\"],\"7yDt8q\":[\"Twenty에 계정을 등록해 주셔서 감사합니다! 시작하기 전에 본인 확인이 필요합니다. 위를 클릭하여 이메일 주소를 인증하세요.\"],\"igorB1\":[\"워크스페이스는 \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" 후에 비활성화되며 모든 데이터가 삭제됩니다.\"],\"7OEHy1\":[\"계정(\",[\"email\"],\")의 비밀번호가 \",[\"formattedDate\"],\"에 성공적으로 변경되었음을 확인합니다.\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"이 링크는 다음 \",[\"duration\"],\" 동안만 유효합니다. 링크가 작동하지 않으면 로그인 인증 링크를 직접 사용할 수 있습니다:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"구독을 업데이트하세요\"],\"wCKkSr\":[\"이메일 인증\"],\"9MqLGX\":[\"구독이 \",[\"daysSinceInactive\"],\"일 전에 만료되어 워크스페이스 <0>\",[\"workspaceDisplayName\"],\"</0>이(가) 삭제되었습니다.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Uitnodiging accepteren\"],\"Yxj+Uc\":[\"Alle gegevens in deze werkruimte zijn definitief verwijderd.\"],\"RPHFhC\":[\"Bevestig uw e-mailadres\"],\"nvkBPN\":[\"Verbinden met Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Beste \",[\"userName\"]],\"tGme7M\":[\"heeft u uitgenodigd om deel te nemen aan een werkruimte genaamd \"],\"uzTaYi\":[\"Hallo\"],\"eE1nG1\":[\"Als u deze wijziging niet heeft geïnitieerd, neem dan onmiddellijk contact op met de eigenaar van uw werkruimte.\"],\"Gz91L8\":[\"Als u Twenty wilt blijven gebruiken, werk dan uw abonnement bij binnen de komende \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Als u Twenty opnieuw wilt gebruiken, kunt u een nieuwe werkruimte aanmaken.\"],\"7JuhZQ\":[\"Het lijkt erop dat uw werkruimte <0>\",[\"workspaceDisplayName\"],\"</0> al voor \",[\"daysSinceInactive\"],\" dagen is opgeschort.\"],\"PviVyk\":[\"Sluit je aan bij je team op Twenty\"],\"ogtYkT\":[\"Wachtwoord bijgewerkt\"],\"OfhWJH\":[\"Resetten\"],\"RE5NiU\":[\"Reset uw wachtwoord 🗝\"],\"7yDt8q\":[\"Bedankt voor uw registratie voor een account op Twenty! Voordat we beginnen, moeten we gewoon bevestigen dat dit u bent. Klik hierboven om uw e-mailadres te verifiëren.\"],\"igorB1\":[\"De werkruimte wordt gedeactiveerd in \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", en alle gegevens zullen worden verwijderd.\"],\"7OEHy1\":[\"Dit is een bevestiging dat het wachtwoord voor uw account (\",[\"email\"],\") succesvol is gewijzigd op \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Deze link is slechts geldig voor de komende \",[\"duration\"],\". Als de link niet werkt, kunt u direct de inlogverificatielink gebruiken:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Werk uw abonnement bij\"],\"wCKkSr\":[\"E-mail verifiëren\"],\"9MqLGX\":[\"Uw werkruimte <0>\",[\"workspaceDisplayName\"],\"</0> is verwijderd omdat uw abonnement \",[\"daysSinceInactive\"],\" dagen geleden is verlopen.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Godta invitasjon\"],\"Yxj+Uc\":[\"Alle data i dette arbeidsområdet har blitt permanent slettet.\"],\"RPHFhC\":[\"Bekreft e-postadressen din\"],\"nvkBPN\":[\"Koble til Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Kjære \",[\"userName\"]],\"tGme7M\":[\"har invitert deg til å bli med i et arbeidsområde kalt \"],\"uzTaYi\":[\"Hei\"],\"eE1nG1\":[\"Hvis du ikke initierte denne endringen, vennligst kontakt eieren av arbeidsområdet ditt umiddelbart.\"],\"Gz91L8\":[\"Hvis du ønsker å fortsette å bruke Twenty, vennligst oppdater abonnementet ditt innen de neste \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Hvis du ønsker å bruke Twenty igjen, kan du opprette et nytt arbeidsområde.\"],\"7JuhZQ\":[\"Det ser ut til at ditt arbeidsområde <0>\",[\"workspaceDisplayName\"],\"</0> har blitt suspendert i \",[\"daysSinceInactive\"],\" dager.\"],\"PviVyk\":[\"Bli med teamet ditt på Twenty\"],\"ogtYkT\":[\"Passord oppdatert\"],\"OfhWJH\":[\"Tilbakestill\"],\"RE5NiU\":[\"Tilbakestill passordet ditt 🗝\"],\"7yDt8q\":[\"Takk for at du registrerte deg for en konto på Twenty! Før vi begynner, vi må bare bekrefte at dette er deg. Klikk ovenfor for å verifisere e-postadressen din.\"],\"igorB1\":[\"Arbeidsområdet vil bli deaktivert om \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", og alle dens data vil bli slettet.\"],\"7OEHy1\":[\"Dette er en bekreftelse på at passordet for kontoen din (\",[\"email\"],\") ble vellykket endret den \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Denne lenken er kun gyldig i de neste \",[\"duration\"],\". Hvis lenken ikke fungerer, kan du bruke verifiseringslenken for innlogging direkte:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Oppdater abonnementet ditt\"],\"wCKkSr\":[\"Verifiser e-post\"],\"9MqLGX\":[\"Ditt arbeidsområde <0>\",[\"workspaceDisplayName\"],\"</0> har blitt slettet da ditt abonnement utløp for \",[\"daysSinceInactive\"],\" dager siden.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Zaakceptuj zaproszenie\"],\"Yxj+Uc\":[\"Wszystkie dane w tej przestrzeni roboczej zostały trwale usunięte.\"],\"RPHFhC\":[\"Potwierdź swój adres email\"],\"nvkBPN\":[\"Połącz z Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Szanowny \",[\"userName\"]],\"tGme7M\":[\"zaprosił Cię do przyłączenia się do przestrzeni roboczej o nazwie \"],\"uzTaYi\":[\"Witaj\"],\"eE1nG1\":[\"Jeśli to nie Ty zainicjowałeś tę zmianę, skontaktuj się niezwłocznie z właścicielem przestrzeni roboczej.\"],\"Gz91L8\":[\"Jeśli chcesz kontynuować korzystanie z Twenty, proszę zaktualizuj swoją subskrypcję w ciągu następnych \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Jeśli chcesz ponownie korzystać z Twenty, możesz utworzyć nową przestrzeń roboczą.\"],\"7JuhZQ\":[\"Wygląda na to, że Twoja przestrzeń robocza <0>\",[\"workspaceDisplayName\"],\"</0> została zawieszona na \",[\"daysSinceInactive\"],\" dni.\"],\"PviVyk\":[\"Dołącz do swojego zespołu w Twenty\"],\"ogtYkT\":[\"Hasło zaktualizowane\"],\"OfhWJH\":[\"Zresetuj\"],\"RE5NiU\":[\"Zresetuj swoje hasło 🗝\"],\"7yDt8q\":[\"Dziękujemy za rejestrację konta w Twenty! Zanim zaczniemy, musimy tylko potwierdzić, że to Ty. Kliknij powyżej, aby zweryfikować swój adres email.\"],\"igorB1\":[\"Przestrzeń robocza zostanie deaktywowana za \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", a wszystkie jej dane zostaną usunięte.\"],\"7OEHy1\":[\"To jest potwierdzenie, że hasło do Twojego konta (\",[\"email\"],\") zostało pomyślnie zmienione \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Ten link jest ważny tylko przez \",[\"duration\"],\". Jeśli link nie działa, można bezpośrednio użyć linku weryfikacyjnego logowania:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Zaktualizuj swoją subskrypcję\"],\"wCKkSr\":[\"Zweryfikuj Email\"],\"9MqLGX\":[\"Twoja przestrzeń robocza <0>\",[\"workspaceDisplayName\"],\"</0> została usunięta, ponieważ Twoja subskrypcja wygasła \",[\"daysSinceInactive\"],\" dni temu.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Àććēƥţ ĩńvĩţē\"],\"Yxj+Uc\":[\"Àĺĺ ďàţà ĩń ţĥĩś ŵōŕķśƥàćē ĥàś ƀēēń ƥēŕḿàńēńţĺŷ ďēĺēţēď.\"],\"RPHFhC\":[\"Ćōńƒĩŕḿ ŷōũŕ ēḿàĩĺ àďďŕēśś\"],\"nvkBPN\":[\"Ćōńńēćţ ţō Ţŵēńţŷ\"],\"JRzgV7\":[\"Ďēàŕ\"],\"Lm5BBI\":[\"Ďēàŕ \",[\"userName\"]],\"tGme7M\":[\"ĥàś ĩńvĩţēď ŷōũ ţō Ĵōĩń à ŵōŕķśƥàćē ćàĺĺēď \"],\"uzTaYi\":[\"Ĥēĺĺō\"],\"eE1nG1\":[\"Ĩƒ ŷōũ ďĩď ńōţ ĩńĩţĩàţē ţĥĩś ćĥàńĝē, ƥĺēàśē ćōńţàćţ ŷōũŕ ŵōŕķśƥàćē ōŵńēŕ ĩḿḿēďĩàţēĺŷ.\"],\"Gz91L8\":[\"Ĩƒ ŷōũ ŵĩśĥ ţō ćōńţĩńũē ũśĩńĝ Ţŵēńţŷ, ƥĺēàśē ũƥďàţē ŷōũŕ śũƀśćŕĩƥţĩōń ŵĩţĥĩń ţĥē ńēxţ \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Ĩƒ ŷōũ ŵĩśĥ ţō ũśē Ţŵēńţŷ àĝàĩń, ŷōũ ćàń ćŕēàţē à ńēŵ ŵōŕķśƥàćē.\"],\"7JuhZQ\":[\"Ĩţ àƥƥēàŕś ţĥàţ ŷōũŕ ŵōŕķśƥàćē <0>\",[\"workspaceDisplayName\"],\"</0> ĥàś ƀēēń śũśƥēńďēď ƒōŕ \",[\"daysSinceInactive\"],\" ďàŷś.\"],\"PviVyk\":[\"ĵōĩń ŷōũŕ ţēàḿ ōń Ţŵēńţŷ\"],\"ogtYkT\":[\"Ƥàśśŵōŕď ũƥďàţēď\"],\"OfhWJH\":[\"Ŕēśēţ\"],\"RE5NiU\":[\"Ŕēśēţ ŷōũŕ ƥàśśŵōŕď 🗝\"],\"7yDt8q\":[\"Ţĥàńķś ƒōŕ ŕēĝĩśţēŕĩńĝ ƒōŕ àń àććōũńţ ōń Ţŵēńţŷ! ßēƒōŕē ŵē ĝēţ śţàŕţēď, ŵē Ĵũśţ ńēēď ţō ćōńƒĩŕḿ ţĥàţ ţĥĩś ĩś ŷōũ. Ćĺĩćķ àƀōvē ţō vēŕĩƒŷ ŷōũŕ ēḿàĩĺ àďďŕēśś.\"],\"igorB1\":[\"Ţĥē ŵōŕķśƥàćē ŵĩĺĺ ƀē ďēàćţĩvàţēď ĩń \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", àńď àĺĺ ĩţś ďàţà ŵĩĺĺ ƀē ďēĺēţēď.\"],\"7OEHy1\":[\"Ţĥĩś ĩś à ćōńƒĩŕḿàţĩōń ţĥàţ ƥàśśŵōŕď ƒōŕ ŷōũŕ àććōũńţ (\",[\"email\"],\") ŵàś śũććēśśƒũĺĺŷ ćĥàńĝēď ōń \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"Ţĥĩś ĩś à ćōńƒĩŕḿàţĩōń ţĥàţ ƥàśśŵōŕď ƒōŕ ŷōũŕ àććōũńţ (\",[\"email\"],\") ŵàś śũććēśśƒũĺĺŷ ćĥàńĝēď ōń \",[\"formattedDate\"],\".<0/><1/>Ĩƒ ŷōũ ďĩď ńōţ ĩńĩţĩàţē ţĥĩś ćĥàńĝē, ƥĺēàśē ćōńţàćţ ŷōũŕ ŵōŕķśƥàćē ōŵńēŕ ĩḿḿēďĩàţēĺŷ.\"],\"R4gMjN\":[\"Ţĥĩś ĺĩńķ ĩś ōńĺŷ vàĺĩď ƒōŕ ţĥē ńēxţ \",[\"duration\"],\". Ĩƒ ţĥē ĺĩńķ ďōēś ńōţ ŵōŕķ, ŷōũ ćàń ũśē ţĥē ĺōĝĩń vēŕĩƒĩćàţĩōń ĺĩńķ ďĩŕēćţĺŷ:\"],\"2oA637\":[\"Ţĥĩś ĺĩńķ ĩś ōńĺŷ vàĺĩď ƒōŕ ţĥē ńēxţ \",[\"duration\"],\". Ĩƒ ţĥē ĺĩńķ ďōēś ńōţ ŵōŕķ, ŷōũ ćàń ũśē ţĥē ĺōĝĩń vēŕĩƒĩćàţĩōń ĺĩńķ ďĩŕēćţĺŷ:<0/>\"],\"H0v4yC\":[\"Ũƥďàţē ŷōũŕ śũƀśćŕĩƥţĩōń\"],\"wCKkSr\":[\"Vēŕĩƒŷ Ēḿàĩĺ\"],\"9MqLGX\":[\"Ŷōũŕ ŵōŕķśƥàćē <0>\",[\"workspaceDisplayName\"],\"</0> ĥàś ƀēēń ďēĺēţēď àś ŷōũŕ śũƀśćŕĩƥţĩōń ēxƥĩŕēď \",[\"daysSinceInactive\"],\" ďàŷś àĝō.\"],\"KFmFrQ\":[\"Ŷōũŕ ŵōŕķśƥàćē <0>\",[\"workspaceDisplayName\"],\"</0> ĥàś ƀēēń ďēĺēţēď àś ŷōũŕ śũƀśćŕĩƥţĩōń ēxƥĩŕēď \",[\"inactiveDaysBeforeDelete\"],\" ďàŷś àĝō.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Aceitar convite\"],\"Yxj+Uc\":[\"Todos os dados deste workspace foram excluídos permanentemente.\"],\"RPHFhC\":[\"Confirme seu endereço de e-mail\"],\"nvkBPN\":[\"Conecte-se ao Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Caro \",[\"userName\"]],\"tGme7M\":[\"convidou você para participar de um workspace chamado \"],\"uzTaYi\":[\"Olá\"],\"eE1nG1\":[\"Se você não iniciou essa alteração, entre em contato com o proprietário do workspace imediatamente.\"],\"Gz91L8\":[\"Se quiser continuar usando o Twenty, atualize sua assinatura nos próximos \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Se quiser usar o Twenty novamente, você pode criar um novo workspace.\"],\"7JuhZQ\":[\"Parece que seu workspace <0>\",[\"workspaceDisplayName\"],\"</0> foi suspenso por \",[\"daysSinceInactive\"],\" dias.\"],\"PviVyk\":[\"Junte-se à sua equipe no Twenty\"],\"ogtYkT\":[\"Senha atualizada\"],\"OfhWJH\":[\"Redefinir\"],\"RE5NiU\":[\"Redefina sua senha 🗝\"],\"7yDt8q\":[\"Obrigado por se registrar no Twenty! Antes de começarmos, precisamos confirmar que é você. Clique acima para verificar seu endereço de e-mail.\"],\"igorB1\":[\"O workspace será desativado em \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", e todos os seus dados serão excluídos.\"],\"7OEHy1\":[\"Esta é uma confirmação de que a senha da sua conta (\",[\"email\"],\") foi alterada com sucesso em \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Este link é válido apenas para os próximos \",[\"duration\"],\". Se o link não funcionar, você pode usar o link de verificação de login diretamente:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Atualize sua assinatura\"],\"wCKkSr\":[\"Verificar e-mail\"],\"9MqLGX\":[\"Seu workspace <0>\",[\"workspaceDisplayName\"],\"</0> foi excluído porque sua assinatura expirou há \",[\"daysSinceInactive\"],\" dias.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Aceitar convite\"],\"Yxj+Uc\":[\"Todos os dados deste workspace foram eliminados permanentemente.\"],\"RPHFhC\":[\"Confirme o seu endereço de e-mail\"],\"nvkBPN\":[\"Conecte-se ao Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Caro \",[\"userName\"]],\"tGme7M\":[\"convidou-o a juntar-se a um workspace chamado \"],\"uzTaYi\":[\"Olá\"],\"eE1nG1\":[\"Se não iniciou esta alteração, contacte imediatamente o proprietário do seu workspace.\"],\"Gz91L8\":[\"Se quiser continuar a usar o Twenty, atualize a sua subscrição nos próximos \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Se quiser usar o Twenty novamente, pode criar um novo workspace.\"],\"7JuhZQ\":[\"Parece que o seu workspace <0>\",[\"workspaceDisplayName\"],\"</0> foi suspenso há \",[\"daysSinceInactive\"],\" dias.\"],\"PviVyk\":[\"Junte-se à sua equipa no Twenty\"],\"ogtYkT\":[\"Palavra-passe atualizada\"],\"OfhWJH\":[\"Redefinir\"],\"RE5NiU\":[\"Redefina a sua palavra-passe 🗝\"],\"7yDt8q\":[\"Obrigado por se registar no Twenty! Antes de começarmos, precisamos confirmar que é você. Clique acima para verificar o seu endereço de e-mail.\"],\"igorB1\":[\"O workspace será desativado em \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\" e todos os seus dados serão eliminados.\"],\"7OEHy1\":[\"Esta é uma confirmação de que a palavra-passe da sua conta (\",[\"email\"],\") foi alterada com sucesso em \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Este link só é válido para os próximos \",[\"duration\"],\". Se o link não funcionar, pode usar diretamente o link de verificação de login:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Atualize a sua subscrição\"],\"wCKkSr\":[\"Verifique o e-mail\"],\"9MqLGX\":[\"O seu workspace <0>\",[\"workspaceDisplayName\"],\"</0> foi eliminado porque a sua subscrição expirou há \",[\"daysSinceInactive\"],\" dias.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Acceptați invitația\"],\"Yxj+Uc\":[\"Toate datele din acest spațiu de lucru au fost șterse permanent.\"],\"RPHFhC\":[\"Confirmați adresa de email\"],\"nvkBPN\":[\"Conectați-vă la Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Dragă \",[\"userName\"]],\"tGme7M\":[\"v-a invitat să vă alăturați unui spațiu de lucru numit \"],\"uzTaYi\":[\"Salut\"],\"eE1nG1\":[\"Dacă nu ați inițiat această schimbare, vă rugăm să contactați imediat proprietarul spațiului de lucru.\"],\"Gz91L8\":[\"Dacă doriți să continuați utilizarea Twenty, vă rugăm să vă actualizați abonamentul în următoarele \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Dacă doriți să folosiți din nou Twenty, puteți crea un spațiu de lucru nou.\"],\"7JuhZQ\":[\"Se pare că spațiul dvs. de lucru <0>\",[\"workspaceDisplayName\"],\"</0> a fost suspendat de \",[\"daysSinceInactive\"],\" zile.\"],\"PviVyk\":[\"Alăturați-vă echipei pe Twenty\"],\"ogtYkT\":[\"Parola a fost actualizată\"],\"OfhWJH\":[\"Resetează\"],\"RE5NiU\":[\"Resetați-vă parola 🗝\"],\"7yDt8q\":[\"Vă mulțumim că v-ați înregistrat pentru un cont pe Twenty! Înainte de a începe, trebuie doar să confirmăm că acesta sunteți dvs. Faceți clic mai sus pentru a verifica adresa de email.\"],\"igorB1\":[\"Spațiul de lucru va fi dezactivat în \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\", iar toate datele sale vor fi șterse.\"],\"7OEHy1\":[\"Aceasta este o confirmare că parola contului dvs. (\",[\"email\"],\") a fost schimbată cu succes la data de \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Acest link este valabil doar pentru următoarele \",[\"duration\"],\". Dacă linkul nu funcționează, puteți folosi direct linkul de verificare a autentificării:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Actualizați abonamentul\"],\"wCKkSr\":[\"Verificați Email\"],\"9MqLGX\":[\"Spațiul dvs. de lucru <0>\",[\"workspaceDisplayName\"],\"</0> a fost șters, deoarece abonamentul a expirat cu \",[\"daysSinceInactive\"],\" zile în urmă.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;
@@ -0,0 +1 @@
/*eslint-disable*/import type{Messages}from"@lingui/core";export const messages=JSON.parse("{\"4WPI3S\":[\"Принять приглашение\"],\"Yxj+Uc\":[\"Все данные в этом рабочем пространстве были окончательно удалены.\"],\"RPHFhC\":[\"Подтвердите ваш адрес электронной почты\"],\"nvkBPN\":[\"Подключиться к Twenty\"],\"JRzgV7\":[\"Dear\"],\"Lm5BBI\":[\"Уважаемый \",[\"userName\"]],\"tGme7M\":[\"пригласил вас присоединиться к рабочему пространству под названием \"],\"uzTaYi\":[\"Здравствуйте\"],\"eE1nG1\":[\"Если вы не инициировали это изменение, немедленно свяжитесь с владельцем вашего рабочего пространства.\"],\"Gz91L8\":[\"Если вы хотите продолжить использование Twenty, пожалуйста, обновите вашу подписку в течение следующих \",[\"remainingDays\"],\" \",[\"dayOrDays\"],\".\"],\"0weyko\":[\"Если вы захотите снова использовать Twenty, вы сможете создать новое рабочее пространство.\"],\"7JuhZQ\":[\"Похоже, что ваше рабочее пространство <0>\",[\"workspaceDisplayName\"],\"</0> было приостановлено на \",[\"daysSinceInactive\"],\" \",[\"daysSinceInactive\",\"plural\",{\"one\":[\"день\"],\"few\":[\"дня\"],\"many\":[\"дней\"],\"other\":[\"дня\"]}],\".\"],\"PviVyk\":[\"Присоединяйтесь к вашей команде на Twenty\"],\"ogtYkT\":[\"Пароль обновлен\"],\"OfhWJH\":[\"Сбросить\"],\"RE5NiU\":[\"Сбросьте ваш пароль 🗝\"],\"7yDt8q\":[\"Спасибо за регистрацию в аккаунте Twenty! Прежде чем начать, нам нужно убедиться, что это вы. Кликните выше, чтобы подтвердить ваш адрес электронной почты.\"],\"igorB1\":[\"Рабочее пространство будет деактивировано через \",[\"remainingDays\"],\" \",[\"remainingDays\",\"plural\",{\"one\":[\"день\"],\"few\":[\"дня\"],\"many\":[\"дней\"],\"other\":[\"дней\"]}],\", и все его данные будут удалены.\"],\"7OEHy1\":[\"Это подтверждение того, что пароль для вашего аккаунта (\",[\"email\"],\") был успешно изменен \",[\"formattedDate\"],\".\"],\"wSOsS+\":[\"This is a confirmation that password for your account (\",[\"email\"],\") was successfully changed on \",[\"formattedDate\"],\".<0/><1/>If you did not initiate this change, please contact your workspace owner immediately.\"],\"R4gMjN\":[\"Эта ссылка действительна только в течение следующих \",[\"duration\"],\". Если ссылка не работает, вы можете использовать ссылку для подтверждения входа напрямую:\"],\"2oA637\":[\"This link is only valid for the next \",[\"duration\"],\". If the link does not work, you can use the login verification link directly:<0/>\"],\"H0v4yC\":[\"Обновите вашу подписку\"],\"wCKkSr\":[\"Подтвердить электронную почту\"],\"9MqLGX\":[\"Ваше рабочее пространство <0>\",[\"workspaceDisplayName\"],\"</0> было удалено, так как ваша подписка истекла \",[\"daysSinceInactive\"],\" \",[\"inactiveDaysBeforeDelete\",\"plural\",{\"one\":[\"день\"],\"few\":[\"дня\"],\"many\":[\"дней\"],\"other\":[\"дней\"]}],\" назад.\"],\"KFmFrQ\":[\"Your workspace <0>\",[\"workspaceDisplayName\"],\"</0> has been deleted as your subscription expired \",[\"inactiveDaysBeforeDelete\"],\" days ago.\"]}")as Messages;

Some files were not shown because too many files have changed in this diff Show More