Compare commits

..
435 Commits
Author SHA1 Message Date
Félix MalfaitandFélix Malfait fc6aafd3c6 Fix clickhouse migration not in dist (#11709)
Clickhouse migrations were not correctly copied to the dist folder (for
command yarn clickhouse:migrate:prod)
2025-04-23 21:23:31 +02:00
Charles BochetandGitHub 8076ff5b82 Fix broken metadata sync due to index removal (#11706)
Changing strategy regarding disabling index creation with new relation
system
2025-04-23 18:06:19 +02:00
MarieandGitHub 4257f30f12 Permission checks on twentyORM global manager (#11477)
In this PR we are handling permissions when using
twentyORMGlobalManager,
and handling permissions for rest api and api key
2025-04-23 17:57:48 +02:00
Thomas TrompetteandGitHub 28a1354928 Allow webhook only on active versions (#11705)
- webhook call should only be allow on active versions
- fixing exceptions that are wrongly sent to sentry
2025-04-23 17:28:41 +02:00
Charles BochetandGitHub a2d2db441f Improve Filter chip empty state (#11704)
Cases:

<img width="477" alt="image"
src="https://github.com/user-attachments/assets/06da3efc-b878-499f-97de-570317caa9b7"
/>

<img width="477" alt="image"
src="https://github.com/user-attachments/assets/40114c16-2810-489d-a60f-2579e2916691"
/>

<img width="477" alt="image"
src="https://github.com/user-attachments/assets/fa80d7cf-7c2d-4c19-919e-2d4c78a9202b"
/>
2025-04-23 16:54:11 +02:00
Charles BochetandGitHub a4882089b1 Add is relation enabled to default feature flags (#11703) 2025-04-23 16:26:53 +02:00
Paul RastoinandGitHub f2828883ad Fix click outside dropdown content confirmation modal (#11702)
# Introduction
Closes https://github.com/twentyhq/twenty/issues/11674
2025-04-23 14:23:15 +00:00
Thomas TrompetteandGitHub 1333c64d98 Add diff in workflow update events + fix existing (#11700)
Closes https://github.com/twentyhq/twenty/issues/11676.

Diff should be available for objects displaying timeline activities. It
should also fit the existing type.
2025-04-23 15:43:41 +02:00
Félix MalfaitandGitHub e1054b0474 Fix clickhouse migration (#11699)
It looks like Clickhouse Cloud uses a different config var than the docs
2025-04-23 15:09:23 +02:00
Charles BochetandGitHub 457c6ed113 Improve storybook stories (#11694)
## Improvements

### TaskGroups

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/0dbd71fe-3999-4cf5-870e-565b8e2df1b9"
/>

### EventCardMessage

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/b41cddc1-3ff2-411f-9ac6-8d4035579e54"
/>

### Blocklist stories

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/e997a0c1-e7e2-4ea4-9253-f31609ee44df"
/>

### WorkflowEditActionUpdateRecord

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/275dab17-ad33-4174-8671-31ba455c20a4"
/>
2025-04-23 13:20:13 +02:00
Félix MalfaitandGitHub 93c2ba153b Add clickhouse prod migration command (#11695)
Add a missing command to launch clickhouse migrations the same way we
launch postgres migrations
2025-04-23 13:02:00 +02:00
martmullandGitHub b6e8103df7 Mime encode names in email headers (#11691)
# Before
<img width="618" alt="image"
src="https://github.com/user-attachments/assets/e6d7c0ac-10f8-4d2e-abe4-05bf86a2ea9e"
/>

# After
<img width="504" alt="image"
src="https://github.com/user-attachments/assets/db147cb9-be19-4d08-b357-cb554dc2a4ae"
/>

Fixes also Emojis encoding in Subject ->
<img width="540" alt="image"
src="https://github.com/user-attachments/assets/8c27743f-3a50-46eb-b0f3-492219bbd646"
/>

 Works with microsoft accounts
<img width="360" alt="image"
src="https://github.com/user-attachments/assets/d924440f-c107-4c1c-ad4d-b4b27016f102"
/>


Fixes https://github.com/twentyhq/twenty/issues/9194
2025-04-23 11:37:40 +02:00
EtienneandGitHub ea850f323a Fix inline cell interactions (#11686)
@bosiraphael I've slightly updated [your recent
pr](https://github.com/twentyhq/twenty/pull/11654)
- before
<img width="490" alt="Screenshot 2025-04-22 at 17 48 26"
src="https://github.com/user-attachments/assets/604d65df-5433-4524-bdc5-6f5083b3c045"
/>

- after
<img width="488" alt="Screenshot 2025-04-22 at 17 47 41"
src="https://github.com/user-attachments/assets/21273372-538e-454e-947b-09373d9e0d6d"
/>


closes https://github.com/twentyhq/core-team-issues/issues/831 and
https://github.com/twentyhq/core-team-issues/issues/828
2025-04-23 08:46:10 +00:00
Charles BochetandGitHub fa5f758228 Fix storybook / chromatic tests flakyness and integration tests (#11687)
## Storybook flakyness

### Actor Display image flakyness

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/875c0738-5e31-4aba-9231-4ba5f78d1355"
/>

**Fix:** stop using a random usage

### Task Groups broken

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/c67e47a1-a027-43f1-9601-68d61a8052b4"
/>

**Fix:** add missing TabListComponentInstance

## Flaky dates

Add https://github.com/k35o/storybook-addon-mock-date

## Integration tests

Fix broken tests due to relation refactoring
2025-04-23 01:57:36 +02:00
Félix MalfaitandGitHub 8694840b92 Rename webhook and open api urls (#11684)
We want to have fewer base path for routing.

We will have:
- /files
- /webhooks
- /graphql
- /metadata
- /rest
- /auth
- /healthz

I'm moving /open-api under /rest, and centralizing the webhooks
(removing /stripe and /cloudflare)
2025-04-22 22:24:26 +02:00
Paul RastoinandGitHub fba9ae6dfe Fallback undefined recordDataChip record name to empty string (#11685)
# Introduction
Closes #11682 

This seems to always has been quite flaky but this latest
[update](https://github.com/twentyhq/twenty/commit/f0de6d31b7b3b84671c3a5af8fd047ef7432666f#diff-63704155b77d9690a87a1ebd9f364bab7e7bb12afed7cc5a5516561ec3798f29L20)
seems to made the name optional.

As previously `undefined` being transpiles to a string results in
`"undefined"` which not a good thing neither 🙃 but wouldn't throw before
2025-04-22 20:17:44 +02:00
cc29c25176 feat: new relation sync-metadata, twenty-orm, create/update (#10217)
Fix
https://github.com/twentyhq/core-team-issues/issues/330#issue-2827026606
and
https://github.com/twentyhq/core-team-issues/issues/327#issue-2827001814

What this PR does when `isNewRelationEnabled` is set to `true`:
- [x] Drop the creation of the  foreign key as a `FieldMetadata`
- [x] Stop creating `RelationMetadata`
- [x] Properly fill `FieldMetadata` of type `RELATION` during the sync
command
- [x] Use new relation settings in TwentyORM
- [x] Properly create `FieldMetadata` relations when we create a new
object
- [x] Handle `database:reset` with new relations

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-04-22 19:01:39 +02:00
Paul RastoinandGitHub de1489aabb Time line activity wrong type (#11673)
# Introduction
Closes https://github.com/twentyhq/core-team-issues/issues/874

`TimeLineActivity` fields `` and `` were typed as required whereas in
reality are nullable, resulting in the related sentry error.
Refactored the type then the related components in order to handle
nullable use case
2025-04-22 15:17:08 +00:00
Baptiste DevessierandGitHub efab98a8f8 Get all first depth fields in the table and the board (#11650)
After discussing it with the team, we now want to query all fields in
the table and the board by default. Feeding the cache with exhaustive
data will make the side panel's life easier, as it needs all the record
fields to determine the actions to enable.
2025-04-22 16:45:36 +02:00
Thomas TrompetteandGitHub 0b7024c94a Perform batch update on workflow runs (#11680)
Command times out on workflow versions with too many runs. Performing
batch update instead
2025-04-22 16:07:29 +02:00
EtienneandGitHub 5250d5c8d6 fix subscription item update (#11648)
closes
https://twenty-v7.sentry.io/issues/6550388239/?environment=prod&project=4507072499810304&query=is%3Aunresolved%20issue.priority%3A%5Bhigh%2C%20medium%5D&referrer=issue-stream&stream_index=6
2025-04-22 14:01:36 +02:00
Raphaël BosiandGitHub 87083cb414 834 Design Adjustments for the Record Page Breadcrumb (#11670)
# Design Adjustments for the Record Page Breadcrumb

Closes [834](https://github.com/twentyhq/core-team-issues/issues/834)
and [826](https://github.com/twentyhq/core-team-issues/issues/826)

## Description

- Added the breadcrumb to every object (not just the workflows)
- Fixed spacings
- Changed icon color from primary to tertiary for proper visual
hierarchy
- Displayed pagination information (current/total)
- Close button has been removed to simplify the UI
- Navigate to index page when the breadcrumb is clicked
- Fixed problems when two record title cells were displayed at the same
time (in the header and in the record page)

## Before

<img width="247" alt="Capture d’écran 2025-04-22 à 12 15 34"
src="https://github.com/user-attachments/assets/5ca2aca7-ffb0-49ea-8d3a-4bd621d78f8d"
/>

## After

<img width="233" alt="Capture d’écran 2025-04-22 à 12 15 06"
src="https://github.com/user-attachments/assets/cbcb5dfe-d616-47c9-8017-71dd4d388534"
/>
2025-04-22 13:52:56 +02:00
Raphaël BosiandGitHub 251a7b126b 838 design adjustments on table kanban and inline fields (#11654)
Closes [#838]( https://github.com/twentyhq/core-team-issues/issues/838)
Design adjustments on table kanban and inline fields.

The table row border selection will be done later since it is harder
than initially thought.
2025-04-22 11:15:53 +02:00
Thomas TrompetteandGitHub 33e0794da9 Update workflow statuses in real time (#11653)
Statuses are maintained by an async job. Those are calculations that we
would like to avoid using in both frontend and backend. Using push
updates are an easier way.



https://github.com/user-attachments/assets/31e44a82-08a8-4100-a38e-c965d5c73ee8
2025-04-22 11:03:12 +02:00
Ken LynchandGitHub 3ce3793129 Update glossary.mdx (#11661)
Remove extraneous AI response. :)
2025-04-22 10:17:16 +02:00
Félix MalfaitandGitHub aa2f3438fa Fix email verification (#11660)
Email verification modal had been broken and looked bad

I also added stories to make it more evident if this happens again
2025-04-20 14:18:43 +02:00
Félix MalfaitandGitHub 24873d0c1d Remove versions from package.json (#11658)
Now the source of truth for the version is set during the build process.
We set it as an environment variable from the tags.
We could add it back to the package.json during the build process (from
the git tag), but there is not use for it at the moment since it's not
npm packages.
2025-04-19 23:03:06 +02:00
Antoine MoreauxandGitHub 83434deb22 refactor(auth): optimize work email handling in sign-up flow (#11655)
Extracted isWorkEmail check into a variable for reusability and adjusted
subdomain generation to conditionally include email. This enhances code
readability and maintains logic consistency.
2025-04-19 15:28:41 +02:00
Baptiste DevessierandGitHub 826889715b Fix e2e tests (#11656)
After @bosiraphael's updates on the table, cells are duplicated when
they get the hover/focus. Playwright has a hard time finding which
element to click on.
I dislike my solution because it doesn't mimic how a real user would use
the application, but I couldn't find a better solution that wasn't
flaky.
2025-04-18 18:39:43 +02:00
MarieandGitHub f8b9e4d780 ignoreLock when recomputing rolePermissions when creating datasource (#11657)
[sentry](https://twenty-v7.sentry.io/issues/6545999328/?environment=prod&project=4507072499810304&query=is%3Aunresolved%20issue.priority%3A%5Bhigh%2C%20medium%5D&referrer=issue-stream&sort=date&stream_index=2)

Our workers have failing jobs because they are concurrently creating
datasources on different pods
While the datasources are independent, they read from the same redis
cache, with the same `RolesPermissionsOngoingCachingLock` value in
redis.
<img width="852" alt="Capture d’écran 2025-04-18 à 18 00 20"
src="https://github.com/user-attachments/assets/42ce8479-acc1-462b-af4c-b547cc2bb0b8"
/>
As a consequence they can fail to create a datasource: the first
datasource computes the permissions and sets ongoingCachingLock to true,
then the second arrives, there are still no available permissions in the
cache, but because of the lock it early returns without permissions and
fails. This behavior goes unnoticed on the product and helps
performances, but is annoying for workers as jobs are failing.

Let's remove the cache lock when creating the datasource !
2025-04-18 18:16:34 +02:00
8512904c0a refacto clickoutside componentv2 (#11644)
Switch to ComponentV2

Friday morning refacto & chill with @charles

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-18 17:48:30 +02:00
Félix MalfaitandGitHub cf5649a1df Deprecate Sentry release (#11651)
Let's deprecate Sentry Release and use APP_VERSION instead. 

It'll make it more clear in the interface to use named version for bug
analysis, than commit sha
2025-04-18 15:48:48 +02:00
martmullandGitHub fba4192d4b Fix rest api integration tests (#11645)
as title
2025-04-18 15:23:36 +02:00
16bd0425e0 i18n - translations (#11647)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-18 14:22:25 +02:00
MarieandGitHub fd750c7c0e Fix metadata cache flush (#11646)
Attempt to fix
https://twenty-v7.sentry.io/issues/6545999328/?environment=prod&project=4507072499810304&query=is%3Aunresolved%20issue.priority%3A%5Bhigh%2C%20medium%5D&referrer=issue-stream&sort=date&stream_index=2
2025-04-18 12:02:36 +00:00
Thomas TrompetteandGitHub 8112c3aab3 Fix workflow run executed by empty (#11642)
Workflow run executed by should not be empty when the workflow is
unamed. Adding Workflow as fallback

<img width="1072" alt="Capture d’écran 2025-04-17 à 19 17 12"
src="https://github.com/user-attachments/assets/e4cc3360-06e5-47e8-ba05-28a0eafab994"
/>
2025-04-18 09:26:51 +00:00
Antoine MoreauxandGitHub 8a2a004fb5 feat(settings): add domain validation effect and delete functionality (#11643)
Introduced a new effect component to validate custom domain DNS records
on mount, centralizing logic. Added a button to reset the custom domain
field, improving user control and form handling. Refactored related code
for maintainability and enhanced UI structure.

Fix https://github.com/twentyhq/core-team-issues/issues/853
2025-04-18 08:18:36 +00:00
oliverGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>Félix MalfaitFélix Malfait
53042cc9dc Date field format display (#11384)
## Introduction

This PR enables functionality discussed in [Layout Date
Formatting](https://github.com/twentyhq/core-team-issues/issues/97).

### TLDR;
It enables greater control of date formatting at the object's field
level by upgrading all DATE and DATE_TIME fields' settings from:

```ts
{
    displayAsRelativeDate: boolean
}
```

to:

```ts

type FieldDateDisplayFormat = 'full_date' | 'relative_date' | 'date' | 'time' | 'year' | 'custom'

{
    displayFormat: FieldDateDisplayFormat
}
```

PR also includes an upgrade command that will update any existing DATE
and DATE_TIME fields to the new settings value

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-04-18 07:00:02 +00:00
Raphaël BosiandGitHub dd0ea2366f 839 Table focus refactoring (#11629)
# Table Focus Refactoring

This pull request implements the table focus refactoring requested in
[#839](https://github.com/twentyhq/core-team-issues/issues/839),
dissociating hover and focus behaviors in the table component to improve
keyboard navigation.

## Technical Implementation

- Created separate component states to handle focus and hover
independently.
- Updated all relevant hooks and functions to use the new focus
mechanism.
- Removed deprecated states and hooks.
- Introduced dedicated portal components to improve the table
performance (the table cells are much simpler and the more complex logic
is handled via the portals)

## Key Behavior Changes

- Performance improvements
- Focus is now handled exclusively with keyboard navigation
- Clicking on a cell or inline-cell now sets the focus to that cell
- Hover state is managed separately from focus, improving user
experience and accessibility
- The table scrolls when the focused cell changes

## Video



https://github.com/user-attachments/assets/9966beac-3b0f-4433-a87a-299506d83353
2025-04-17 16:40:24 +00:00
Thomas TrompetteandGitHub 18a89b5152 Set error on number field and prevent form from being submitted (#11637)
<img width="943" alt="Capture d’écran 2025-04-17 à 18 14 46"
src="https://github.com/user-attachments/assets/1208075e-937f-4224-886c-be578264d448"
/>
2025-04-17 18:39:18 +02:00
WeikoandGitHub 19da80d2e4 Fix logs in integration tests for unhandled exceptions without removing logger (#11635)
Setting a global exception filter for unhandled exceptions to avoid the
default Nest ExceptionsHandler being called (and logging exceptions)
2025-04-17 18:35:21 +02:00
WeikoandGitHub 3fe12cd8b5 Fix select default value not in options (#11622)
Also fixing a bunch of places where validation exceptions were not
properly handled
2025-04-17 18:34:31 +02:00
EtienneandGitHub dd1ac4deee Fix mismatching stripe subscription metadata plan/planKey (#11634)
for later : https://github.com/twentyhq/core-team-issues/issues/867
2025-04-17 18:21:35 +02:00
martmullandGitHub e4150ff3cb Fix logs in integration tests (#11632)
## Before
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/3e8be2e0-ac16-4735-8783-69c800bf0aaf"
/>

## After
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/1a83e7ce-d70a-43c4-a6cd-89dd456d80c1"
/>
2025-04-17 15:05:10 +00:00
Lucas BordeauGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
caf44207fd Implemented filter on FULL_NAME sub-fields (#11628)
This PR implements what's missing to have sub-field filtering.

There is a backend modification to save subFieldName, we just add this
field on view filter workspace entity.

This PR adds subFieldName where missing in frontend, notably in
applyFilter calls, that will be refactored soon.

Also fixes a bug in ViewBar where Add Filter button was at the right
side of the ViewBar, while it should be right after the chips section.

Another bug fixed where we wouldn't delete an empty record filter on
dropdown click outside from the view bar, which was already the case
where using the filter chip dropdown.

<img width="512" alt="image"
src="https://github.com/user-attachments/assets/e9a2f8d2-a66f-4800-853a-4df5c6b627a9"
/>

<img width="495" alt="image"
src="https://github.com/user-attachments/assets/7542697b-0689-4095-9c3c-b5e47875355f"
/>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-17 15:03:56 +00:00
Baptiste DevessierandGitHub 1ba0c24071 Set the page hotkey scope only once (#11626)
The `useEffect` in the `PageChangeEffect` component that sets the page's
hotkey scope is run 4-5 times every time the page changes. It should be
executed only once when the location changes. The re-runs are due to
unstable dependencies that shouldn't re-trigger the `useEffect`.

This PR minimizes the re-runs of the `useEffect`. In the following
video, I discuss a more global pattern.

The documentation for the `useEffectEvent` hook:
https://react.dev/learn/separating-events-from-effects#extracting-non-reactive-logic-out-of-effects.


https://github.com/user-attachments/assets/8c0c238b-f1c4-4bbb-b083-0825f7176599
2025-04-17 16:57:49 +02:00
Antoine MoreauxandGitHub 21c3edf8d6 fix(integration-tests): auth with multiworkspace and skip clickhouse … (#11630)
…tests
2025-04-17 14:49:57 +00:00
EtienneandGitHub a7b75c61e8 fix billing command - add workflow subscription item (#11631)
to migrate user with trialing or paused subscription status with trial
end behaviour 'paused'
2025-04-17 16:23:03 +02:00
f40aafb89f fix: reflect on #10521 changes to order-by-input-factory.ts file (#10662)
# This PR

Fixes an error on the FindMany REST API
I was getting the following error:
```
{
  "statusCode": 400,
  "error": "TypeError",
  "messages": [
    "Cannot read properties of undefined (reading 'fields')"
  ]
}
```
Now, it's working as expected
Related to #10521

cc: @Weiko @ijreilly

---------

Co-authored-by: Weiko <corentin@twenty.com>
2025-04-17 16:06:53 +02:00
42e060ac74 Ws poc (#11293)
related to https://github.com/twentyhq/core-team-issues/issues/601

## Done
- add a `onDbEvent` `Subscription` graphql endpoint to listen to
database_event using what we have done with webhooks:
- you can subscribe to any `action` (created, updated, ...) for any
`objectNameSingular` or a specific `recordId`. Parameters are nullable
and treated as wildcards when null.
  - returns events with following shape
```typescript
  @Field(() => String)
  eventId: string;

  @Field()
  emittedAt: string;

  @Field(() => DatabaseEventAction)
  action: DatabaseEventAction;

  @Field(() => String)
  objectNameSingular: string;

  @Field(() => GraphQLJSON)
  record: ObjectRecord;

  @Field(() => [String], { nullable: true })
  updatedFields?: string[];
```
- front provide a componentEffect `<ListenRecordUpdatesEffect />` that
listen for an `objectNameSingular`, a `recordId` and a list of
`listenedFields`. It subscribes to record updates and updates its apollo
cached value for specified `listenedFields`
- subscription is protected with credentials

## Result

Here is an application with `workflowRun`


https://github.com/user-attachments/assets/c964d857-3b54-495f-bf14-587ba26c5a8c

---------

Co-authored-by: prastoin <paul@twenty.com>
2025-04-17 16:03:51 +02:00
Thomas TrompetteandGitHub b112d06f66 Fix onClick on forms (#11624)
As title
2025-04-17 13:53:40 +00:00
Charles BochetandGitHub 56874bf84b Fix aggregate bar (#11620)
Closes https://github.com/twentyhq/twenty/issues/10943

Also adds stories:
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/377059b1-f6b5-4d8c-b7d1-e74e70448445"
/>
2025-04-17 15:29:20 +02:00
Thomas TrompetteandGitHub d2881bb4a2 Allow workflow field update when custom (#11619)
- Allow workflow field update when custom
- Also handle the case where object name is not defined in actions
2025-04-17 15:01:18 +02:00
GuillimandGitHub 1401f80081 Messaging : better logging (#11621)
Better error logging for messaging import exception handler.

Goal is to have better info on why Unknown errors are thrown and avoid
such messages `Unknown error occurred while importing messages for
message channel XXXXXXXX in workspace YYYYYYYYYY: Unknown error occurred
while importing messages for message channel XXXXXXXX...`
2025-04-17 14:16:05 +02:00
EtienneandGitHub 71dbd1d66b fix: re-init subscription items when user ends his trial period (#11616) 2025-04-17 13:53:37 +02:00
GuillimandGitHub 6e7956b990 logs removal (#11618)
view with Martin, not necessary any longer
2025-04-17 10:01:05 +00:00
martmullandGitHub 0469aba90b Improve from in send email action (#11581)
## Before
<img width="701" alt="image"
src="https://github.com/user-attachments/assets/77184775-5b06-4777-9276-1338cc457070"
/>

## After
<img width="705" alt="image"
src="https://github.com/user-attachments/assets/9528eb0e-54c3-452c-8297-27796ccc75a1"
/>
2025-04-17 11:34:55 +02:00
Antoine MoreauxandGitHub e457ade362 chore(constants): add TrackAnalytics to excluded operations (#11617)
Included TrackAnalytics in the list of excluded middleware operations.
This ensures consistent handling of operations that bypass middleware
processing.
2025-04-17 11:24:45 +02:00
Vaibhav DevereandGitHub 6023bda579 Drop await usage for now-synchronous encodeFileToken() (#11612)
### Remove unnecessary `await` from `encodeFileToken` calls (now
synchronous) #11611

####  Context

In [PR #11385 – commit
26c17f3](https://github.com/twentyhq/twenty/pull/11385/commits/26c17f3205eb0cf4b93dd37cfec97638596ed263),
`FileService.encodeFileToken()` was updated to be a **synchronous**
method. However, several places in the codebase were still calling it
using `await`.

####  Changes 
This PR cleans up those redundant `await` usages to:
- Improve clarity
- Avoid confusion (no longer awaiting a non-Promise)
- Slightly reduce overhead in affected functions
- Removed `await` from calls to `this.fileService.encodeFileToken(...)`
2025-04-17 10:55:54 +02:00
9e9ba73a43 better handling the function argument without touching it (#11614)
leave intact the `input` argument to avoid side effects on the parent
caller

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-04-17 08:54:56 +00:00
nitinandGitHub d89474d43b Fix tab icon alignment issue (#11613)
before:
![Screenshot 2025-04-17 at 13 40
00](https://github.com/user-attachments/assets/b29b3e6b-0185-4289-950c-37a24b63cb65)

after:
![Screenshot 2025-04-17 at 13 39
49](https://github.com/user-attachments/assets/af2efe6f-3908-40d6-acde-d2839a5a8ba2)
2025-04-17 10:29:51 +02:00
GuillimandGitHub b5e6600c73 Capitalize labels objectmetadata (#11609)
Capitalize labels singular and plural from objectmetadata

Fixes [#664](https://github.com/twentyhq/core-team-issues/issues/664)
2025-04-16 21:02:12 +00:00
thomasdnGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
3ad110da33 Added support for podman deployment (#11600)
Added files needed to deploy twenty on podman using podman-compose.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-16 22:08:14 +02:00
Antoine MoreauxandGitHub 587281a541 feat(analytics): add clickhouse (#11174) 2025-04-16 16:33:10 +00:00
b6901a49bf Fix-inline-height (#11608)
Follow up from inline height #11553 related to PR #11442


we had some issues with hover styles

Fixes
https://github.com/twentyhq/twenty/issues/11442#issuecomment-2805893663

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-16 17:49:04 +02:00
e1b99a6f39 Fix Account Owner Dropdown to Display Team Member Profile Pictures #11370 (#11385)
#11370  & #11402
### Changes made:
1. Updated search.service.ts to properly handle workspace member avatar
and Person Avatar URLs with authentication tokens
2. Integrated FileService for token generation
3. Added FileModule to SearchModule for dependency injection

### Implementation details:
- Used getImageUrlWithToken to append authentication tokens to avatar
URLs specifically for workspace members

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-04-16 17:46:37 +02:00
MarieandGitHub 4d78f5f97f [permissions] Improve performances using a cache for userWorkspaces roles (#11587)
In this PR we are 

- introducing a cached map `{ userworkspaceId: roleId } `to reduce calls
to get a userWorkspace's role (we were having N+1 around that with
combinedFindMany queries and generally having a lot of avoidable
queries)
- using the roles permissions cache (`{ roleId: { objectNameSingular:
{ canRead: bool, canUpdate: bool, ...} } `) in Permissions V1's
userHasObjectPermission, in order to 1) improve performances to avoid
calls to get roles 2) start using our permissions cache
2025-04-16 17:07:43 +02:00
Félix MalfaitandGitHub ab277476a8 Remove Sentry fingerprint (#11602)
As discussed this @ijreilly, Fingerprinting is probably not needed and
Sentry will do a better job by itself
2025-04-16 16:25:40 +02:00
nitinandGitHub c95a84c8e5 update KeyValuePairType enum and add IS_CONFIG_VAR_IN_DB_ENABLED config var (#11596)
closes https://github.com/twentyhq/core-team-issues/issues/758
2025-04-16 16:23:57 +02:00
Thomas TrompetteandGitHub 78e10b2da5 Update next step ids on step update (#11605)
When inserting a new step between step 1 et step 2, then step 1 should
have the new step as next step id, add stop having step 2.

When deleting a step, we link the parent and next steps together. It may
change in the future
2025-04-16 15:30:05 +02:00
bf704bd1bc Improve CSV import sub-field selection (#11601)
This PR adds a better UX for selecting sub-fields when importing CSV
files.

Before : 

<img width="395" alt="image"
src="https://github.com/user-attachments/assets/5a599e7d-ed07-4531-8306-9d70e7cfa37d"
/>

After : 

<img width="298" alt="image"
src="https://github.com/user-attachments/assets/2be8a1df-d089-4341-970e-6db2b269141e"
/>

<img width="296" alt="image"
src="https://github.com/user-attachments/assets/584285f4-4e71-4abd-9adf-11819cab0dc5"
/>

- A util `getSubFieldOptionKey` has been made to be able to reference
the sub field in the `options` object of the spreadsheet import.
- New components have been created :
`MatchColumnSelectFieldSelectDropdownContent`,
`MatchColumnSelectSubFieldSelectDropdownContent` and
`MatchColumnSelectV2`
- Extracted the hard-coded option do not import into a constant
`DO_NOT_IMPORT_OPTION_KEY`
- Passed `availableFieldMetadataItems` to spreadsheet global options so
it's available anywhere in the hierarchy of components.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-04-16 15:08:24 +02:00
Baptiste DevessierandGitHub b1c0613514 Fix execution permissions (#11604)
The PR https://github.com/twentyhq/twenty/pull/11400 introduced changes
to the execution permissions of many executable files. These changes
aren't correct and must be reverted.

cc. @charlesBochet
2025-04-16 11:46:37 +02:00
GuillimandGitHub db8a60fe6f more logging to look at calendar errors (#11603)
Adding more logs to look at calendar errors that we have currently

Should help investigation on
https://twenty-v7.sentry.io/issues/5182547220/events/7e8b168887384ede8397e79c15adeb50/?project=4507072499810304
2025-04-16 11:46:02 +02:00
b7cfe89b14 Onboarding doc update (#11599)
Co-authored-by: Weiko <corentin@twenty.com>
2025-04-16 09:55:57 +02:00
madanlal mohanrao nikaljeandGitHub 4c2c9e4273 Refactor webhook event name generation in OpenApiService (#11592)
Fixes - #11589
POT - 
![Screenshot 2025-04-16 at 12 39
11 AM](https://github.com/user-attachments/assets/43802e1a-1ece-4b44-831f-35d70285c5fb)
2025-04-16 08:03:47 +02:00
Charles Bochet 981b1bd767 Fix stories 2025-04-15 23:41:30 +02:00
Charles Bochet 53c6ddb2ad Fix stories 2025-04-15 19:05:14 +02:00
EtienneandGitHub 6f78a51da4 fix query param init before workspace redirection (#11590) 2025-04-15 18:47:26 +02:00
WeikoandGitHub 43af5ceb5e Add object level permission permissions to role page (ReadOnly) (#11568)
## Context
This PR adds the display of object-level permissions. A following PR
will add the ability to update those permissions.
The PR contains the SettingsRoleObjectLevel page but it's not fully
implemented yet (save won't trigger the corresponding mutation)

<img width="616" alt="Screenshot 2025-04-14 at 18 02 40"
src="https://github.com/user-attachments/assets/f8c58193-31f3-468a-a96d-f06a9f2e1423"
/>
2025-04-15 18:46:36 +02:00
Baptiste DevessierandGitHub c23942ce6f Order the workflow run's output properly in the JsonFieldDisplay (#11583)
In this PR:

- Order the workflow run's output in the JsonField Display; the order
should be: error, stepsOutput, flow
- Ensure the special characters are hidden in the JSON visualizer
- Add missing scenarios to Json Tree's stories as it ensures Chromatic
checks them


https://github.com/user-attachments/assets/2ca5ae1d-fdba-4327-bad2-246fd9d23cb9

Closes https://github.com/twentyhq/core-team-issues/issues/804
2025-04-15 18:40:53 +02:00
8bd7b78825 Moved Select Options to External Files (#11400)
This is a minor rework of PR #10738.

I noticed an inconsistency with how Select options are passed as props.
Many files use constants stored in external files to pass options props
to Select objects. This allows for code reusability. Some files are not
passing options in this format.

I modified more files so that they use this method of passing options
props. I made changes to:
- WorkerQueueMetricsSection.tsx 
- SettingsDataModelFieldBooleanForm.tsx 
- SettingsDataModelFieldTextForm.tsx 
- SettingsDataModelFieldNumberForm.tsx 
- PlaygroundSetupForm.tsx 
- ViewPickerContentCreateMode.tsx 

I also noticed that some of these files were incorrectly using
useLingui(), so I fixed the import and usage where needed.

---------

Co-authored-by: Beau Smith <bsmith26@iastate.edu>
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-15 18:31:17 +02:00
EtienneandGitHub 797bb0559a create stripe customer before checking out + update on command (#11578)
two distincts fix in this PR

- add billing threshold for current users (in migration command)
- create stripe customer before checking out in order to enable cloud
user to create multiple workspaces (with associated stripe customer -
closes https://github.com/twentyhq/core-team-issues/issues/852)
2025-04-15 18:02:35 +02:00
Charles BochetandGitHub dee779179b Remove user action from waitFor in stories (#11588)
As per title:
- waitFor is a loop that waits for a condition to be filled, it should
be use to expect or in rare case to wait for element to be present in
the page (in most cases, you can use findByXXX)
- user actions should not be in this loop, otherwise they will be
triggered multiple times
2025-04-15 17:34:28 +02:00
Thomas TrompetteGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
c40baf036c Add command to backfill next step id on version and run steps (#11579)
- add next step id on step
- backfill next step id on step, except for the last one
- backfill flow for workflow run, when it exists

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-15 15:26:58 +00:00
GuillimandGitHub d3922a7f5b removing RecordTableEmptyStateByGroupNoRecordAtAll (#11584)
We found out that `RecordTableEmptyStateByGroupNoRecordAtAll` was used
only when `hasRecordGroups` was true in "RecordTableEmptyState"

However the only parent component is RecordTableEmpty and called
RecordTableEmptyState when `hasRecordGroups` was false

Fixes https://github.com/twentyhq/core-team-issues/issues/833

Meaning never called at all
2025-04-15 16:52:09 +02:00
Charles BochetandGitHub 3c189688d0 Fix stories (#11585)
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/f77a006c-9538-4734-b80e-b58b9f5e7835"
/>
2025-04-15 16:11:31 +02:00
Lucas BordeauandGitHub aa399ca91e Fix a bug that overwrites currentRecordFilters when using URL filtered view (#11525)
This PR fixes a bug that happened when navigating to a filtered view
from a record show page related record section, clicking on 'All'.

The problem was that the QueryParamsFiltersEffect effect component was
overwriting the currentRecordFilter of a different object metadata item
than the current view.

Which when we navigated back on the original view, had emptied the
filters, while they shouldn't change if we only navigate without
refreshing the app.

Fixes https://github.com/twentyhq/core-team-issues/issues/657
2025-04-15 15:32:41 +02:00
GuillimandGitHub 5c23b52c0d multiline CSS aligned to the top (#11582)
Updating CSS for inline cells so that multilie works fine and are
aligned to the top

Fixes
https://github.com/twentyhq/core-team-issues/issues/757?issue=twentyhq%7Ctwenty%7C11495
2025-04-15 13:15:37 +00:00
Charles BochetandGitHub 7bab6cab41 Fix storybook and chromatic tests and fix Block editor custom slash menu positioning (#11580)
Storybook / Chromatic fixes:
- fix linaria twenty-ui style loading (since twenty-ui prebuild
refactoring)
- update prefetch view mocks
- add ability to disable hotkeys initialization in storybook to avoid
race condition

Custom slash menu positioning fix
2025-04-15 14:57:35 +02:00
Murali SinghandGitHub 9da2a74d9e Fix inline field height #11442 (#11553)
Screenshots:

![Image](https://github.com/user-attachments/assets/998f2a88-53ab-49ef-8bb5-c7bef9158b0d)


![Image](https://github.com/user-attachments/assets/218c8f4b-f8d1-4d49-8029-8f99113f01ec)


![Image](https://github.com/user-attachments/assets/1dfde61a-62bb-403a-af3d-fb99c16ab29e)

@Bonapara Can you confirm is this what you expected ?
2025-04-15 14:42:21 +02:00
Charles BochetandGitHub d6ae4d59f0 Various fixes charles 3 (#11573) 2025-04-15 13:45:04 +02:00
Antoine MoreauxandGitHub f30070c2c9 feat(auth): enhance logo behavior and simplify billing logic (#11574)
Added default domain redirection functionality to the Logo component,
leveraging UndecoratedLink for navigation when default logos are used.
Removed metered product billing feature flag logic in the billing
webhook subscription service to simplify and streamline the codebase.

Fix https://github.com/twentyhq/core-team-issues/issues/783
2025-04-15 13:43:13 +02:00
GuillimandGitHub e1ce98825b Possiblity to reconnect on Failed unkown (#11576)
Possiblity to reconnect you account on Failed Unkown errors.

This PR triggers a similar flow than the Failed errors for unsufficient
permission already existing in twenty.

Allows our users to force the synchro again even though they have a
strange error. It's been asked by some customers since we have had a
couple of issues in messaging lately

Fixes https://github.com/twentyhq/twenty/issues/11411
2025-04-15 13:39:18 +02:00
e8db0176a1 Remove recoil sync (#11569)
Recoil-sync was causing issues with Firefox, replacing it with a simpler
mechanism to hydrate variables on page load

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-04-15 13:32:12 +02:00
Baptiste DevessierandGitHub 6c2d64dcb2 Fix e2e tests (#11577) 2025-04-15 12:16:38 +02:00
EtienneandGitHub 8b10059e1b fix - remove isMeteredProductBillingEnabled (#11575) 2025-04-15 11:15:02 +02:00
16fb4d406b i18n - translations (#11572)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-14 18:49:45 +02:00
Raphaël BosiandGitHub 8f2dddaa5f Create stories for action display components (#11547)
Create stories for:
- ActionButton
- ActionComponent
- ActionDisplay
- ActionDropdownItem
- ActionListItem
2025-04-14 18:42:49 +02:00
EtienneandGitHub 0249cf3b34 remove feature flag on billing metered product + fixes (#11570)
- remove feature flag
- design fixes
2025-04-14 18:39:19 +02:00
EtienneandGitHub 9a69cd0b61 add billing threshold + specific trial free credits (#11533)
In this PR : 
- set billing thresholds after subscription creation (not possible
during billing checkout)
- add specific free trial workflow credit quantities + set them in
subscription item + check them when receiving stripe alert event

closes : https://github.com/twentyhq/core-team-issues/issues/682
2025-04-14 16:25:07 +00:00
Baptiste DevessierandGitHub 704b18af30 Create FormRichTextField (#11474)
This PR adds a new Form Field for Rich Text V2 fields. The
implementation was relatively simple, thanks to @thomtrp's job on the
back-end side.

## Demo

### Edit mode + insert in database


https://github.com/user-attachments/assets/f44a6442-fad3-4ad9-af50-a7d90e872c90

### Readonly mode


https://github.com/user-attachments/assets/66243ddf-d4a2-4187-894c-356d9c02b2b2

Closes https://github.com/twentyhq/private-issues/issues/229
2025-04-14 18:07:09 +02:00
Baptiste DevessierandGitHub 0be700f376 Fix e2e tests (#11565)
Fixing the e2e tests
2025-04-14 17:47:57 +02:00
GuillimandGitHub 8a3f8ef324 Chip untitled modification (#11498)
Fixes https://github.com/twentyhq/core-team-issues/issues/663
2025-04-14 15:33:41 +00:00
MarieandGitHub d4deca45e8 Read feature flags from cache (#11556)
We are now storing a workspace's feature flag map in our redis cache. 
The cache is invalidated upon feature flag update through the lab
resolver.
2025-04-14 17:31:13 +02:00
Charles BochetandGitHub 15eb96337f Various fixes (#11567)
In this PR:
- Remove SignUpLoading blank screen by an empty dark overlay =>
VerifyEffect
- Add ModalContent from pages themselves instead of using it the Layout.
This allow for empty dark overlay without showing an empty modal with
padding
2025-04-14 17:24:16 +02:00
85d94822b3 Fix the Command menu items right padding #11484 (#11489)
Screenshot:
![Screenshot 2025-04-10 at 1 07
24 AM](https://github.com/user-attachments/assets/1e7ed8e7-594f-4ead-9b04-0c7dab636fbd)

---------

Co-authored-by: guillim <guigloo@msn.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-04-14 14:12:15 +02:00
Thomas TrompetteandGitHub 69a00eaaf6 Emit message event with actual message object (#11548)
Events were emitted with the wrong object. We were using
messageWithParticipant instead of actual message object
2025-04-14 13:49:19 +02:00
abecdbafc1 Add Edit Rich Text functionality to table view (#11390)
Fixes https://github.com/twentyhq/core-team-issues/issues/729


[recording.webm](https://github.com/user-attachments/assets/ea95d67b-64a3-4fef-91ed-b06318099a78)

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-14 11:46:22 +00:00
Charles BochetandGitHub 8f7a82f177 Fix refactor selectableList broke hotkey onEnter behavior (#11562) 2025-04-14 12:25:54 +02:00
4a123a8eac i18n - translations (#11560)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-14 11:11:30 +02:00
ace515eea7 Fix workflow with manual triggers and no selection not appearing in the command menu (#11544)
Fix workflow with manual triggers and no selection not appearing in the
command menu

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-14 11:03:29 +02:00
Lucas BordeauandGitHub b5a1d1a895 Improving empty filter chip UI (#11537)
This PR is simply removing the : character on the filter chip when the
filter value is empty.

The issue originally was about removing the filter chip when closing the
filter value dropdown with an empty value but it is already the default
behavior.

Fixes https://github.com/twentyhq/core-team-issues/issues/658
2025-04-14 10:43:20 +02:00
EtienneandGitHub 721a34864f create workflow subscription item addition command (#11541)
closes https://github.com/twentyhq/core-team-issues/issues/541
2025-04-14 10:41:37 +02:00
e2b2697fcc refacto dropdown v1>v2 in address input (#11549)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-14 10:37:40 +02:00
0de8140b3a CreateComponentFamilyState -> createComponentFamilyStateV2 (#11546)
Refacto of createComponentFamilyState

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-14 10:31:30 +02:00
Thomas TrompetteandGitHub 27f542e132 Use transformer in workflows (#11497)
We do not manage rich text properly in workflows. This is because API
has a layer called transformer service. Looks a bit as a duplicate of
format data, but this api layer was already there for position anyway.
Using it in workflow record actions.

I hope at some point we merged formatData util and transformer.
2025-04-11 16:31:45 +00:00
Baptiste DevessierandGitHub 897684f995 JSON Visualizer: ensure labels are 24px tall as all other elements (#11535)
## Before

![CleanShot 2025-04-11 at 14 42
15@2x](https://github.com/user-attachments/assets/a4726342-212a-40b3-829f-40cae7724fec)

## After

![CleanShot 2025-04-11 at 14 42
27@2x](https://github.com/user-attachments/assets/e84a4d81-fdcc-422e-b294-55867a4bc5fb)
2025-04-11 18:25:29 +02:00
MarieandGitHub 162c6bcaa3 [permissions] Implement object-records permissions in query builders (#11458)
In this PR we are

- (if permissionsV2 is enabled) executing permission checks at query
builder level. To do so we want to override the query builders methods
that are performing db calls (.execute(), .getMany(), ... etc.) For now
I have just overriden some of the query builders methods for the poc. To
do so I created custom query builder classes that extend typeorm's query
builder (selectQueryBuilder and updateQueryBuilder, for now and later I
will tackle softDeleteQueryBuilder, etc.).
- adding a notion of roles permissions version and roles permissions
object to datasources. We will now use one datasource per roleId and
rolePermissionVersion. Both rolesPermissionsVersion and rolesPermissions
objects are stored in redis and recomputed at role update or if queried
and found empty. Unlike for metadata version we don't need to store a
version in the db that stands for the source of truth. We also don't
need to destroy and recreate the datasource if the rolesPermissions
version changes, but only to update the value for rolesPermissions and
rolesPermissionsVersions on the existing datasource.

What this PR misses
- computing of roles permissions should take into account
objectPermissions table (for now it only looks at what's on the roles
table)
- pursue extension of query builder classes and overriding of their db
calling-methods
- what should the behaviour be for calls from twentyOrmGlobalManager
that don't have a roleId?
2025-04-11 17:34:02 +02:00
martmullandGitHub 82fa71c2cd Update canary package versions (#11543)
as title
2025-04-11 16:48:41 +02:00
WeikoandGitHub 04f9c5a5de Prepare 0.51 (#11542) 2025-04-11 16:00:24 +02:00
Charles BochetandGitHub 3018e3ac89 Fix date picker month/year dropdown (#11539)
Fixes https://github.com/twentyhq/twenty/issues/11524
2025-04-11 15:22:00 +02:00
GuillimandGitHub 2a8defe262 contact/company creations errors (#11540)
Seeing a couple of issues related to company creations in logs, I
suspect this to be the root cause

This should help a lot in all the support we have to do on email
synchronisation
2025-04-11 15:19:03 +02:00
Charles BochetandGitHub 9da5cca82a Fix dropdown width (#11536)
Fix regressions due to dropdown refactoring
2025-04-11 14:59:57 +02:00
Lucas BordeauandGitHub fe98deef4e Fixed command menu hotkey listener refactor (#11534)
This PR fixes a refactor that was done recently to avoid having
clickoutside listeners on the closed command menu.

The useScopedHotkey hook should have stayed in the command menu
container, because it should always listen.

This has been fixed.
2025-04-11 14:59:48 +02:00
03800ce6d0 i18n - translations (#11532)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-11 14:42:45 +02:00
martmullandGitHub 1794b3a611 Remove build lambda at creation (#11531)
Remove useless build call (done at first execution)
2025-04-11 14:40:00 +02:00
Baptiste DevessierandGitHub e8488e1da0 Only display Flow for Workflow Runs and display Output tab for triggers (#11520)
> [!WARNING]
> I refactored a bunch of components into utility functions to make it
possible to display the `WorkflowStepHeader` component for **triggers**
in the `CommandMenuWorkflowRunViewStep` component. Previously, we were
asserting that we were displaying the header in `Output` and `Input`
tabs only for **actions**. Handling triggers too required a bunch of
changes. We can think of making a bigger refactor of this part.

In this PR:

- Only display the Flow for Workflow Runs; removed the Code Editor tab
- Allows users to see the Output of trigger nodes
- Prevent impossible states by manually setting the selected tab when
selecting a node

## Demo

### Success, Running and Not Executed steps


https://github.com/user-attachments/assets/c6bebd0f-5da2-4ccc-aef2-d9890eafa59a

### Failed step


https://github.com/user-attachments/assets/e1f4e13a-2f5e-4792-a089-928e4d6b1ac0

Closes https://github.com/twentyhq/core-team-issues/issues/709
2025-04-11 14:31:34 +02:00
Raphaël BosiandGitHub c8011da4d7 Fix title input hotkey scope (#11530)
Before:


https://github.com/user-attachments/assets/e0be8247-6d3e-49d0-8595-76f9547bf354


After:


https://github.com/user-attachments/assets/ae23b01b-3ad7-4b90-b4ae-ce604d984d73
2025-04-11 14:22:31 +02:00
446919bc72 Remove onboarding flashes + close all dropdowns on CommandMenu close (#11483)
## Remove onboarding flashes

Tested:
- sign in with credentials
- sign in with social oAuth
- sign up with credentials
- multidomain
- single domain
- reset password

No more flashes, and code logic simplified!

## Close all dropdowns on CommandMenu close

Before:


https://github.com/user-attachments/assets/244ff935-3d40-47d5-a097-12d4cc3810dd


After:


https://github.com/user-attachments/assets/1de692f8-5032-404a-be74-025ebca67138

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-11 14:15:14 +02:00
Raphaël BosiandGitHub 637a7f0e64 Fix workflow dropdown hotkey scope (#11518)
The hokey scope was set on focus of the outermost container of the field
inputs. When clicking on a dropdown inside this container, the hokey
scope was set first by the dropdown and then by the focused container.
This led to the previous hotkey scope being overwritten.
The fix consists in moving the hokey scope setter on focus to the inner
container.

Before:


https://github.com/user-attachments/assets/12538c5b-43ab-4b76-a867-7eda6992b1ea


After:


https://github.com/user-attachments/assets/002fedba-010c-41e9-bec6-d5d80cb2dcc5
2025-04-11 09:07:36 +00:00
d34ec7b253 Fix: "Not shared" label not visible due to extra long email subject (#11492)
Closes #11408 

- Added `min-width: 100px` in `EmailThreadNotShared`
- Added `isShared` prop to `StyledSubject` for better subject handling
in shared visibility scenarios

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-11 10:13:33 +02:00
Thomas des FrancsandGitHub 04168e720c Update 0.51.0.mdx (#11517) 2025-04-11 10:12:31 +02:00
nitinandGitHub 0cccb80221 revert: transform -> marginLeft in DefaultLayout motion div (#11522) 2025-04-10 22:33:12 +02:00
Félix MalfaitandGitHub e6bb3d4d51 Change required permission for MS (#11519)
Change User.Read.All to User.Read in docs
2025-04-10 18:33:37 +02:00
Thomas des FrancsandGitHub 5a8006f1dd Update src/content/releases/0.51.0 2025-04-10 18:17:08 +02:00
martmullandGitHub b8406820ee Fix wrong filter for get requests (#11512)
- remove wrong exception filter for GET api requests
- remove messageThreadId requirements on messages for requests done with
API key (no user, only workspace)
- doing the same for calendarEvents
2025-04-10 17:29:53 +02:00
3e1b5d23f5 i18n - translations (#11515)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-10 17:10:55 +02:00
bc8f71fab5 wip: api playground fixes (#11345)
# ISSUE 
- closes #10926 

- [x] fix paddings
- [x] fix page change to playground animation
- [x] fixes multiple skeletons issues

---

- Also was showing multiple skeletons on reloading, **before**: 


https://github.com/user-attachments/assets/0cdef639-c121-4cbb-b056-b89e60862c54

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-10 17:02:55 +02:00
a86317eb71 i18n - translations (#11514)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-10 17:01:13 +02:00
EtienneandGitHub ee5aa2393d fix billingCheckoutSession query param + enable redirect on workspace… (#11509)
… during onboarding



fixes : https://github.com/twentyhq/core-team-issues/issues/668
2025-04-10 16:47:40 +02:00
WeikoandGitHub d69932c6c4 Fix boolean default value update not updating psql default value when false (#11513)
Fixes https://github.com/twentyhq/twenty/issues/11471

## Context
When sending false as a new defaultValue, this was not going through the
migration creation code due to this condition
```typescript
if (updatableFieldInput.defaultValue)
```
2025-04-10 16:23:16 +02:00
Thomas TrompetteandGitHub 6f5118c677 Remove fixed width for dropdown variables, fix optimistic on file upload (#11507)
As title
2025-04-10 16:07:32 +02:00
Lucas BordeauandGitHub f8f11894e8 Introduce generic way to close any open dropdown on page location change (#11504)
In this PR we introduce a generic way to close any open dropdown
idempotently, with the hook useCloseAnyOpenDropdown.

We also introduce a generic hook useExecuteTasksOnAnyLocationChange that
is called each time the page location changes.

This way we can close any open dropdown when the page location changes,
which fixes the original issue of having advanced filter dropdown
staying open between page changes.

Fixes https://github.com/twentyhq/core-team-issues/issues/659
2025-04-10 16:00:50 +02:00
1844c39a99 i18n - translations (#11511)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-10 15:47:28 +02:00
WeikoandGitHub 35c6cb4f4a fix chromatic 1 (#11510) 2025-04-10 15:44:12 +02:00
WeikoandGitHub 63a165666a fix error messages margin (#11508)
## Context
Fixing error messages margin in various places
Reset password
<img width="453" alt="Screenshot 2025-04-10 at 15 17 00"
src="https://github.com/user-attachments/assets/7a0fb090-4029-415f-8733-0c9e3aa3c3f2"
/>
Api key
<img width="543" alt="Screenshot 2025-04-10 at 15 16 21"
src="https://github.com/user-attachments/assets/d8f8a348-84d3-44d7-8cea-863c3ddb02fc"
/>

Also setting overflow on api key label in api keys table
<img width="580" alt="Screenshot 2025-04-10 at 15 16 15"
src="https://github.com/user-attachments/assets/207df981-c906-4926-a56d-38167628267f"
/>
2025-04-10 15:36:59 +02:00
Thomas TrompetteandGitHub b8d5f6d4b0 Center empty state on record table (#11506)
as title
2025-04-10 15:05:49 +02:00
martmullandGitHub 4e4c0ddd3c Add command to update serverless layer (#11500)
New command to add packages to serverless -> Run into your terminal
`npx nx run twenty-server:command serverless:add-packages -p
stripe@17.0.0,@slack/web-api,@slack/oauth`

Will create a new layer folder, add packages in it and update the new
last_layer_version

<img width="1468" alt="image"
src="https://github.com/user-attachments/assets/da8ee77f-e512-4ceb-95bc-cfa979f3439c"
/>

Before
<img width="877" alt="image"
src="https://github.com/user-attachments/assets/35f6bb6c-2ebf-46fe-bd15-4e6352901fc8"
/>

After
<img width="938" alt="image"
src="https://github.com/user-attachments/assets/785af132-5e8c-4a9a-bc57-dffdcaaeec02"
/>
2025-04-10 14:53:59 +02:00
WeikoandGitHub 1802c30995 Fix workspace menu width varies (#11505)
Before


https://github.com/user-attachments/assets/cd386034-bddd-4452-bb7f-2b3749ba74d6

After


https://github.com/user-attachments/assets/07ab40b1-0f8d-4b80-b0db-ad1ea8c584f7
2025-04-10 14:47:40 +02:00
Thomas TrompetteandGitHub 5b1091b19e Store trigger output (#11503)
As title. Trigger output is the workflow payload
2025-04-10 14:36:15 +02:00
cc6043be9f i18n - translations (#11502)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-10 14:11:46 +02:00
145ddba721 fix settings container vertical scroll position (#11464)
@charlesBochet is this flex needed here? we don't use flex-specific
properties.

I did check all scrolls seem to be working fine, could u please check
once more? thanks :)


before:
<img width="1512" alt="Screenshot 2025-04-09 at 12 59 25"
src="https://github.com/user-attachments/assets/1b0066e3-c3f7-4bd8-829b-174d2747ee66"
/>

after:
<img width="1512" alt="Screenshot 2025-04-09 at 12 58 13"
src="https://github.com/user-attachments/assets/06b103ed-d72f-4bc3-b530-7ae25542f568"
/>

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-04-10 14:11:21 +02:00
db88c93eff Navigation drawer refactor (#11251)
closes #11195
closes #11199

### Context

The yellow dots in the Settings Navigation Drawer (used to indicate
advanced settings) were being hidden due to ScrollWrapper's overflow
handling. This required both a fix for the visibility issue and an
improvement to the component structure.

### Changes
1. Keep scrolling logic of the MainNavigationDrawer and
SettingsNavigationDrawer in one place, and conditionally apply
`<StyledScrollableInnerContainer>` when isSettingsDrawer is true.

2. Fixed Yellow Dots Visibility
Added specific padding in NavigationDrawerScrollableContent to
accommodate yellow dots:
```
  padding-left: ${theme.spacing(5)}; // Space for yellow dots
  padding-right: ${theme.spacing(8)}; // Space for no-padding scroll
  ```
  
  This ensures the yellow dots are visible while maintaining proper scroll behavior

3. Improved Component Composition
Using proper component composition instead of passing components as props
Components are now composed in a more React-idiomatic way:
```
  <NavigationDrawer>
    <NavigationDrawerScrollableContent>
      <SettingsNavigationDrawerItems />
    </NavigationDrawerScrollableContent>
    <NavigationDrawerFixedContent>
      <AdvancedSettingsToggle />
    </NavigationDrawerFixedContent>
  </NavigationDrawer>
  ```

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-10 14:05:10 +02:00
WeikoandGitHub 41674129c3 Fix sign-in-up sso buttons width (#11499)
## Context
An enforced width was introduced recently for error texts but broke
Microsoft sso placement due to its longer text.
I've replaced it by a min-width and introduced the missing one in global
signin/up.

Before
<img width="456" alt="Screenshot 2025-04-10 at 12 14 51"
src="https://github.com/user-attachments/assets/43f29c63-f257-4c1c-9c16-b0170343c43e"
/>

After
<img width="494" alt="Screenshot 2025-04-10 at 12 14 17"
src="https://github.com/user-attachments/assets/17c4362a-1336-4adc-aeb9-76c6dcd059c6"
/>
2025-04-10 13:51:21 +02:00
Raphaël BosiandGitHub b84a31cb8a Create open side panel page action (#11481)
Closes https://github.com/twentyhq/core-team-issues/issues/697
2025-04-10 11:34:34 +02:00
Raphaël BosiandGitHub 4a4ce9a6fe Fix PageHeaderToggleCommandMenuButton (#11496)
This PR fixes a bug introduced by
https://github.com/twentyhq/twenty/pull/11470

Before:


https://github.com/user-attachments/assets/8c1b8114-591f-4b58-a177-551c4f0dfb98

After:


https://github.com/user-attachments/assets/b93aa498-2ae8-4001-b541-372866d24f54
2025-04-10 11:29:45 +02:00
Charles BochetandGitHub a7e6564017 Make token update synchronous on FE (#11486)
1. Removing tokenPair internal variable of ApolloFactory. We will relay
on cookieStorage
2. setting the cookie explicitely instead of only relaying on recoil
cookieEffect which is too late
2025-04-10 01:39:25 +02:00
WeikoandGitHub 7bd68ad176 Fix event propagation after record creation (#11485)
Fixes https://github.com/twentyhq/twenty/issues/11460
2025-04-10 01:37:26 +02:00
Lucas BordeauandGitHub 2b77f598b2 Fixed (#11482)
This PR fixes many small bugs around the recent hotkey scope refactor.

- Removed unused ActionBar files
- Created components CommandMenuOpenContainer and
KeyboardShortcutMenuOpenContent to avoid mounting listeners when not
needed
- Added DEFAULT_CELL_SCOPE where missing in some field inputs
- Called setHotkeyScopeAndMemorizePreviousScope instead of
setHotkeyScope in new useOpenFieldInputEditMode hook
- Broke down RecordTableBodyUnselectEffect into multiple simpler effect
components that are mounted only when needed to avoid listening for
keyboard and clickoutside event
- Re-implemented recently deleted table cell soft focus component logic
into RecordTableCellDisplayMode
- Created component selector isAtLeastOneTableRowSelectedSelector
- Drill down hotkey scope when opening a dropdown
- Improved debug logs
2025-04-09 18:34:31 +02:00
GuillimandGitHub 9f4e8c046f Messaging-unit-tests (#11467)
# Unit test on the Messaging Module

Initially the issue was to create integration test for the messaging
module but after speaking with the core team, we decided to go for an
easier implementation: unit test only,

We decided to focus our test on three main components of the module :  
- message list
- message import
- message save & create contact

Fixes https://github.com/twentyhq/core-team-issues/issues/56
2025-04-09 17:57:43 +02:00
Charles BochetandGitHub e23688cb41 Fix lint issues (#11475) 2025-04-09 17:35:25 +02:00
b6c079f050 Fixed Navbar padding issue #11438 (#11470)
@Bonapara 
Related Issue: Fixes #11438.

Screenshot:
![Screenshot 2025-04-09 at 4 10
41 PM](https://github.com/user-attachments/assets/4c5346b6-4e1c-4441-bd8a-b88aee139b9b)

---------

Co-authored-by: guillim <guigloo@msn.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-04-09 17:22:49 +02:00
Thomas TrompetteandGitHub 5d9cd257ae Command menu mobile - Prevent overflow on navigation bar (#11480)
Fix https://github.com/twentyhq/twenty/issues/11413
2025-04-09 17:16:46 +02:00
Thomas TrompetteandGitHub 025e50645d Avoid settings object cover image to be reduced on scroll (#11473)
Fix https://github.com/twentyhq/twenty/issues/11420
2025-04-09 16:43:25 +02:00
Raphaël BosiandGitHub 9e0402e691 Action menu refactoring (#11454)
# Description

Closes [#696](https://github.com/twentyhq/core-team-issues/issues/696)

- `useAction` hooks have been removed for all actions
- Every action can now declare a react component
- Some standard action components have been introduced: `Action`,
`ActionLink` and `ActionModal`
- The `ActionDisplay` component uses the new `displayType` prop of the
`ActionMenuContext` to render the right component for the action
according to its container: `ActionButton`, `ActionDropdownItem` or
`ActionListItem`
- The `ActionDisplayer` wraps the action component inside a context
which gives it all the information about the action
-`actionMenuEntriesComponenState` has been removed and now all actions
are computed directly using `useRegisteredAction`
- This computation is done inside `ActionMenuContextProvider` and the
actions are passed inside a context
- `actionMenuType` gives information about the container of the action,
so the action can know wether or not to close this container upon
execution
2025-04-09 13:12:49 +00:00
Lucas BordeauandGitHub 1834b38d04 Fix show page relation record count (#11459)
This PR fixes the incorrect relation count on a show page relation
section title, when there are more than 60 records.

An aggregate COUNT query has been used to rely on the backend.

A new component RecordDetailRelationSectionDropdown has been created to
abstract a chunk of the parent RecordDetailRelationSection component.

Fixes https://github.com/twentyhq/twenty/issues/11032
2025-04-09 14:41:11 +02:00
nitinandGitHub bd3ec6d5e3 rename core-module environment to twenty-config (#11445)
closes https://github.com/twentyhq/core-team-issues/issues/759
2025-04-09 14:11:26 +02:00
MarieandGitHub fe6d0241a8 [permissions] Allow logo update during sign-up (#11468)
Fixes https://github.com/twentyhq/twenty/issues/11410
2025-04-09 13:58:30 +02:00
c6203d8928 i18n - translations (#11469)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-09 11:34:35 +02:00
8582bcccf5 remove billing stripe plan product id from env (#11461)
Fixes https://github.com/twentyhq/core-team-issues/issues/661
remove billing stripe plan product id from env

Co-authored-by: Keshav Gupta <keshavgupta@Keshavs-MacBook-Air.local>
2025-04-09 11:31:38 +02:00
EtienneandGitHub 11fb8e0284 add metered products usage (#11452)
- add metered products usage module on settings/billing page
- add new resolver + logic with meter event data fetching from Stripe

<img width="590" alt="Screenshot 2025-04-08 at 16 34 07"
src="https://github.com/user-attachments/assets/34327af1-3482-4d61-91a6-e2dbaeb017ab"
/>
<img width="570" alt="Screenshot 2025-04-08 at 16 31 58"
src="https://github.com/user-attachments/assets/55aa221a-925f-48bf-88c4-f20713c79962"
/>

- bonus : disable subscription switch from yearly to monthly

closes https://github.com/twentyhq/core-team-issues/issues/681
2025-04-09 09:26:49 +00:00
Thomas des FrancsandGitHub b25ee28c12 Update CONTRIBUTING.md (#11465)
Updated the issue assignment policy for good first issues to prioritize
PRs over Issue assignments.
2025-04-09 11:16:13 +02:00
Thomas TrompetteandGitHub 46bbf02431 Emit event on message creation (#11466)
adding event emitting on message creation
2025-04-09 11:04:43 +02:00
GuillimandGitHub c6be260fec datasource TTL fix (#11444)
Comparing the last date a datasource was used instead of a fixed TTL :
should fix workers issues "error: Error: Connection terminated"

FYI was done in pair prog with @Weiko
2025-04-09 10:08:20 +02:00
ccdc2835a2 i18n - translations (#11463)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-09 01:20:17 +02:00
b6e344e7be Various fixes (#11448)
# Scrollbar fix

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

<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/b13fe0f2-8c61-4ea8-9ea1-e61e571a90da"
/>

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-04-09 01:03:43 +02:00
ab63214efa Added Proper theme color for Favorite Input box (#11434)
#11418  The background color changes based on the theme.
 - If the theme is 'light', the background color will be white.
- Otherwise, it will use the theme's background transparent lighter
color.

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
2025-04-08 20:46:36 +00:00
martmullandGitHub f121c94d4a 701 workflow improve webhook triggers (#11455)
as title

Nota bene: I did not filter execution by http method. A POST webhook
trigger can be triggered by a GET request for more flexibility. Tell me
if you think it is a mistake


https://github.com/user-attachments/assets/1833cbea-51a8-4772-bcd8-088d6a087e79
2025-04-08 19:01:22 +00:00
2f7f28a574 i18n - translations (#11457)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-08 18:37:10 +02:00
Baptiste DevessierandGitHub 6521d19238 Use JSON visualizer for JSON fields (#11428)
- Updates on the JSON field input
  - Previously, we were editing json fields in a textarea 
- Now, we display a JSON visualizer and the user can click on an Edit
button to edit the JSON in Monaco
- The JSON field input has a special behavior for workflow run output.
We want the error to be displayed first in the visualizer. Displaying
the error in red was optional but makes the output clearer in the
context of a workflow run record board.
- Made the code editor transparent in the json field input
- Ensure workflow run's output is not considered readonly in
`packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts`;
we want the json visualizer to always be displayed in this specific case

## Demo

### Failed Workflow Run


https://github.com/user-attachments/assets/7a438d11-53fb-4425-a982-25bbea4ee7a8

### Any JSON field in the record table


https://github.com/user-attachments/assets/b5591abe-3483-4473-bd87-062a45e653e3

Closes https://github.com/twentyhq/core-team-issues/issues/539
2025-04-08 16:18:36 +00:00
EtienneandGitHub c1d421de06 remove default meter driver (#11456)
Remove default meter driver to prevent metric logs from polluting the
console.
2025-04-08 18:17:58 +02:00
Thomas TrompetteandGitHub dd1d9c32c3 Remove task, note and file tabs for workflows (#11453)
Command menu tries to fetch task, file and notes that are not available
for workflow entities. Hiding those tabs
2025-04-08 17:35:41 +02:00
Thomas TrompetteandGitHub a049cd4a25 Update placeholder on record picker object type update (#11451)
- requires a refacto so several fields can be updated at once
- updating object name on record picker will now update placeholder
- add a min-height to label so fields do not get moved when the label is
deleted
2025-04-08 14:20:15 +00:00
6611fbf9ac Fix : Menu shouldn't be displayed on logged out pages (#11437)
#11414 
Conditionally render MobileNavigationBar based on user authentication
status
- Added useIsLogged hook to check if the user is authenticated.
- Updated MobileNavigationBar component to render only when the user is
logged in.
![Screenshot from 2025-04-08
12-33-04](https://github.com/user-attachments/assets/1b58a5ac-e5ff-418f-a903-b952f37db84d)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-08 11:54:21 +02:00
Charles BochetandGitHub 17474a0e1e Remove cell hotkey scope (#11435)
Remove FieldContext hotkey scope as:
- each Field should set its own hotkey scope (ex:
emails-field-input-{recordId} or emails-field-input for now)
- while opening a fieldInput, we should synchronously set the
corresponding hotkey scope

To cut this refactoring in half, I'm allowing all input to use
TableHotkeyScope.CellEditMode
2025-04-08 11:18:23 +02:00
3d90eb4eb9 Fix broken dropdown auto resize behavior (#11423)
This PR was originally about fixing advanced filter dropdown auto resize
to avoid breaking the app main container, but the regression is not
limited to advanced filter dropdown, so this PR fixes the regression for
every dropdown in the app.

This PR adds a max dropdown max width to allow resizing dropdowns
horizontally also, which can happen easily for the advanced filter
dropdown.

In this PR we also start removing `fieldMetadataItemUsedInDropdown` in
component `AdvancedFilterDropdownTextInput` because it has no impact
outside of this component which is used only once.

The autoresize behavior determines the right padding-bottom between
mobile and PC.

Mobile : 

<img width="604" alt="Capture d’écran 2025-04-07 à 16 03 12"
src="https://github.com/user-attachments/assets/fbdd8020-1bfc-4e01-8a05-3a9f114cdd40"
/>

PC :

<img width="757" alt="Capture d’écran 2025-04-07 à 16 03 30"
src="https://github.com/user-attachments/assets/f80a5967-8f60-40bb-ae3c-fa9eb4c65707"
/>

Fixes https://github.com/twentyhq/core-team-issues/issues/725
Fixes https://github.com/twentyhq/twenty/issues/11409

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-08 11:03:10 +02:00
Félix MalfaitandGitHub 89abf3db4f Remove dead code to backfill record positions (#11439)
Fixes https://github.com/twentyhq/core-team-issues/issues/767
2025-04-08 11:02:40 +02:00
8b5c259c36 i18n - translations (#11441)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-08 11:01:29 +02:00
ea93ac6348 fetch latest version tag from docker hub (#11362)
closes #11352

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-08 08:55:03 +00:00
eliasylonenandGitHub 95eba07f6e Share RICH_TEXT_V2 field value override between REST and GraphQL APIs (#10912)
Fixes issue #10606.

This PR makes `RICH_TEXT_V2` field behavior in REST API matche the
current behavior in GraphQL API:

Currently both `markdown` and `blocknote` fields must be included in the
request, one of them can be `null`. The field with a `null` value will
be filled by the converted value of the other field.

In other words, this works:
```
curl http://localhost:3000/rest/notes \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzQxODA1MzQyLCJleHAiOjQ4OTU0MDUzNDEsImp0aSI6ImZlMzU0NTBkLTlhMDMtNGE2ZS04ODVjLTBlNTU3M2Y3YTE0NiJ9.6_g8cwoSE7ZCX1Zzsw44gZIyBdLKNsnDqMOmm1bKik0' \
  --data '{
  "position": 1,
  "title": "a",
  "bodyV2": {    
    "markdown": "test4\n\ntest3\n\n# test1\n",
    "blocknote": null
  },
  "createdBy": {
    "source": "EMAIL"
  }
}'
```

And this does not work:
```
curl http://localhost:3000/rest/notes \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzQxODA1MzQyLCJleHAiOjQ4OTU0MDUzNDEsImp0aSI6ImZlMzU0NTBkLTlhMDMtNGE2ZS04ODVjLTBlNTU3M2Y3YTE0NiJ9.6_g8cwoSE7ZCX1Zzsw44gZIyBdLKNsnDqMOmm1bKik0' \
  --data '{
  "position": 1,
  "title": "",
  "body": "",
  "bodyV2": {    
    "markdown": "test4\n\ntest3\n\n# test1\n"
  },
  "createdBy": {
    "source": "EMAIL"
  }
}'
```

---

It would be nice not to require the null value, maybe let's make that a
separate PR?
2025-04-07 23:30:53 +02:00
6bc18960c9 Show Header in RecordTable on empty state and show groups in Group By views (#11416)
Closes #11298

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-07 22:05:59 +02:00
07b25a2aad i18n - translations (#11432)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-07 19:33:05 +02:00
Charles BochetandGitHub 20895f1129 Fix views reordering (#11431)
As per title
2025-04-07 19:18:05 +02:00
Thomas TrompetteandGitHub 17b7e703b4 Refacto form select input (#11426)
- small fixes on form design
- refacto form select input to use the existing select component
2025-04-07 16:56:59 +00:00
WeikoandGitHub ff59658d39 Fix permission check during onboarding (#11429)
## Context
CurrentUser is fetched during onboarding however roles and permissions
are not created yet during that stage so an error was thrown. We only
want to fetch permissions after the onboarding of the workspace.
2025-04-07 18:53:53 +02:00
WeikoandGitHub f403c551d7 Add settings permissions check on FE (#11425)
## Context
Now that we can update role settings permissions, we need to reflect
that on the FE as well (hiding/showing nav items + redirection logic).
Feature flag check here is not really needed because since not having
any setting permission will result in the same behavior as Permission
V1.
This PR updates the resolvers to return settings permissions of the
current user
2025-04-07 17:15:33 +02:00
Charles Bochet d4db399933 Fix Google Icon 2025-04-07 17:14:36 +02:00
0226dec717 i18n - translations (#11427)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-07 17:13:37 +02:00
GuillimandGitHub 6451b7b5e3 Fix-messaging-calendar-issues (#11424)
- fixing Event Calendar bug due to TypeORM limitations
- avoid systematic crash on contact creation jobs, simply logging
2025-04-07 17:13:13 +02:00
Paul RastoinandGitHub 69da046812 Twenty-website copy twenty-ui dockerfile (#11394)
Fixes deployment


https://github-production-user-asset-6210df.s3.amazonaws.com/45004772/430425195-218d6f61-3ab0-4042-8206-52eac22fe599.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAVCODYLSA53PQK4ZA%2F20250404%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250404T143735Z&X-Amz-Expires=300&X-Amz-Signature=489088bc5bf23c371d80d655c83a61e8ba481313b368369785c203b7268dd924&X-Amz-SignedHeaders=host
2025-04-07 17:09:55 +02:00
GuillimandGitHub c798cccba7 Switching plan overflows (#11396)
Swithcing plan overflows the modal soo Bonapara said to go back to
simple switch plan until we have a better version next week with the
stirpe pricing page
2025-04-07 17:09:29 +02:00
Charles BochetandGitHub 59ae978ee3 Fix safari broken table (#11417)
In this PR:
- deprecate usage of useInView (react-intersection-observer) for record
table
- fixes #11254
2025-04-07 16:33:02 +02:00
26504f02a3 i18n - translations (#11422)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-07 15:39:19 +02:00
EtienneandGitHub 361b7682dd add trial period ending banner + server logic (#11389)
Solution : 
- if user reaches his workflow usage cap + in trial period > display
banner
- end trial period if user has payment method (if not, not possible)

<img width="941" alt="Screenshot 2025-04-04 at 10 27 32"
src="https://github.com/user-attachments/assets/d7a1d5f7-9b12-4a92-a7c7-15ef8847c790"
/>
2025-04-07 15:28:02 +02:00
nitinandGitHub a627eff5c2 fix: Use namePlural instead of labelPlural for favorite view URLs (#11407)
closes #11404

Used `namePlural` instead of `labelPlural` for generating favorite view
URLs to ensure proper routing across all languages.
2025-04-07 08:54:43 +02:00
Lucas BordeauandGitHub f365caa365 Fixed ViewBarDetails unnecessary padding-top (#11395)
This PR fixes a recent change in ViewBarDetails that added an
unnecessary padding-top.

Before : 

<img width="876" alt="image"
src="https://github.com/user-attachments/assets/f0152f24-da2c-44f7-b299-a98cb559b2d1"
/>

After : 

<img width="884" alt="image"
src="https://github.com/user-attachments/assets/48a0c9d8-1c22-4eb7-abfa-02963facbcd6"
/>
2025-04-07 07:37:19 +02:00
Charles BochetandGitHub 8fa09e21ec Fix safari broken table (#11399)
Fixes #11254
2025-04-04 19:16:33 +02:00
241d8820d7 i18n - translations (#11398)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-04 17:47:01 +02:00
WeikoandGitHub e1f6c61651 add settings permissions update (#11377)
Fixes https://github.com/twentyhq/core-team-issues/issues/710
2025-04-04 17:40:14 +02:00
6142e193ce i18n - translations (#11397)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-04 17:34:18 +02:00
10e140495c Fixing Singup sequence FLASHING💥 (#11371)
After investiagting the different options ([see related
issue](https://github.com/twentyhq/core-team-issues/issues/660#issuecomment-2766030972))
I decided to add a "Verify Component" and a to build a custom Layout for
this route.

Reason I cannot use the default one is to have all preloaded once the
user changes website and lands on the verify route.

Reason I did not modify the DefaultLayout to match our need is that is
would require many changes in order to avoid preloading states for our
specific usecase.

Fixes https://github.com/twentyhq/core-team-issues/issues/660

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-04-04 15:25:15 +00:00
Charles Bochet 609e06fd14 Fix main build 2025-04-04 16:21:23 +02:00
2308091b13 Remove overlay-scroll-bar (#11258)
## What

- Deprecate overlayscrollbars as we decided to follow the native
behavior
- rework on performances (avoid calling recoil states too much at field
level which is quite expensive)
- Also implements:
https://github.com/twentyhq/core-team-issues/issues/569

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-04-04 16:13:48 +02:00
Thomas TrompetteandGitHub 6b184cc641 Select node by default when pending form action (#11378)
We want the run side panel to be open when the form step is pending



https://github.com/user-attachments/assets/a7a7015e-b0b7-422a-b625-eca8f2614ac1
2025-04-04 15:56:57 +02:00
Lucas BordeauandGitHub 59e8e0633b Fix advanced filter dropdown input components (#11381)
This PR fixes the filter value input components that are used in
advanced dropdown filter, which slightly differ from the classic object
filter dropdown in the view bar.

We notably needed the same experience as other text inputs in the
application, for entering filter values.

## New text and number filter experience : 

<img width="681" alt="image"
src="https://github.com/user-attachments/assets/b373bb6b-dc00-4396-9294-9b866b91fa02"
/>

## New date filter experience : 

<img width="683" alt="image"
src="https://github.com/user-attachments/assets/8aea22e2-6f3b-4641-9f3d-6d7ba537bc27"
/>

To obtain the same experience for date input as in workflow forms, it
would require to duplicate or factorize a lot of complex code that
manipulates dates and user events with the input, it would be better
tackled in another issue related to a larger refactor effort :
https://github.com/twentyhq/core-team-issues/issues/736

Fixes https://github.com/twentyhq/core-team-issues/issues/674
2025-04-04 12:45:21 +00:00
Raphaël BosiandGitHub ad2357a6fd Fix error when workflows aren't enabled (#11388)
Fix Uncaught Error: Workflow is not enabled. If you want to use it,
please enable it in the lab.
<img width="505" alt="erroractionmenu"
src="https://github.com/user-attachments/assets/66e60219-20fb-4b00-90e4-d6bd640be774"
/>

This error was due to using the hook `useWorkflowWithCurrentVersion`
outside of a workflow object. Adding a skip parameter wasn't enough
because the `useFindOneRecord` uses `useObjectMetadataItem` which throws
if workflows aren't enabled.
2025-04-04 14:26:19 +02:00
27256c960e i18n - translations (#11387)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-04 11:33:11 +02:00
Baptiste DevessierandGitHub 9353e777ea Copy JSON values on click (#11382)
https://github.com/user-attachments/assets/1638c196-fb9c-4f2b-910c-6d8b0693a37a

Closes https://github.com/twentyhq/core-team-issues/issues/568
2025-04-04 11:20:57 +02:00
WeikoandGitHub bb40bc9929 Fix update field default value (#11386)
Fixes https://github.com/twentyhq/twenty/issues/11379
2025-04-04 08:58:53 +00:00
Lucas BordeauGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>Félix Malfait
5e43839efb Fix CSV import select field matching (#11361)
This PR fixes a bug that prevented to do the matching of an imported CSV
file that contains a SELECT type column.

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

## Stacking context improvement 

During the development it was clear that we lacked a reliable way to
understand our own z indices for components like modal, portaled
dropdown, overlay background, etc.

So in this PR we introduce a new enum RootStackingContextZIndices, this
enum allows to keep track of our root stacking context component
z-index, and because it is an enum, it prevents any conflict.

See
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Stacking_context
for reference.

## Component cleaning

Components have been reorganized in a SubMatchingSelectRow component

The Dropdown component has been used to replace the SelectInput
component which doesn't fit this use case because we are not in a cell,
we just need a simple standalone dropdown, though it would be
interesting to extract the UI part of the SelectInput, to share it here,
the benefit is not obvious since we already have good shared components
like Tag and Dropdown to implement this specific use case.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-03 16:28:15 +02:00
EtienneandGitHub 752eb93836 disable workflow execution if billing issue (#11374)
closes https://github.com/twentyhq/core-team-issues/issues/404
2025-04-03 14:18:44 +00:00
7eec64b6e0 have footer on emails (#11300)
# ISSUE 

- Closes #9622

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-03 14:26:19 +02:00
EtienneandGitHub cfae440a02 add stripe alert listening and cap on subscriptionItems (#11330)
in this PR :  
- reverting https://github.com/twentyhq/twenty/pull/11319 > at trial
period end, subscriptions switch to 'past_due' status if payment method
not set up
- adding cap on subscriptionItems and updating them when receiving alert
event + refreshing them when beginning a new subscription cycle

closes https://github.com/twentyhq/core-team-issues/issues/606
2025-04-03 13:44:32 +02:00
Baptiste DevessierandGitHub 144a326709 JSON visualizer: Highlight the parent nodes of in-use nodes (#11373)
https://github.com/user-attachments/assets/5f31023d-b24f-40c8-a061-ffc0d02b63b0

Closes https://github.com/twentyhq/core-team-issues/issues/715
2025-04-03 12:11:19 +02:00
Paul RastoinandGitHub 4a4e65fe4a [REFACTOR] Twenty UI multi barrel (#11301)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.

## TODO
- [x] Manual migrate twenty-website twenty-ui imports

## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them

### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app

Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
2025-04-03 09:47:55 +00:00
MarieandGitHub 8c9fcfe5a4 Hot fix for currentWorkspaceMember not found error on blocklist page (#11367)
Fixes
https://twenty-v7.sentry.io/issues/6504066204/?project=4507072563183616
Introduced by https://github.com/twentyhq/twenty/pull/11325

Called it a hotfix as not sure if missing currentWorkspaceMember is an
expected state - maybe it is expected to be temporarily missing ?
2025-04-03 08:44:11 +00:00
MarieandGitHub 0df07a766a Handle no value options in filters (#11351)
Fixes #11323 - see description to reproduce

The issue was that the filter was malformed for no value options: 
<img width="649" alt="Capture d’écran 2025-04-02 à 14 56 25"
src="https://github.com/user-attachments/assets/5287c4f8-7eaf-4488-b692-4d7634236d3d"
/>
causing
<img width="333" alt="Capture d’écran 2025-04-02 à 14 56 43"
src="https://github.com/user-attachments/assets/aa1b7333-50da-4b7d-979b-70dab9a1ab41"
/>


after fix: 
<img width="653" alt="Capture d’écran 2025-04-02 à 14 39 56"
src="https://github.com/user-attachments/assets/1777c068-7231-4e14-bc41-84ef7909cf10"
/>
2025-04-03 10:41:50 +02:00
Thomas TrompetteandGitHub 256a5c1a2b Remove form feature flag (#11369)
As title. Workflows are still in the lab and forms are starting to be
usable
2025-04-03 08:38:19 +00:00
Raphaël BosiandGitHub 1240d39f56 Introduce hooks to retrieve directly the registered actions without using actionMenuEntriesComponentState (#11359)
Introduce two hooks:
- `useRegisteredRecordActions`
- `useRegisteredRecordAgnosticActions`

These hooks will be used to read directly the registered actions
according to the context.
We will stop to rely on `actionMenuEntriesComponentState` to improve
performances and reduce state copies and updates.

This PR is part of
https://github.com/twentyhq/core-team-issues/issues/683, and at this
step, we still save the actions inside
`actionMenuEntriesComponentState`.
2025-04-03 10:25:42 +02:00
Thomas TrompetteandGitHub a062a17229 Add form date field (#11360)
Builder
<img width="498" alt="Capture d’écran 2025-04-02 à 19 02 20"
src="https://github.com/user-attachments/assets/29db9fa9-aae4-4d1f-98f2-0b2371f944f1"
/>

Execution
<img width="837" alt="Capture d’écran 2025-04-02 à 19 02 47"
src="https://github.com/user-attachments/assets/ce1c442c-3c06-4f7e-99d6-3eb8fb1d2428"
/>
2025-04-03 10:10:43 +02:00
GuillimandGitHub 8abec309e0 Phone-onclickoutside (#11350)
Fixes : For phones I have to press "enter" to validate my changes but
for other fields it's saved automatically when I leave the cell

Bug related to onClickOutside on the MultiItemFieldMenuItem component
that shows phones (but also emails...)

Seen with @bonapara : we keep a consitent behaviour meaning
- saving input on click outside when primary item is being edited
- not saving input on click outside when other items are being edited



Fixes https://github.com/twentyhq/twenty/issues/11246
2025-04-03 07:57:02 +00:00
Baptiste DevessierandGitHub bea75b9532 Set failed node's output as red (#11358)
| Error | Success |
|--------|--------|
| ![CleanShot 2025-04-02 at 18 18
45@2x](https://github.com/user-attachments/assets/6674d4d2-344a-4e16-9608-a70cde07a376)
| ![CleanShot 2025-04-02 at 18 20
23@2x](https://github.com/user-attachments/assets/55b5a467-528f-4f07-9166-40ed14943ee2)
|

Closes https://github.com/twentyhq/core-team-issues/issues/716
2025-04-03 08:58:56 +02:00
183dc40916 [FIX] Out of memory while running app localy (#11341)
# Introduction
Lately encountering a lot of out of memory error when running
twenty-front in watch mode with both TypeScript and lint checkers
```ts
Error: Worker terminated due to reaching memory limit: JS heap out of memory
    at new NodeError (node:internal/errors:405:5)
    at [kOnExit] (node:internal/worker:287:26)
    at Worker.<computed>.onexit (node:internal/worker:209:20)
```

The existing configuration looks like this:
```ts
// packages/twenty-front/vite.config.ts
'cd ../.. && eslint packages/twenty-front --report-unused-disable-directives --max-warnings 0 --config .eslintrc.cjs',
```
This is wrong as computing the root eslintrc completely omitting
twenty-front's one ***and its ignorePattern*** so will be checking in
`node_modules` etc checking for project-structure :).
For example this a
[snippet](https://gist.github.com/prastoin/d7f8ad4ef5eb2f7732209b756a38094c)
of the above commands errors. We can see rule that should be disabled by
`eslintrc.react.cjs` extension made from twenty-front `eslintrc` :
```ts
/Users/paulrastoin/ws/twenty-two/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/__stories__/SettingsDataModelFieldSettingsFormCard.stories.tsx
  23:27  warning  Forbidden non-null assertion  @typescript-eslint/no-non-null-assertion
```

## Fixes
- consume the `twenty-front` package eslint configuration within the
vite lint checker
- eslint overrides extends are getting merged based on glob inclusion of
their files declarations
- any linted files should be included in one of our `tsconfig`
- removed redundant and counter-productive negative `ignorePatterns`, as
eslint will naturally only lint files within configuration file
directory by default which will result making it go through local
`node_modules` project structure

## Now
Less cpu usage <3.5 gb and faster
```ts
// from packages/twenty-front
TIMING=1  npx eslint . --report-unused-disable-directives --max-warnings 0 --config .eslintrc.cjs --debug
#...
Rule                                            | Time (ms) | Relative
:-----------------------------------------------|----------:|--------:
project-structure/folder-structure              | 19578.927 |    20.2%
prettier/prettier                               | 13746.156 |    14.2%
no-redeclare                                    |  9546.570 |     9.9%
@nx/workspace-explicit-boolean-predicates-in-if |  8167.805 |     8.4%
@typescript-eslint/no-unused-vars               |  6872.803 |     7.1%
import/no-relative-packages                     |  6577.273 |     6.8%
@nx/enforce-module-boundaries                   |  6520.945 |     6.7%
import/no-duplicates                            |  4987.476 |     5.2%
react/no-direct-mutation-state                  |  2323.082 |     2.4%
react/require-render-return                     |  1155.261 |     1.2%
```

## Conclusion
Please note that `nx linter` might not be as strict as vite config
eslint runner

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-04-02 18:35:00 +02:00
010b7663c4 i18n - translations (#11357)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-02 18:21:16 +02:00
Raphaël BosiandGitHub 5e905180c8 690 extract shouldberegistered from the useaction hooks (#11355)
Closes https://github.com/twentyhq/core-team-issues/issues/690

This PR is the first part of a refactoring on the actions system
https://github.com/twentyhq/core-team-issues/issues/683. It:
- Removes `shouldBeRegistered` from the useAction hook
- Instead `shouldBeRegistered` becomes a function to which we can pass
parameters which describe the context of the app (the object, the
selected record, information about favorites ...). It returns a boolean.
- `useShouldActionBeRegisteredParams` returns the parameters to pass to
the `shouldBeRegistered`
- Introduces a way to inherit actions from the default config and to
overwrite its properties, closing
https://github.com/twentyhq/core-team-issues/issues/72

Some tests testing if an action was registered correctly have been
removed, we should add them back at the end of the global refactoring.
2025-04-02 17:58:14 +02:00
Antoine MoreauxandGitHub bc57e98949 chore(ci): extend timeout for Danger JS job (#11356)
Increased the timeout for the Danger JS job from 3 to 5 minutes to
prevent premature failures on slower runs. This ensures more reliable
execution of the workflow.

To unlock https://github.com/twentyhq/twenty/pull/11339
2025-04-02 17:26:52 +02:00
Thomas TrompetteandGitHub 7488c6727a Add record picker in form action (#11331)
Record picker becomes a form field that could be used in another context
than workflows.

Settings
<img width="488" alt="Capture d’écran 2025-04-02 à 10 55 53"
src="https://github.com/user-attachments/assets/a9fc09ff-28cd-4ede-8aaa-af1e986cda8e"
/>

Execution
<img width="936" alt="Capture d’écran 2025-04-02 à 10 57 36"
src="https://github.com/user-attachments/assets/d796aeeb-cae1-4e59-b388-5b8d08739ea8"
/>
2025-04-02 17:08:33 +02:00
EtienneandGitHub 2bc9691021 fix stripe subscription deletion (#11353)
when soft deleting workspace, stripe subscription is canceled then
workspace is soft deleted before stripe async web hook event is
received. This webhook event is needed to update billingSubscription
status.
2025-04-02 15:59:09 +02:00
2b14cd5649 i18n - translations (#11354)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-02 15:47:16 +02:00
c03438a02e Support Moved to Security Setting (#11335)
This PR addresses issue #11321 by moving the "Support" section from the
General tab to the Security tab.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-02 13:40:56 +00:00
Félix MalfaitandGitHub e94065fb44 Auto-run migration script on docker entrypoint (#11338)
A small PR but a big step towards making Twenty easier to self-host and
upgrade!

Now changing the tag and pulling a new version should be the only step
to upgrade as migrations script will be ran automatically upon starting
the containers. It was already the case for typeorm migrations, but not
for standard objects migration and data migration scripts. It is still
possible to disable this behavior for the most complex deployments such
as our own cloud.
2025-04-02 15:37:51 +02:00
martmullandGitHub 55eadf1ab4 11312 rest api cant filter enum type columns by null value (#11343)
- Fixes https://github.com/twentyhq/twenty/issues/11312
- Fixes record undefined on webhook deleted events
- Emit destroy event when deleting record via the rest api
2025-04-02 15:07:53 +02:00
Paul RastoinandGitHub 76bbffc315 [FIX] Upgrade command missmatch (#11348)
# Introduction
As we deploy patch from the main branch we've ship the upgrade for the
`0.51` within the `0.50`.
We should only do that when about to release

We should find a way for this not to occur again
2025-04-02 11:58:50 +00:00
EtienneandGitHub da1413c5a1 fix hot reload (#11347) 2025-04-02 11:42:05 +00:00
martmullandGitHub c6afb0d1ba Revert "[4/n]: migrate the RESTAPI GET /rest/* to use TwentyORM direc… (#11344)
…tly (#10372)"

This reverts commit a26b3f54d6.
2025-04-02 13:39:28 +02:00
Lucas BordeauandGitHub e47c19e86f Advanced filter bug bash (#11327)
This PR fixes some bugs on advanced filters dropdown.

See reference master issue :
https://github.com/twentyhq/core-team-issues/issues/648

Fixes https://github.com/twentyhq/core-team-issues/issues/726
Fixes https://github.com/twentyhq/core-team-issues/issues/724
Fixes https://github.com/twentyhq/core-team-issues/issues/655
2025-04-02 10:25:51 +02:00
martmullandGitHub 6e92b19e01 11311 object create webhook should be triggered when object is created via rest api (#11336)
Fixes https://github.com/twentyhq/twenty/issues/11311
2025-04-02 10:22:22 +02:00
GauravandGitHub 9a2f7d8b71 Fix: Timeline responsiveness (#11288)
Fixes #11136


[recording.webm](https://github.com/user-attachments/assets/328afab8-511a-4090-bdb0-e1cdb42565be)
2025-04-02 10:07:05 +02:00
Antoine MoreauxandGitHub 44bf393b06 fix(email-validation): validation email cross workspace (#11261)
- Finish removing captcha guard from validation  email
- Fix an issue where domain validation wasn't working on a multi-domain
setup
2025-04-02 08:06:34 +00:00
44d4e4efaf fix: Display multipicker on Created By source filter (#10543)
fix: #9674 

When the selected filter is Is and the field type is ACTOR, we now show
ObjectFilterDropdownSourceSelect instead of a text input. This ensures
proper selection for actor-based filters. Also previously, when a user
changed the selected option, all default values were getting wiped out
so i have made some changes in updatedSelectedItems

Loom video:
https://www.loom.com/share/8b80a78d5e8c4a5192d69cab96f0c838?sid=5bd76fff-7cd4-4e3c-8c48-d1c9e4e95651

---------

Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-04-02 09:56:32 +02:00
WeikoandGitHub 3357f93415 Bump 0.51.0-canary (#11329) 2025-04-02 09:23:17 +02:00
MarieandGitHub e6422ad08a Only show a workspace member their own email blocklist (#11325)
Fixes https://github.com/twentyhq/twenty/issues/11320
2025-04-01 16:23:37 +00:00
martmullandGitHub 8e6b175f34 700 fix rest api issues (#11326)
fixes issues listed here
https://github.com/twentyhq/core-team-issues/issues/700
2025-04-01 17:22:24 +02:00
51ea241a1c Fix mobile table styling outside record table (#10407) (#10663)
Fix to issue #10407 now including a fix to `ChipFieldDisplay` [throwing
an error outside a record
table](https://twenty-v7.sentry.io/issues/6350031213/?project=4507072563183616&referrer=github-pr-bot).

This works now, but refactoring is needed before merging.

Fixes #10407

---------

Co-authored-by: ad-elias <elias@autodiligence.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-04-01 17:11:31 +02:00
WeikoandGitHub 8385e2d08b Fix object metadata collection not found (#11306)
## Context
This fix ensures that even if a datasource creation promise throws and
is cached, subsequent requests won't return that cached exception.
Also adding a TTL on MetadataObjectMetadataOngoingCachingLock, this is
not something that should stay in the cache forever and could
potentially unlock some race conditions (the origin of the issue is
probably due to performances where the lock is not removed as it should
be after metadata computation and caching)
2025-04-01 16:38:43 +02:00
Thomas TrompetteandGitHub 7e07b3f7e9 Fix dropdown height and scroll in workflows (#11324)
Before
<img width="459" alt="Capture d’écran 2025-04-01 à 15 58 41"
src="https://github.com/user-attachments/assets/8f2183b3-cbc4-4e03-97f7-e9edc719af35"
/>

After
<img width="459" alt="Capture d’écran 2025-04-01 à 15 58 28"
src="https://github.com/user-attachments/assets/00ef0ffe-bfc8-429c-942a-c02f6c52d91d"
/>
2025-04-01 14:11:06 +00:00
EtienneandGitHub 1d4fc5ff4a update subscription with metered products at trial ending (#11319)
Context
- Subscription with metered prices can't be 'paused' at the end of
trialing period
- Currently, pausing subscription have been the process we choose at
Twenty

Two solutions : 
- [x] (The chosen one!) Adding metered products when the trial period is
ended.
- [ ] Switching from 'paused' to 'past_due' status at the end of
trialing period. Tricky because we should handle different cases of
'past_due' subscription status, some causing workspace suspension and
some other not.

closes https://github.com/twentyhq/core-team-issues/issues/676
2025-04-01 15:57:01 +02:00
Thomas TrompetteandGitHub e74c8723d0 Add position in run / version + block creation from generated API (#11318)
- Add position during workflow version / creation. It will allow to have
the versions and runs ordered
- Block the creation from generated api for versions. We use workflow
post hooks or create from draft
2025-04-01 15:29:54 +02:00
Félix MalfaitandGitHub aa5da92555 Improve upsert for spreadsheet import (#11283)
Refactor query runner to improve the import method for upserts, we now
take into account any unique field and prevent any conflict upfront.
Previously, we would only update if an `id` was passed.



https://github.com/user-attachments/assets/8087b864-ba42-4b6e-abf2-b9ea66e6c467


This is only a first step, there are other things to fix on the frontend
for this to work.
2025-04-01 15:27:08 +02:00
MarieandGitHub 366106bb2b Fix field metadata creation page (#11285)
in this pr 
1. fixing error helper was no longer showing. unfortunately had to
resort to `formConfig.trigger`...
2. closes https://github.com/twentyhq/twenty/issues/11262
3. closes https://github.com/twentyhq/twenty/issues/11263


https://github.com/user-attachments/assets/11f763bb-3098-4b0e-bc96-8a0de3cb3c19
2025-04-01 14:09:24 +02:00
GuillimandGitHub 9cbc2e3df0 Option-menu-imporovements (#11309)
- icon switching : if switching layout from table to kanban
and if icon is the table icon (the default one) then automatically
update the icon to the kanban icon

- add tooltip on Layout default view to better explain why it's
unavaialble


Fixes [#689](https://github.com/twentyhq/core-team-issues/issues/689)
Fixes [#688](https://github.com/twentyhq/core-team-issues/issues/688)
Fixes [#686](https://github.com/twentyhq/core-team-issues/issues/686)
2025-04-01 10:31:40 +00:00
bc81853095 i18n - translations (#11317)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-01 12:01:27 +02:00
Thomas TrompetteandGitHub 023d071103 Set record position on workflow creation (#11308)
- Migrate record position factory to core-modules 
- set position on record creation
2025-04-01 09:50:43 +00:00
Félix MalfaitandGitHub b2012229f4 Display system objects in Workflow triggers (#11314)
Fixes #11310

Note:
- I changed "system to advanced" because I think we will link that to
the Advanced Mode in the future
- Not sure when to use useCallback / useMemo, AI added some useCallbacks
which I removed but I left some useMemo... Not sure what should be the
rule
- It had to use MenuItem because this sub-menu behavior wasn't available
in the Standard select component. We should probably rename the
"MenuItem" elements to something more generic. I didn't do it in this PR
because I'm not sure about the strategy and it would change a lot of
files.
2025-04-01 11:48:52 +02:00
a26b3f54d6 [4/n]: migrate the RESTAPI GET /rest/* to use TwentyORM directly (#10372)
# This PR

- Addressing #3644
- Migrates the `findOne` and the `findMany` Rest API to use TwentyORM
directly
- Adds integration tests to the migrated methods

---------

Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: martmull <martmull@hotmail.fr>
2025-04-01 11:34:43 +02:00
Raphaël BosiandGitHub a24e96ae0e 685 Move all navigation actions inside the actions configs (#11303)
Move all navigation actions inside the actions configs
2025-04-01 11:23:28 +02:00
Paul RastoinandGitHub f6e4cd45ed [CHORE] Update browserslist (#11304)
# Introduction
```sh
npx update-browserslist-db@latest
```
https://github.com/browserslist/update-db#readme
2025-03-31 18:12:38 +02:00
1c675149f7 i18n - translations (#11305)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-31 18:01:13 +02:00
WeikoandGitHub 06ff16e086 add role update (#11217)
## Context
This PR introduces the new Create and Edit role components, behind the
PERMISSIONS_ENABLED_V2 feature flag.
2025-03-31 17:57:14 +02:00
Paul RastoinandGitHub 3c9bf2294f test(front): fix main failing test due to timezone (#11295)
# Introduction
Due to winter to summer timezone update, it shown that we have some unit
tests flakiness due to both mocked and unmocked date.now invokation
between app bootstrap and test bootstrap

This PR does not refactor this behavior
Just fix the currently failing test

## Note
Removed a duplicated file
2025-03-31 15:39:05 +02:00
Lucas BordeauandGitHub b13be7bd2e Advanced filter fix placeholder and icon in dropdown buttons (#11286)
This PR fixes a UI issue that brings a lot more robustness to the
advanced filter look and feel.

It adds the icon of the field metadata item in the filter field select.

It adds a custom placeholder in the filter input select, depending on
the field metadata item type.

<img width="661" alt="image"
src="https://github.com/user-attachments/assets/8bf2044f-52cf-447d-909d-3312089c0df5"
/>
2025-03-31 08:32:04 +00:00
Lucas BordeauandGitHub 98475ee63e Advanced filter UI fast follow-ups (#11272)
This PR fixes the issue about the easy fast follow-up part on advanced
filter.

Fixes https://github.com/twentyhq/core-team-issues/issues/675

Changes : 
- Changed horizontal gap to spacing(1) for AdvancedFilterDropdownRow
- Created a DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET for all
sub-dropdowns in advanced filter dropdown with a y-offset of 2px.
- Created a DropdownOffset type
- Used a padding-left of spacing(2.25) in
AdvancedFilterLogicalOperatorCell to allign the disabled text with the
text in the Select component
- Added IconTrash and accent danger on
AdvancedFilterRecordFilterGroupOptionsDropdown and
AdvancedFilterRecordFilterOptionsDropdown
- Removed unnecessary CSS properties on
AdvancedFilterRootRecordFilterGroup
- Set dropdownMenuWith to 280 for AdvancedFilterValueInputDropdownButton
- Fixed Dropdown generic clickable component container that was
expanding
- Set IconFilter instead of IconFilterCog in AdvancedFilterChip
- Set AdvancedFilterDropdownButton dropdown content width to 650 instead
of 800
- Refactored generic IconButton component so that it disambiguates
secondary and tertiary variant for the color CSS props
2025-03-31 10:24:52 +02:00
Antoine MoreauxandGitHub eea30828a4 test(user-workspace): add unit tests for UserWorkspaceService + review #11239 (#11256) 2025-03-30 08:37:16 +02:00
GuillimandGitHub ce07d2645c Renaming + remove setRecoilvalue (#11281)
Simple renaming of a hook plus a deletion of a useless setRecoilValue
hook.

pretty strainghtforward
2025-03-28 18:59:47 +01:00
Raphaël BosiandGitHub 352cf3d38e Update previousHotkeyScopeState to be a family state (#11270)
Fixes #11259
2025-03-28 17:59:05 +01:00
3e20134676 Add command to update createdBySource enum values (#11280)
Fixes webhook create workflowRun by WEBHOOK error

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2025-03-28 16:53:14 +00:00
GuillimandGitHub d9ac662302 Microsoft Throtling error and wrongly types on our side (#11266) 2025-03-28 16:37:46 +00:00
8e39585c0d i18n - translations (#11271)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-28 16:45:59 +01:00
859ed1d8dd Feature: Update record right click menu (#11252)
closes #11193


[recording.webm](https://github.com/user-attachments/assets/2c89f50a-546b-4047-8b5a-69f6588cabe4)

---------

Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
2025-03-28 16:31:43 +01:00
EtienneandGitHub da9e078283 fix no billing subscription error (#11257)
closes https://github.com/twentyhq/core-team-issues/issues/625

Context : 
Soft-deleted workspaces no longer have any subscriptions. Computing
inactivity causes an error.
2025-03-28 14:45:13 +00:00
3e5a6d0a5e i18n - translations (#11260)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-28 11:20:53 +01:00
Raphaël BosiandGitHub b1c57edc90 Display a tooltip for actions without short labels (#11243)
- Merge `RecordIndexActionMenuButtons` and `RecordShowActionMenuButtons`
into a single component `PageHeaderActionMenuButtons`
- Display tooltip after 1s for actions without short labels


https://github.com/user-attachments/assets/7649bed8-a1a9-4c1d-8fbe-f1bf3a51db56
2025-03-28 11:01:55 +01:00
Thomas TrompetteandGitHub 8d35454dd8 Form action fast follows (#11242)
- on form action, add trash on hover and fix spacing
- fix dark mode
- second batch of icon fixes

<img width="692" alt="Capture d’écran 2025-03-27 à 18 25 48"
src="https://github.com/user-attachments/assets/963f50d2-12ee-425c-97a3-2ad0c6bb5c57"
/>
2025-03-28 10:11:01 +01:00
Paul RastoinandGitHub 78943b3370 chore(website): start docs (#11253) 2025-03-28 10:02:51 +01:00
vaibhav-devereGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>Charles BochetCharles Bochet
8127e4f635 Fix filter button height (#11237)
[#11218](https://github.com/twentyhq/twenty/issues/11218) Fixed Filter
Reset Button height

- Replaced the default button with LightButton to align with Figma
design specs which Restores expected button size and appearance.

- Ensured the correct height, padding, and styling are applied
automatically.

- Wrapped the button inside StyledChipcontainer for consistency.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-03-28 09:28:05 +01:00
c294d967a3 reset main context store viewId and current view type on settings page (#11206)
closes https://github.com/twentyhq/core-team-issues/issues/588

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-03-28 09:26:59 +01:00
EtienneandGitHub 391392dd87 set up metrics collecting with open telemetry (#11236)
Done :  
- move metrics and health cache services from health module to metrics
module
- refactor metrics counter from specific method to set up from enum keys
- add OpenTelemetry (Otel) instrumentation for metrics
- set up Otel SDK to send metrics to Otel collector

To do later : 
- implement Otel instrumentation for traces + plug Sentry on top
2025-03-28 08:45:24 +01:00
Félix MalfaitandGitHub e9e33c4d29 Refactor spreadsheet import (#11250)
Mostly renaming objects to avoid conflicts (it was painful because names
were too generic so you could cmd+replace easily)

Also refactoring `useBuildAvailableFieldsForImport`
2025-03-28 07:56:51 +01:00
Antoine MoreauxandGitHub 9af2628264 feat(auth): enhance email validation when no workspace available + disable captcha on email validation (#11239)
Implemented fallback logic to associate a user with a workspace when
none is found. Introduced new GraphQL types and mutations for roles and
permissions management. Simplified and refactored URL-building logic for
email verification, improving code maintainability and flexibility.
2025-03-28 07:38:58 +01:00
WeikoandGitHub 976c6afb4b Generate more random subdomains (#11249)
Sorted the 2 existing lists + added a new colors list to generate the
random subdomain to have less collision

<img width="368" alt="Screenshot 2025-03-27 at 19 25 28"
src="https://github.com/user-attachments/assets/6d6d27f1-134b-4ae7-88a6-4af506115317"
/>
2025-03-27 19:40:20 +01:00
Charles BochetandGitHub a683827e4b Fix inline cell height issue and allow field settings update (#11248)
In this PR:
- allow to update settings on fields metadata (regression introduced by
a recent refactoring of fields-metadata update)
- revert changes introduced by
https://github.com/twentyhq/twenty/pull/11221
2025-03-27 19:29:34 +01:00
MarieandGitHub e201da263d Fix sync-workspace log (#11247)
This is now log for every workspace we sync so stopped making sense
2025-03-27 18:17:11 +00:00
Paul RastoinandGitHub 226b8f1b00 [CHORE] Remove preconstruct local patch (#11153)
# Introduction
Please find related parent PR
https://github.com/twentyhq/twenty/pull/11083

Custom TypeScript configuration file should be available on "2.8.12" !
See related changeset version dump PR
https://github.com/preconstruct/preconstruct/pull/619
2025-03-27 18:19:46 +01:00
c19f6c3479 0.50 (#11241)
changelog :)

---------

Co-authored-by: Marie Stoppa <marie.stoppa@essec.edu>
2025-03-27 18:07:37 +01:00
65bf836e8b i18n - translations (#11240)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-27 17:36:51 +01:00
7508f09ede i18n - translations (#11235)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-27 17:21:26 +01:00
MarieandGitHub d12bbdf9f1 Move MigrationRelations command out of 0.50 -> 0.51 (#11234) 2025-03-27 16:06:52 +00:00
Raphaël BosiandGitHub f145862cf1 Design fixes on show page (#11221)
- Fixes record inline cell height
- Fixes notes body empty state
2025-03-27 17:03:06 +01:00
Thomas TrompetteandGitHub 439ccb0a52 Fix workflow placeholders and icons (#11222)
- allow all form fields that may need a placeholder to set it
- update main icons on versions and runs
2025-03-27 17:01:33 +01:00
4eefa45164 Option-menu-switch-table-kanban (#11167)
New options menu feature: table/kanban switching

This feature is the last one of the Optino Menu v2 update. It is
designed to enable the possibility to switch from table to kanban and
vice-versa.

Only the default tab is not editable for consitency in the UX of the
application as designed by our team

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-03-27 16:49:44 +01:00
Lucas BordeauandGitHub 937a393d74 Fix missing positionInRecordFilterGroup in applyFilter legacy logic (#11230)
This PR fixes a bug where modifying a filter in the advanced filter
dropdown was making it change its position in the filter list.

This is due to the legacy logic of applyFilter(), which outlines the
importance of the needed refactor to remove this logic which requires
duplicating code modification in nearly every component that can modify
a record filter.

This refactor will be addressed over the next sprints, because there are
underlying sub-refactors to tackle first, as outlined by
https://github.com/twentyhq/core-team-issues/issues/559
2025-03-27 16:40:30 +01:00
GuillimandGitHub 3b9e7c38eb better error handling for messaging import in case of unknown error (#11228) 2025-03-27 16:19:57 +01:00
Lucas BordeauandGitHub 645d76fe69 Removing advanced filter feature flag (#11219)
This PR removes the advanced filter feature flag.
2025-03-27 15:25:07 +01:00
d4c40113b1 i18n - translations (#11216)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-27 09:01:24 +01:00
Félix MalfaitandGitHub a545b3adf3 CSV Import translations and dark mode fixes (#11184)
Fixes #11182 and adds missing translations
2025-03-27 08:53:00 +01:00
fad02fa26b i18n - translations (#11215)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-27 00:01:02 +01:00
5bf21a9567 i18n - translations (#11214)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 23:45:52 +01:00
Charles BochetandGitHub 22e56339a1 Fix field options not updatable anymore (#11213)
As per title
2025-03-26 23:40:44 +01:00
589b4cf77e fix: View switcher should close after view switch (#11154)
# Issue

- closes #11147

Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
2025-03-26 23:02:05 +01:00
2cc59929b8 Fixed the 'Extra space on GroupBy option menu' (#11179)
#10969 

Fixed!


![image](https://github.com/user-attachments/assets/4784891c-2c49-4098-8d1b-a8b52fdec1f6)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-03-26 22:59:52 +01:00
d02b5b4086 fix: adjust padding for icon button in Data model visualizer (#11180)
Fixes #10958 


![image](https://github.com/user-attachments/assets/4d9105c9-1bdb-4cb1-801c-56cb1cac0cce)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-03-26 22:53:51 +01:00
6f7e6e63f8 fix: improve settings menu spacing by moving logout into last section (#11202)
Issue: https://github.com/twentyhq/twenty/issues/11194

- Remove separate navigation drawer section for logout button
- Move logout button into the last section of settings menu
- Fix visual spacing between menu items

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-03-26 22:10:12 +01:00
4c7d71e325 chore(analytics): remove tinybird (#11146)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-03-26 21:59:42 +01:00
e64c4b8ab1 Feature/add flexibility to docker image makefile (#11210)
Trying to build the images for different build platforms using the
Makefile isn't possible without changing the code.

This change exposes the PLATFORM and TAG variables to enable the user to
build and tag the images with greater flexibility.

The same TAG variable can be used on the prod-*-run targets too.

Postgres is not built in the same way but is run in the Makefile too, so
the TAG variable applied here too, but not the platform.

---------

Co-authored-by: Paul McKeown <paul@midships.io>
2025-03-26 21:48:47 +01:00
Paul RastoinandGitHub 870069f682 [CHORE] Enable workspace version validation on upgrade for next release (#11211)
# Introduction
- Activate workspace version validation on upgrade command
- Update upgrade command to manager next release starting from `0.44.0`
2025-03-26 21:47:39 +01:00
Charles BochetandGitHub 5bd10d40cb Fix deactivate objects impacts (#11185)
In this PR:
- Remove deactivated objects from ActivityTargetInlineCell record picker
- Prevent users to deactivate createdAt, updatedAt, deletedAt fields on
any objects

Still left:
- write unit tests on the assert utils
- write integration tests on field metadata service
- prevent users to deactivate createdAt, updatedAt, deletedAt on FE
2025-03-26 20:45:46 +01:00
90e884d33f i18n - translations (#11209)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 19:37:26 +01:00
151841d3db i18n - translations (#11207)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 19:21:48 +01:00
d8ab23046a i18n - translations (#11204)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 18:33:33 +01:00
c48fed6173 i18n - translations (#11201)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 18:20:15 +01:00
Thomas TrompetteandGitHub b3948d2406 Add selected number to title (#11200)
Fixes https://github.com/twentyhq/core-team-issues/issues/586

<img width="633" alt="Capture d’écran 2025-03-26 à 17 37 10"
src="https://github.com/user-attachments/assets/2215aa95-1f74-4634-b76e-2b9322230fe8"
/>
2025-03-26 18:03:42 +01:00
Raphaël BosiandGitHub 1860768b52 Create 'Hide deleted records' action (#11198)
This PR:
- Makes 'See deleted records' action unavailable when the deleted filter
is active
- Creates 'Hide deleted records'



https://github.com/user-attachments/assets/b45f8ead-d973-418d-afba-a5983ac85172
2025-03-26 17:59:59 +01:00
760a21d8fa i18n - translations (#11197)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 17:37:16 +01:00
Raphaël BosiandGitHub 3660bec01d New TitleInput UI component for side panel (#11192)
# Description

I previously introduced the `RecordTitleCell` component, but it was
coupled with the field context, so it was only usable for record fields.
This PR:
- Introduces a new component `TitleInput` for side panel pages which
needed to have an editable title which wasn't a record field.
- Fixes the hotkey scope problem with the workflow step page title
- Introduces a new hook `useUpdateCommandMenuPageInfo`, to update the
side panel page title and icon.
- Fixes workflow side panel UI
- Adds jest tests and stories

# Video



https://github.com/user-attachments/assets/c501245c-4492-4351-b761-05b5abc4bd14
2025-03-26 17:31:48 +01:00
MarieandGitHub 4827ad600d Add workspace constraint on defaultRoleId and activationStatus (#11191)
Part of https://github.com/twentyhq/core-team-issues/issues/526

An active workspace's defaultRoleId should never be null.
We can't rely on a simple postgres NOT NULL constraint as defaultRoleId
will always be initially null when the workspace is first created since
the roles do not exist at that time.
Since a suspended workspace can be active again, we should maintain that
rule during the workspace suspension. The only moment defaultRoleId can
have a null value is during the onboarding. this pr enforces that
2025-03-26 14:43:13 +00:00
fe3dd40034 i18n - translations (#11190)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 15:20:30 +01:00
MarieandGitHub 7af90eb4c4 [permissions V2] Custom role deletion (#11187)
Closes https://github.com/twentyhq/core-team-issues/issues/616
2025-03-26 15:08:48 +01:00
WeikoandGitHub 16cb768c5c Do not suspend onboarding workspaces after stripe hook and add cron to delete them after 7 days (#11189)
## Context
When a trial ends, we receive a webhook from stripe to switch a
workspace from its previous status to SUSPENDED.
We also have some workspaces in ONGOING_CREATION that started the flow,
started the trial but did not finish completely the flow which means the
workspace has no data/metadata/schema.
Because of this, we can have workspaces that switch from
ONGOING_CREATION to SUSPENDED directly and ONGOING_CREATION workspaces
that didn't start the trial can stay in this state forever since they
are not cleaned up by the current cleaner.
To solve those 2 issues, I'm adding a new cron that will automatically
clean ONGOING_CREATION workspaces that are older than 7 days and I'm
updating the stripe webhook to only update the activationStatus to
SUSPENDED if it's already ACTIVE (then it will be deleted by the other
cron in the other case)
2025-03-26 14:35:11 +01:00
MarieandGitHub 72b4b26e2c [permissions] Enable permissions V1 for all workspaces (#11172)
Closes https://github.com/twentyhq/core-team-issues/issues/526

(for reminder: 
1. Make defaultRoleId non-nullable for an active workspace
2. Remove permissions V1 feature flag
3. Set member role as default role for new workspaces

About 1.:
An active workspace's defaultRoleId should never be null.
We can't rely on a simple postgres NOT NULL constraint as defaultRoleId
will always be initially null when the workspace is first created since
the roles do not exist at that time.

Let's add a more complex rule to ensure that

About 3.:
In the first phase of our deploy of permissions, we chose to assign
admin role to all existing users, not to break any existing behavior
with the introduction of the feature (= existing users have less rights
than before).

As we deploy permissions to all existing and future workspaces, let's
set the member role as default role for future workspaces.
)
2025-03-26 12:51:34 +00:00
0f7adedc96 i18n - translations (#11188)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 13:42:53 +01:00
f55a20addc [ENH] UseActionMenuEntries optimization (#11183)
# Introduction
Dynamically set in remove actions only if map has given key
Thanks @charlesBochet for the class on Recoil !

Related to https://github.com/twentyhq/twenty/issues/11079

## Conclusion
As always any suggestions are welcomed !

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-03-26 13:19:08 +01:00
Félix MalfaitandGitHub c36cfb3af9 Fix no timeline for opportunities (#11186)
Fixes #11176
2025-03-26 12:35:59 +01:00
80074bc21b i18n - translations (#11181)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-26 11:20:43 +01:00
Lucas BordeauandGitHub 8cc9f77417 Advanced filters bug bash (#11178) 2025-03-26 11:06:53 +01:00
7d6573e766 i18n - translations (#11177)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-25 22:31:15 +01:00
Félix MalfaitandGitHub 6ec06be18d Overwrite standard translations (#11134)
Manage overwriting translations for standard fields and standard objects
properties
2025-03-25 22:17:29 +01:00
7a7003d859 i18n - translations (#11175)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-25 19:21:40 +01:00
EtienneandGitHub 934abf1fb3 fix billing issues (#11160)
- first commit : fix quantity update on Enterprise plan
- second commit : fix user with soft deleted workspace trying to
recreate another workspace

closes https://github.com/twentyhq/core-team-issues/issues/634
2025-03-25 19:09:36 +01:00
Antoine MoreauxandGitHub 20f2a251b6 fix(auth): handle missing workspaceId in auth service (#11173)
Add a check to return undefined if workspaceId is not provided. Prevents
unnecessary execution and potential errors during workspace lookup.
2025-03-25 17:58:53 +00:00
Thomas TrompetteandGitHub 3e7ec4f756 Allow empty string for primary link (#11171)
Fixes https://github.com/twentyhq/twenty/issues/11166



https://github.com/user-attachments/assets/72208ac9-c19d-4c19-b305-40d6318310ed
2025-03-25 18:17:38 +01:00
ddd439031f i18n - translations (#11170)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-25 17:21:08 +01:00
nitinandGitHub 2182821fe1 fixed workspace tabs not working in admin panel (#11164)
fixed a regression on user lookup workspace tabs.
regression caused after this merge
https://github.com/twentyhq/twenty/pull/11008
2025-03-25 17:09:17 +01:00
Paul RastoinandGitHub a58831978f [FIX] Compute custom datname in entrypoint.sh (#11148)
Close https://github.com/twentyhq/twenty/issues/10928
2025-03-25 16:29:47 +01:00
Raphaël BosiandGitHub b24046b1bb Various design fixes on side panel (#11165)
- Fix background color
- Fix Menu Item height
- Fix Input design
- Fix show page summary card design
2025-03-25 15:21:10 +00:00
martmullandGitHub 45b8a330c6 Fix lambda iam (#11163)
As title, fix lambda driver for serverless functions
2025-03-25 15:57:04 +01:00
b70aa6f2ee i18n - translations (#11155)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-25 15:21:06 +01:00
GuillimGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
877d6e9304 Option-menu-v2-input (#11116)
Adding the possibility to change the view name and incon from the
Options menu dropdown

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-03-25 15:13:13 +01:00
acead6169c i18n - translations (#11152)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-25 13:52:56 +01:00
Raphaël BosiandGitHub f3f4dd7648 Fix record page title focus (#11145)
Fix record page title focus when creating a new record.

Before:


https://github.com/user-attachments/assets/3f032873-ef5b-4799-a6b7-6459674e4347


After:


https://github.com/user-attachments/assets/7ca4107d-cea8-477d-9e6a-2b74c3121a13
2025-03-25 11:54:29 +01:00
Paul RastoinandGitHub d601213c21 [FIX] twenty-shared build on window even wsl (#11130)
# Introduction
Handles both '/' and '\' by using native path api
2025-03-25 11:44:32 +01:00
GuillimandGitHub 96c61df007 fix MailboxConcurrency And email format without @ (#11133) 2025-03-25 11:40:58 +01:00
Lucas BordeauandGitHub de1f8a4706 Fix created by filter dropdown that appears on other filter types. (#11126)
Fixes https://github.com/twentyhq/twenty/issues/11077

This PR fixes a bug where the actor source filter dropdown input was
showing on other field types filter dropdown input.

It just adds a check to show this particular actor source filter
dropdown input only if the field type is ACTOR.

Sub field name reset and lifecycle will be handled in the incoming work
on sub field filtering.
2025-03-25 11:26:39 +01:00
MarieandGitHub 4680bc740a [permissions V2] Upsert object and setting permissions (#11119)
Closes https://github.com/twentyhq/core-team-issues/issues/639
2025-03-25 11:07:51 +01:00
Antoine MoreauxandGitHub 54e346a2aa fix(auth): prevent workspace creation on signup with password with no authorized user + fix 10982 (#11128)
Updated the method to properly fetch and return workspace data with
related approved access domains. This ensures the correct handling of
workspace lookup and fixes potential issues with undefined returns.

Fix #10982
2025-03-24 20:24:58 +01:00
Raphaël BosiandGitHub fd23a6e5ff Update delete record message (#11132)
Update delete record message
2025-03-24 20:22:05 +01:00
52cf6f4795 Allow to edit labels of standard objects (#10922)
Fixes #10793

This PR is a work in progress.

**Still left to fix:**

- [x] When disabling synchronization of labels / api names, the edited
labels should be set to the English version. Currently the client just
send the localized versions together with the `isLabelSyncedWithName`
change. Could be an easy fix.
- [ ] Sometimes flipping the switch don't trigger the update function,
may be a regression as it seems to affect the custom objects too.
- [ ] There is a frontend problem where the labels inputs don't reflect
the changes made. When enabling back synchronisation after editing
labels, they are correctly back to their base values (backend,
navigation breadcrumb, etc) but the label inputs still have the old
values (switching pages will put them back to normal). I suspect this
could be linked to the above problem.
- [ ] API names are still displayed for standard objects per (kept them
for debugging, trivial fix)
- [ ] `SettingsDataModelObjectAboutForm` have a `disableEdition`
parameter which is now used only for a few fields, not sure if it's
worth keeping because it's a bit misleading since it doesn't "disable"
much?
- [ ] I don't know what these do, but I have seen "Remote" object types.
Not sure if they work with my patch or not (I don't know how to test
them)
- [ ] Make it work with metadata synchronisation


**What should work:**

- Disabling synchronization of standard objects should work, label
inputs should no longer be disabled
- Modifying labels should work
- Enabling back synchronization should reset back the labels to the base
value and disable the label inputs again (minus the mentioned display
bug)
- The synchronisation switch should still work as expected for custom
objects
- Creating custom objects should still work (it uses the same form)

---------

Signed-off-by: AFCMS <afcm.contact@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-03-24 20:19:52 +01:00
Raphaël BosiandGitHub bc1b55ddc3 Add see runs and see workflows actions (#11131)
On default objects:
- Add see workflows

On workflows:
- Add see all runs (no selection)

On workflow runs:
- Add see workflows (no selection)
- Add see linked workflow (single record selection)
- Add see run version (single record selection)

On workflows versions
- Add see all runs (no selection)
- Add see workflows (no selection)
- Add see linked workflow (single record selection)
- Add see linked runs (single record selection)
2025-03-24 18:12:32 +01:00
Thomas TrompetteGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
049a065307 Update workflow run step (#11125)
Currently, when filling the form, values are not saved in the action
settings. This is an issue because we do not see the response in the
node settings, only in the output of the step.

This PR:
- adds a new endpoint to update a step in the run flow output
- updates this flow when a step is updated



https://github.com/user-attachments/assets/2e74a010-a0d2-4b87-bd1f-1c91f7ca6b60

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-03-24 17:42:15 +01:00
Paul RastoinandGitHub 0656b640d7 [FIX] Dockerfile copy prettierrc (#11129)
Now needed to build twenty-shared
2025-03-24 17:28:17 +01:00
Paul RastoinandGitHub da394ffcdc [FIX] Move preconstruct patch into twenty-shared package (#11124) 2025-03-24 15:51:14 +01:00
Paul RastoinandGitHub e23fcb21ae [CD][FIX] Copy twenty-shared patch (#11123)
Copy patches
2025-03-24 15:34:36 +01:00
Raphaël BosiandGitHub 0084946b76 608 fix hotkey scope and dropdown issues in the command menu (#11121)
Closes https://github.com/twentyhq/core-team-issues/issues/608

- Introduces a new hotkey scope `CommandMenuFocused`
- Fixes hotkey scopes issues in the side panel
2025-03-24 14:34:09 +00:00
Charles BochetandGitHub 6898a40ac3 Fix relation from many notes (#11120)
Fixes https://github.com/twentyhq/twenty/issues/3415
2025-03-24 15:19:05 +01:00
Lucas BordeauandGitHub e83e7b3b40 Fix bug and refactored advanced filter field selection dropdown (#11103)
This PR is a first step towards isolating each filter dropdown use case,
here we isolate advanced filter field selection dropdown from view bar
filter field selection dropdown.

## Isolation of advanced filter field selection logic

We reimplement the previously generic logic into
AdvancedFilterFieldSelectMenu and AdvancedFilterSubFieldSelectMenu
components.

For now it contains duplicated code but, the end goal is to factorize
what's common to all object filter dropdowns in small hooks where
possible, at the end of the code path isolation first step, which will
be done for applyFilter and selectFilter logic that will be able to be
deleted after code path isolation.

A new component ObjectFilterDropdownFilterSelectMenuItemV2 has been
created to expose an onClick method instead of computing logic that
tries to guess where it is located, which allows to verticalize what
happens when we select a field in each specific use case, one layer
above.

Created the hook useSelectFieldUsedInAdvancedFilterDropdown which
contains the logic for field selection for advanced filter field select
dropdown specific use case, the first example of a good verticalized and
unique place to handle field selection in the context of advanced
filter.

The naming useAdvancedFilterDropdown was lying and is now
useAdvancedFilterFieldSelectDropdown which is now self-explanatory.

useAdvancedFilterFieldSelectDropdown has been removed from the main
object filter dropdown, thus isolating advanced filters field select
dropdown from the generic object filter field select dropdown.

## Miscellaneous

Removed states that were used in the generic filter dropdown to guess
whether it was in advanced filter context or not.
2025-03-24 15:18:53 +01:00
Paul RastoinandGitHub 8b2a90dea1 [FEAT] Generate barrel export named modules and types (#11110)
# Introduction
In this PR using the Ts AST dynamically compute what to export,
gathering non-runtime types and interface in an `export type`

[Export type TypeScript
documentation](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html)

From
```ts
// index.ts
export * from "submodule"
```
To
```ts
export type { SomeType } from "submodule";
export { SomeFunction, SomeConst } from "submodule";
```


close https://github.com/twentyhq/core-team-issues/issues/644

## Motivations
- Most explicit and maintainable
- Best for tree-shaking
- Clear dependency tracking
- Prevents name collisions

## Important note
Please keep in mind that I will create, very soon, a dedicated
`generate-barrel` package in our yarn workspaces in order to:
- Make it reusable for twenty-ui
- Split in several files
- Setup lint + tsconfig
- Add tests

## Conclusion
As usual any suggestions are more than welcomed !
2025-03-24 15:06:16 +01:00
e6dec51ca6 Display workflow step header in workflow run input and output tabs (#11102)
- Wrap the content of Workflow View, Workflow Edit, and Workflow Run
side panels with a container making them take all the available height
- Remove the `StyledContainer` of code steps as it's redundant with the
global container
- Add the WorkflowStepHeader to the input and output tabs
- Make the JSON visualizer take all the available height in input and
output tabs
- Reuse the WorkflowStepBody component in the input and output tabs as
it applies proper background color

## Demo

![CleanShot 2025-03-21 at 18 30
26@2x](https://github.com/user-attachments/assets/c3fa512b-c371-4d0b-9bf6-a5f84d333dda)

Fixes
https://discord.com/channels/1130383047699738754/1351906809417568376

---------

Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
2025-03-24 13:06:26 +00:00
EtienneandGitHub 1c5f3ef5fa clean searchResolvers in server (#11114)
Introduces break in change

- remove search... resolvers
- rename globalSearch to search
- rename searchRecord.objectSingularName > objectNameSingular
closes https://github.com/twentyhq/core-team-issues/issues/643
2025-03-24 13:42:51 +01:00
Charles BochetandGitHub 6e7d2db58f Fix From Many relation for deleted notes crashing (#11117)
In this PR, I'm: 
- fixing the root cause (we should not try to render a RecordChip if the
record is not defined in RelationFromMany Display)
- fixing related typing issues
- we won't be able to catch the issue from TS perspective as
ObjectRecord is a Record of string, any
2025-03-24 13:29:44 +01:00
GuillimandGitHub 3ec72a2bca logging ids to import (#11115) 2025-03-24 09:17:31 +00:00
f9e826d37e fix: Added ScrollWrapper on Settings Sidebar (#11106)
## Description

- this PR fixes issue #11092 
- Added ScrollWrapper and scroll in Settings sidebar while maintaining
the exit Settings fixed.

## Added Behaviour



https://github.com/user-attachments/assets/c8db0f6d-986e-46f3-85d6-bb3028c56e5f

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-03-23 13:02:33 +00:00
Paul RastoinandGitHub 9ad8287dbc [REFACTOR] twenty-shared multi barrel and CJS/ESM build with preconstruct (#11083)
# Introduction

In this PR we've migrated `twenty-shared` from a `vite` app
[libary-mode](https://vite.dev/guide/build#library-mode) to a
[preconstruct](https://preconstruct.tools/) "atomic" application ( in
the future would like to introduce preconstruct to handle of all our
atomic dependencies such as `twenty-emails` `twenty-ui` etc it will be
integrated at the monorepo's root directly, would be to invasive in the
first, starting incremental via `twenty-shared`)

For more information regarding the motivations please refer to nor:
- https://github.com/twentyhq/core-team-issues/issues/587
-
https://github.com/twentyhq/core-team-issues/issues/281#issuecomment-2630949682

close https://github.com/twentyhq/core-team-issues/issues/589
close https://github.com/twentyhq/core-team-issues/issues/590

## How to test
In order to ease the review this PR will ship all the codegen at the
very end, the actual meaning full diff is `+2,411 −114`
In order to migrate existing dependent packages to `twenty-shared` multi
barrel new arch you need to run in local:
```sh
yarn tsx packages/twenty-shared/scripts/migrateFromSingleToMultiBarrelImport.ts && \
npx nx run-many -t lint --fix -p twenty-front twenty-ui twenty-server twenty-emails twenty-shared twenty-zapier
```
Note that `migrateFromSingleToMultiBarrelImport` is idempotent, it's atm
included in the PR but should not be merged. ( such as codegen will be
added before merging this script will be removed )

## Misc
- related opened issue preconstruct
https://github.com/preconstruct/preconstruct/issues/617

## Closed related PR
- https://github.com/twentyhq/twenty/pull/11028
- https://github.com/twentyhq/twenty/pull/10993
- https://github.com/twentyhq/twenty/pull/10960

## Upcoming enhancement: ( in others dedicated PRs )
- 1/ refactor generate barrel to export atomic module instead of `*`
- 2/ generate barrel own package with several files and tests
- 3/ Migration twenty-ui the same way
- 4/ Use `preconstruct` at monorepo global level

## Conclusion
As always any suggestions are welcomed !
2025-03-22 19:16:06 +01:00
Charles Bochet 8a21c19f03 Fix missing record field component instanceId 2025-03-22 18:52:57 +01:00
Mitesh GuptaandGitHub aba55a5a34 fix: update allowed protocols for URL validation in environment variables (#11104)
When using postgres cloud databse connection string with protocol
'postgresql', it throws error "ERROR PG_DATABASE_URL must be a URL
address".

![Screenshot 2025-03-22 at 12 24
25 AM](https://github.com/user-attachments/assets/a5fdcb4e-686c-481a-9f4d-48a65aa44a30)
2025-03-22 13:32:50 +00:00
Charles BochetandGitHub ccf60284cf Various fixes (#11108)
Fixes many bug regarding TableCell and InlineCells
2025-03-22 14:19:10 +01:00
Charles BochetandGitHub 692e08f0d4 Improve AppError boundaries (#11107)
## What

This PR aims to make sure all application exceptions are captured
through react-error-boundaries

Once merged we will have:
- Root Level: AppErrorBoundary at the highest level (full screen) ==>
this one needs to be working in any case, not relying on Theme, was not
working
- Route Level: AppErrorBoundary in DefaultLayout (full screen) ==> this
was missing and it seems that error are not propagated outside of the
router, making errors triggered in CommandMenu or NavigationDrawer
missing
- Page Level: AppErrorBoundary in DefaultLayout write around the Page
itself (lower than CommandMenu + NavigationDrawer)
- Manually triggered: example in ClientConfigProvider

## Screenshots

App level (ex throw in IconsProvider)
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/18a14815-a203-4edf-b931-43068c3436ec"
/>

Route level (ex throw in CommandMenu)
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/ca066627-14c7-438e-a432-f0999a1f3b84"
/>

Page level (ex throw in RecordTable)
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/ffeaa935-02af-4762-8859-7a0ccf8b77e1"
/>

Manually Triggered (clientConfig, ex when backend is not up)
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/062d6d84-097a-4ed9-b6ce-763b8c27c659"
/>
2025-03-22 09:19:08 +01:00
Thomas TrompetteandGitHub c50cdd9510 Execute workflow form action (#11099)
- create a form filler component
- send the response on submit
- put back a field name. We need it for the step output
- validate a form is well set before activation

TODO:
- we need to refresh to see the form submitted. We need to discuss about
a strategy
- the response is not saved in the step settings. We need a new endpoint
to update workflow run step



https://github.com/user-attachments/assets/0f34a6cd-ed8c-4d9a-a1d4-51455cc83443
2025-03-21 17:38:14 +00:00
Félix MalfaitandGitHub 07bd2486ca Add enterprise plan in cloud onboarding (#11100)
Adding a way to switch to enterprise plan during onboarding

<img width="409" alt="Screenshot 2025-03-21 at 17 03 19"
src="https://github.com/user-attachments/assets/7a8c9ebd-3d77-4875-a141-c30fa5119eff"
/>
2025-03-21 17:38:13 +01:00
EtienneandGitHub e624e8deee replace search resolvers by global search in front (#11086)
Done
- Replace global search in multi record picker and single record picker

To do
- refactor SingleRecordPicker to match MultipleRecordPicker - next 1:1
- items in this issue
https://github.com/twentyhq/core-team-issues/issues/643



closes https://github.com/twentyhq/core-team-issues/issues/535
2025-03-21 17:25:00 +01:00
Antoine MoreauxandGitHub da527f1780 chore(ci): update Tinybird CI workflow (#11033)
Added a new job to check for changed files before executing the CI
workflow. Integrated Tinybird local service, updated environment
variables, and refined the CI steps for better functionality and
clarity.
2025-03-21 16:32:24 +01:00
nitinandGitHub 3960b0f99d App health check: Optimize pending migration query (#11049)
## Optimization: Efficient Health Check Query

This PR optimizes the workspace health check system by replacing the N+1
query pattern with efficient database queries.

### Key Improvements

- **Eliminated N+1 Query Problem**: Instead of fetching all workspaces
and then querying each one individually for pending migrations (which
caused slowness in production), we now use a single optimized query to
directly identify workspaces with pending migrations

- **Better Performance**: Reduced the number of database queries from
potentially hundreds/thousands (previous implementation) to just 2 fixed
queries regardless of workspace count

- **Full Coverage Instead of Sampling**: Rather than implementing a cap
on workspace checks at 100 (which was a workaround for performance
issues), this solution addresses the root cause by optimizing the query
pattern. We can now efficiently check all workspaces with pending
migrations without performance penalties.

@FelixMalfait This addresses the "always eager-load when you can"
feedback by handling the problem at the database level rather than just
applying a limit. The optimized query should solve both the performance
issues and provide more accurate health status information.
2025-03-21 16:30:38 +01:00
Nabhag MotivarasandGitHub 081f5fa766 Sort menu Incorrect padding bottom #11078 (#11091)
##  Issue
- Closes #11078
- **visual regression** from PR - #9159 that likely might have break
visuals in several other places.
2025-03-21 16:29:30 +01:00
Raphaël BosiandGitHub 3328998e61 Remove hotkey scope from useListenClickOutside (#11098)
The old `useListenClickOutside` API allowed us to pass a hotkeyScope as
a parameter, the click outside was triggered only if the current hotkey
scope matched the parameter. We don't want this anymore. This fixes a
few bugs related to hotkey scopes inside the side panel.
2025-03-21 16:27:58 +01:00
WeikoandGitHub 2c5e3dba60 revert logout button to settings menu (#11097)
## Context
Reverting back the removal of "Log out" button. While it is now
accessible from the workspace picker, suspended workspaces don't have
access to that picker and are locked in the settings pages so I'm adding
back the log out button in the settings menu

<img width="225" alt="Screenshot 2025-03-21 at 15 52 40"
src="https://github.com/user-attachments/assets/d5453868-d043-49e9-9207-2cfdd65838da"
/>
2025-03-21 16:26:06 +01:00
martmullandGitHub 463a2995a9 Fix wrong totalCount in groupBy view (#11094)
Add filters by visibleRecordGroup to compute totalCount
## Before
see https://github.com/twentyhq/twenty/issues/11067

## After

![image](https://github.com/user-attachments/assets/333e8898-e051-4554-b462-9b1d2f10ec05)

- After hiding `New` and `Meeting` columns:
<img width="1280" alt="image"
src="https://github.com/user-attachments/assets/a2ae1728-ea11-4e2d-86e5-02778b3c42c0"
/>

- With filtering
<img width="1275" alt="image"
src="https://github.com/user-attachments/assets/263d8865-9cba-4b46-84a0-e9270b29109b"
/>

- Works also in groupBy view
<img width="1280" alt="image"
src="https://github.com/user-attachments/assets/c1ec171f-6eec-45db-aafc-2bd2d1de8841"
/>
2025-03-21 16:22:24 +01:00
martmullandGitHub 6a412a531f Remove useless skelettonLoader (#11096)
## After

![image](https://github.com/user-attachments/assets/772e9a8c-5df2-4bfd-95ea-5854022e82e0)
<img width="825" alt="Capture d’écran 2025-03-21 à 15 43 14"
src="https://github.com/user-attachments/assets/87818683-9cb0-4264-a6c3-f0420b0ae34d"
/>
2025-03-21 16:19:45 +01:00
Lucas BordeauandGitHub c961d3a60d Reorganized components in advanced filter dropdown (#11089)
This PR essentially focuses on a refactor of the component hierarchy and
naming in advanced filter dropdown, to make it more readable and easy to
maintain.

This refactor was required because this area of the code is recursive,
so it's better to see the same abstract components in the recursion,
instead of trying to guess whether we have the same components than the
level above or not.

Also keep in mind that this refactor is meant to separate the advanced
filter code path from the view bar simple filter code path, while
reusing what's reusable, so here we have a first attempt at finding the
sweet spot, that we'll be able to duplicate on other filter dropdown use
cases.

- We now use AdvancedFilterRecordFilterGroupRow and
AdvancedFilterRecordFilterRow to make it clearer in the advanced filter
dropdown recursion where we are.
- Children component of AdvancedFilterRecordFilterRow have been
abstracted at the same level to make reading easier
- The field selection dropdown is now in a self-explanatory component
that follows the same naming pattern as other dropdowns in the app :
AdvancedFilterFieldSelectDrodownButton, together with
AdvancedFilterFieldSelectDrodownContent.
- The field selection search in the filter dropdown is now a standalone
component : AdvancedFilterFieldSelectSearchInput
- The UI container of a row has been abstracted in a new
AdvancedFilterDropdownRow

Miscellaneous : 
- Renamed a bunch of view filter old naming to record filter naming.
2025-03-21 16:19:19 +01:00
Raphaël BosiandGitHub d7dabe5826 Update timeline empty state in the side panel (#11093)
Fixes https://github.com/twentyhq/twenty/issues/10914

Before:


https://github.com/user-attachments/assets/83764543-06bf-455e-8b07-fa2822691005


After:


https://github.com/user-attachments/assets/09afa15d-6352-4024-a6b9-2cccf5991d8a
2025-03-21 15:30:14 +01:00
Charles BochetandGitHub b49dc57bbf Microsoft mail sync: make sure that message list fetch is using max batch size (#11095)
Fixes https://github.com/twentyhq/core-team-issues/issues/641
2025-03-21 15:20:39 +01:00
WeikoandGitHub 77ee016d6f Fix currency update (#11088)
## Context
Currency picker was not working properly, clicking a value was
triggering the clickOutsideListener of the parent and was closing the
select without saving. We are now toggling the click outside listener
based on the state of the currency picker dropdown
This also means the UX changed a bit, now choosing a value or clicking
outside only closes the select (allowing you to choose the amount as
well) and only enter OR clicking outside will save
2025-03-21 13:56:03 +01:00
Raphaël BosiandGitHub cecb32cd89 Fixes on rich text editor (#11087)
- Update skeleton loader padding and make it take only one line
- Update rich text editor padding to align with the fields inside the
side panel
<img width="163" alt="Capture d’écran 2025-03-21 à 11 57 00"
src="https://github.com/user-attachments/assets/5964404a-3a32-4d7e-b96f-3377949430bf"
/>
2025-03-21 13:49:39 +01:00
Raphaël BosiandGitHub 3af5cde170 Reorder commands (#11084)
Put workflows on top of navigation commands in the command menu.
2025-03-21 11:10:30 +01:00
99438a810c Error invalid link (#10288)
Don't have access to push on
https://github.com/twentyhq/twenty/pull/9942, so close it and open new
PR here

<img width="244" alt="Screenshot 2025-02-18 at 11 09 39"
src="https://github.com/user-attachments/assets/4bc1b436-147a-4d17-88c8-2aff0fffd06a"
/>
<img width="246" alt="Screenshot 2025-02-18 at 11 09 51"
src="https://github.com/user-attachments/assets/3d7b2972-ab7e-4e3b-a177-658325a3bb70"
/>

Ok for RecordInlineCell / RecordTableCell and EmailsFieldInput /
LinksFieldInput.
I think it's too complex for a so small issue (agree with you khuddite)

closes https://github.com/twentyhq/twenty/issues/9778
on top of https://github.com/twentyhq/twenty/pull/9942 from @khuddite

---------

Co-authored-by: khuddite <khuddite@gmail.com>
Co-authored-by: khuddite <62555977+khuddite@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-03-21 10:18:55 +01:00
bd162da318 Update environment-variables.ts (#11068)
Added rediss protocol.

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-03-20 22:47:38 +01:00
Raphaël BosiandGitHub 70a295641d Fix overscroll behavior on dev environment (#11080)
Before:


https://github.com/user-attachments/assets/c04e8d5d-cfa2-4d62-a4c6-39041c8b61c8


After:


https://github.com/user-attachments/assets/953b31b4-9388-43ed-9279-2bc0a73b24dc
2025-03-20 22:44:43 +01:00
AyushandGitHub 8c982653cf fix/REDIS_URL-has-failed-the-following-constraints-isUrl (#11082)
Added 'rediss' in protocols array so that url doesn't fail isUrl
constraint
2025-03-20 22:38:27 +01:00
nitinandGitHub 8309575a94 Fix option text deletion in select fields by removing error for empty values (#11081)
closes #11013
Fixes the issue where users couldn't delete all text in select field
options. Removed the error throw for empty strings in the
computeOptionValueFromLabel function, allowing proper text deletion.
This error handling was redundant since the form validation already
prevents submission with empty values.
2025-03-20 22:35:48 +01:00
Paul RastoinandGitHub 872c0e97f6 [BUGFIX] GenerateDefaultRecordChipData returns RecordChipData (#11071)
# Introduction
closes https://github.com/twentyhq/twenty/issues/11030, might not fix
the issue as it seems to be related to the passed record to
`identifierChipGeneratorPerObject` about to dig deeper in this generator
code => found nothing revelant doubled check each `RecordChip`
invocation that could provide undefined record
Fixed wrong default generated `RecordChipData` signature

## Reproducibility
I've only been able to reproduce the bug in production using the very
same opportunity than within the issue, but not all the time

https://crm.twenty-internal.com/objects/opportunities?viewId=b709d3d1-2dd2-455d-ba73-784f3ab00883
```json
// Removed timelineActivities to prevent linking ids
{
  "data": {
    "opportunity": {
      "__typename": "Opportunity",
      "closeDate": null,
      "company": null,
      "companyId": null,
      "createdAt": "2024-06-17T09:45:22.357Z",
      "deletedAt": null,
      "id": "006a22dd-6bd6-4247-a24b-42fb164cd48c",
      "name": "test",
      "pointOfContact": null,
      "pointOfContactId": null,
      "position": 0,
      "probability": "0",
      "stage": "NEW_STAGE",
      "updatedAt": "2025-03-20T16:27:51.927Z",
      "amount": {
        "__typename": "Currency",
        "amountMicros": null,
        "currencyCode": "USD"
      },
      "attachments": {
        "__typename": "AttachmentConnection",
        "edges": []
      },
      "createdBy": {
        "__typename": "Actor",
        "source": "MANUAL",
        "workspaceMemberId": null,
        "name": "",
        "context": {}
      },
      "favorites": {
        "__typename": "FavoriteConnection",
        "edges": []
      },
      "taskTargets": {
        "__typename": "TaskTargetConnection",
        "edges": []
      },
      "noteTargets": {
        "__typename": "NoteTargetConnection",
        "edges": [
          {
            "__typename": "NoteTargetEdge",
            "node": {
              "__typename": "NoteTarget",
              "appEventId": null,
              "companyId": null,
              "createdAt": "2025-01-22T17:11:07.801Z",
              "deletedAt": null,
              "feedbackId": null,
              "id": "2e8eca1c-e2c2-425a-93fc-ef2aeb65f410",
              "issueId": null,
              "listingId": null,
              "noteId": "ab586b51-6931-4a4a-9c24-0d16226211b2",
              "opportunityId": "006a22dd-6bd6-4247-a24b-42fb164cd48c",
              "personId": null,
              "somethingId": null,
              "testId": null,
              "updatedAt": "2025-01-22T17:11:07.801Z"
            }
          }
        ]
      },
    }
  }
}
```
2025-03-20 18:38:19 +01:00
WeikoandGitHub 95014b0ac5 Fix role edit page tab instance id (#11074)
Also fixes workflows
Fixes https://github.com/twentyhq/twenty/issues/11073
2025-03-20 18:30:02 +01:00
Thomas TrompetteandGitHub 3876cb8250 Separate workflow step details and run step details (#11069)
Workflows step details in workflows and versions should be different
from the node tab in run. For most cases, it was using the same
component. But for forms, it will be a different one.

This PR:
- renames form action into formBuilder. formFiller is coming
- put code into a separated folder
- creates a new component for node details
2025-03-20 16:22:55 +00:00
MarieandGitHub 24bae89ebc Fix click outside for RecordDetailRelationSection (#11072)
Fixes https://github.com/twentyhq/twenty/issues/11061
2025-03-20 16:22:28 +00:00
martmullandGitHub 6d8cf442a7 Give worker access to server volume (#11065) 2025-03-20 16:43:38 +01:00
Raphaël BosiandGitHub 7dac60cfee Fix useFindManyRecords withSoftDeleterFilter (#11056)
Fixes #11038

# Fix useFindManyRecords withSoftDeleterFilter

The error came from a faulty implementation of the `withSoftDeleted`
parameter inside `useFindManyRecords` and from the fact that
`withSoftDeleted: true` was added to access deleted records actions.
However, this parameter was always set in
`useFindManyRecordsSelectedInContextStore` instead of considering
whether the filter was active or not.

```
const withSoftDeleterFilter = {
  or: [{ deletedAt: { is: 'NULL' } }, { deletedAt: { is: 'NOT_NULL' } }],
};
```

The final filter was incorrectly doing an `or` operation between the
base filter and `withSoftDeleterFilter` when it should have been an
`and`:

```
filter: {
  ...filter,
  ...(withSoftDeleted ? withSoftDeleterFilter : {}),
}
```

The correct implementation should be:

```
filter:
  filter || withSoftDeleted
    ? {
        and: [
          ...(filter ? [filter] : []),
          ...(withSoftDeleted ? [withSoftDeleterFilter] : []),
        ],
      }
    : undefined,
```

# Fix useFindManyRecordsSelectedInContextStore

- Check if the soft deleted filter is active before using the
`withSoftDeleterFilter` parameter
2025-03-20 16:27:55 +01:00
Antoine MoreauxandGitHub 295f153abe fix(settings): adjust custom domain table cell styling (#11060)
Set table cell display to block to ensure proper rendering and
alignment. This resolves layout issues caused by the previous styling.
2025-03-20 13:35:58 +01:00
Antoine MoreauxandGitHub 5f390d75b0 refactor(button): rename loading to isLoading for clarity (#11058)
Updated all instances of the loading prop to isLoading across Button and
related components. This improves readability and ensures consistency in
the codebase.
2025-03-20 11:57:19 +00:00
Antoine MoreauxandGitHub 548597bfd0 refactor(ui): simplify workspace dropdown component structure (#11040)
Reorganized the workspace dropdown rendering logic for improved
readability and maintainability. Ensured consistent handling of
separators and dropdown items, while preserving the existing
functionality.

Fix #11034
2025-03-20 12:38:10 +01:00
MarieandGitHub 8b7188e85b [fix] [permissions] Fix role assignment at sign-up (#11045)
In [a previous PR](https://github.com/twentyhq/twenty/pull/11023) I
fixed the issue where users where re-assigned the default role when
signin up using SSO.
The "fix" actually introduced another error, calling
`assignRoleToUserWorkspace` only when a user is signing up to twenty,
forgetting about the case where an existing twenty user signs up to a
different workspace. They should still be assigned the role in that
case.

To fix this and improve clarity, I moved assignRoleToUserWorkspace to
addUserToWorkspace and renamed addUserToWorkspace to
addUserToWorkspaceIfUserNotInWorkspace since this is at each sign in but
does nothing if user is already in the workspace.

I think ideally we should refactor this part to improve readability and
understandability, maybe with different flows for each case: signIn and
signUp, to twenty or to a workspace
2025-03-20 10:37:26 +00:00
Lucas BordeauandGitHub 4b34aa60b1 Fix minor bugs on advanced filters (#11044)
This PR fixes some minor bugs on advanced filters.

## Dropdown menu header in filter input

The chevron down icon in the operand dropdown menu header was missing
due to a recent refactor of the DropdownMenuHeader component.

I just removed the unused EndIcon and replaced its usage by
EndComponent.

## Advanced filter dropdown staying open with 0 filters

The behavior we have for non-advanced filters is that the chip should
disappear if the filter gets empty, which is logical, an empty filter is
equivalent to not having filters, so don't want empty chips.

For advanced filters, the principle is the same, except that it's a bit
more complex to handle due to the recursive filter group hierarchy.

Here we create a useRemoveRootRecordFilterGroupIfEmpty hook, that we can
call everywhere a synchronous action should end up removing advanced
filters completely. (instead of using an effect)

This hook is distinct from removeRecordFilterGroup because we want
removeRecordFilterGroup to do only one job and we don't want it to hide
any side effect. It's better to have the side effect in a separate hook
that we call sequentially afterwards, in a self-explanatory manner.

## Miscellaneous

In this PR we add a new component selector to get the root level record
filter group, which is handy in a lot of cases.

The return type of the useChildRecordFiltersAndRecordFilterGroups hook
when it's empty has been fixed, though as discussed with Charles, it
would be better to turn it into selectors, which will certainly be done
in future PRs.
2025-03-20 11:26:14 +01:00
5f8fae9ada i18n - translations (#11054)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-20 11:21:03 +01:00
martmullandGitHub d99f027e8d 400 workflows webhooks trigger (#11041)
https://github.com/user-attachments/assets/dc0ece22-4d87-417f-b9e1-a11c3fd52ce8
2025-03-20 10:12:52 +00:00
bc94891a27 i18n - translations (#11052)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-20 11:12:44 +01:00
Thomas TrompetteandGitHub 369abfe424 Rename onPersist to onChange for form fields (#11047)
We are about to build forms where fields will not be persisted
immediately. It will have to be submitted.
Renaming the prop to reflect it.
2025-03-20 10:58:02 +01:00
e666506ea3 chore: Unify Label Styles Across App #6389 (#10728)
Chores #6389 

## Description
This PR addresses inconsistencies in the codebase where elements that
visually function as labels were implemented with custom-styled
components rather than the standardized Label component from the UI
library.

## Changes
I've replaced several custom-styled text elements with the standardized
Label component from twenty-ui to improve consistency and
maintainability across the application. These modifications maintain the
same visual appearance and functionality while standardizing the
implementation.

## Components Modified:
InputLabel: Converted from a styled label to use the Label component
InputHint: Replaced styled div with a styled Label component
TableSection: Introduced a StyledLabel using the Label component for
section headings
StyledDropdownMenuSubheader: Converted from a styled div to a styled
Label component
NavigationDrawerSectionTitle: Replaced internal text element with the
Label component
SettingsCard: Updated description element to use the Label component
SettingsListItemCardContent: Changed description span to use the Label
component
RecordDetailSectionHeader: Added a StyledLabelLink for link text using
the Label component
TaskList: Modified the task count display to use the Label component
CommandGroup: Updated group headings to use the Label component
WorkerMetricsGraph: Replaced no-data message with a Label-based
component
ViewPickerSelectContainer: Changed from a styled div to a styled Label
component

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-03-20 09:32:34 +00:00
3b3ed1a907 i18n - translations (#11051)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-20 10:20:50 +01:00
d8ea292321 (#11036) task: remove redundant logout button from settings sidebar (#11050)
- Removed logout item from settings navigation drawer
- Removed logout locator and method from E2E tests
- Removed logout item from NavigationDrawer story

The logout functionality is now exclusively available through the menu
switcher, making the UI more consistent and reducing duplication.

Closes #11036

<img width="851" alt="Screenshot 2025-03-19 at 9 46 33 PM"
src="https://github.com/user-attachments/assets/3d73ec84-a2b7-4c4d-9605-dc83a9a760c1"
/>

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-03-20 10:08:59 +01:00
183f497ad7 i18n - translations (#11048)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-19 19:21:35 +01:00
99f68416dc i18n - translations (#11046)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-19 19:01:39 +01:00
Thomas TrompetteandGitHub 7b0bf7c4b0 Form action field base settings (#11035)
- Add settings for text and number fields
- Settings are for now the same but I still separated with two
components because they will evolve


https://github.com/user-attachments/assets/96b7fffd-c3a1-45b9-aeaa-45d63505de3c
2025-03-19 17:44:02 +00:00
GuillimandGitHub 8b513a7d3b contact creation was recreating a primaryemail du to casing (#11042)
a first step to fix some of the issues i notices in grafana for netzero

in grafana, we log this error :  
```
error: error: duplicate key value violates unique constraint "IDX_UNIQUE_87914cd3ce963115f8cb943e2ac"

query failed: INSERT INTO "workspace_20fjk7nksxxxxxxxxxxx"."person"("cdrOpportunityId", "siteOpportunityId", "xLinkPrimaryLinkLabel", "xLinkPrimaryLinkUrl", "xLinkSecondaryLinks", "linkedinLinkPrimaryLinkLabel", "linkedinLinkPrimaryLinkUrl", "linkedinLinkSecondaryLinks", "emailsPrimaryEmail", "emailsAdditionalEmails", "companyId", "deletedAt", "position", "phonesPrimaryPhoneNumber", "phonesPrimaryPhoneCountryCode", "phonesPrimaryPhoneCallingCode", "phonesAdditionalPhones", "createdAt", "id", "searchVector", "avatarUrl", "city", "jobTitle", "nameFirstName", "nameLastName", "updatedAt", "createdBySource", "createdByWorkspaceMemberId", "createdByName", "createdByContext") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, DEFAULT, $4, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $5, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $6, $7, DEFAULT, $8, $9, $10, $11), (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $12, $13, $14, DEFAULT, $15, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $16, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $17, $18, DEFAULT, $19, $20, $21, $22), (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $23, $24, $25, DEFAULT, $26, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $27, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $28, $29, DEFAULT, $30, $31, $32, $33), (DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $34, $35, $36, DEFAULT, $37, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $38, DEFAULT, DEFAULT, DEFAULT, DEFAULT, $39, $40, DEFAULT, $41, $42, $43, $44) RETURNING "xLinkPrimaryLinkLabel", "xLinkPrimaryLinkUrl", "xLinkSecondaryLinks", "linkedinLinkPrimaryLinkLabel", "linkedinLinkPrimaryLinkUrl", "linkedinLinkSecondaryLinks", "emailsPrimaryEmail", "emailsAdditionalEmails", "companyId", "deletedAt", "position", "phonesPrimaryPhoneNumber", "phonesPrimaryPhoneCountryCode", "phonesPrimaryPhoneCallingCode", "phonesAdditionalPhones", "createdAt", "id", "searchVector", "avatarUrl", "city", "jobTitle", "nameFirstName", "nameLastName", "updatedAt", "createdBySource", "createdByWorkspaceMemberId", "createdByName", "createdByContext" 
```
The index is related to the People table on the netzero workspace
schema. When I looked at the email trying to be inserted, it contains
captial letters. I looked at the DB and there are existing small caps
emails. That's why my guess it that the failure comes from the contact
creation service.

Intersting fact, it comes from the CALENDAR source, not the email one.
That's why i added a little bit of error logging in case it reproduces
itself
2025-03-19 18:24:35 +01:00
Raphaël BosiandGitHub 7fe81f28b7 Update CommandMenuContextChipRecordSetterEffect to use the cache (#11043)
Closes https://github.com/twentyhq/core-team-issues/issues/581

Get records from the cache directly instead of querying them.
2025-03-19 18:21:06 +01:00
Antoine MoreauxandGitHub 28028ca4c0 fix(twenty-front): error on captcha initialisation (#11039) 2025-03-19 16:09:35 +00:00
Raphaël BosiandGitHub cfdb3f5778 584 Refactor Tabs (#11008)
Closes https://github.com/twentyhq/core-team-issues/issues/584

This PR:
- Migrates the component state `activeTabIdComponentState` from the
deprecated V1 version to V2.
- Allows the active tab state to be preserved during navigation inside
the side panel and reset when the side panel is closed.
- Allows the active tab state to be preserved when we open a record in
full page from the side panel


https://github.com/user-attachments/assets/f2329d7a-ea15-4bd8-81dc-e98ce11edbd0


https://github.com/user-attachments/assets/474bffd5-29e0-40ba-97f4-fa5e9be34dc2
2025-03-19 15:53:22 +00:00
0d40126a29 i18n - translations (#11031)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-03-19 15:20:18 +01:00
EtienneandGitHub b8b00e5398 add integration test on global search resolver (#11007)
closes https://github.com/twentyhq/core-team-issues/issues/580
2025-03-19 14:22:02 +01:00
GuillimandGitHub d59c989ecd Align navigation icons (#10983)
Fixes https://github.com/twentyhq/twenty/issues/10550

FYI : I took the same width calculation than the other navigation items.
2025-03-19 13:16:15 +00:00
69b3b34859 [permissions] Fix user is assigned default role after SSO sign-in (#11023)
When logging using a SSO method, we call signInUp service in which we
were wrongfully assigning a role to the user even if the user is signing
in and not signin up.

This went unnoticed during our QA as a different sign-in method is
called when logging with the credentials.

---------

Co-authored-by: Weiko <corentin@twenty.com>
2025-03-19 14:14:45 +01:00
8c5335a863 i18n - translations (#11026)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-19 13:22:31 +01:00
Paul RastoinandGitHub 5ba3c09b14 [CHORE] Prepare twenty-serverfor twenty-shared multi barrel refactor (#11022)
# Introduction
While running https://github.com/twentyhq/twenty/pull/10960 scripts
discovers few issues:
- Invalid named folder `pre-hooks.ts`
- Mock consuming outbound imported module resulting in consumed before
initialization
2025-03-19 11:26:42 +00:00
Paul RastoinandGitHub 4ce607869e [CHORE] TypeScript Lingui configuration file (#11021)
# Introduction 
We want theses configurations to be strictly typed but needed as in
https://github.com/twentyhq/twenty/pull/10960 will browse through all
TypeScript files within the codebase
2025-03-19 12:05:55 +01:00
7065f6f15d i18n - translations (#11020)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-19 11:59:34 +01:00
Baptiste DevessierandGitHub 1ecc5e2bf6 Limit nodes opened by default in the JSON Tree component (#11002)
- Add a parameter to choose which nodes to open by default
- On the Admin Panel, open all nodes by default
- On the Workflow Run step output, open only the two first depths
- On the Workflow Run step input, open only the previous step first
depth
- Display `[empty string]` when a node is an empty string
- Now, display `null` instead of `[null]`

## Demo


https://github.com/user-attachments/assets/99b3078a-da3c-4330-b0ff-ddb2e360d933

Closes https://github.com/twentyhq/core-team-issues/issues/538
2025-03-19 11:44:34 +01:00
BOHEUSandGitHub 15a2cb5141 Update playwright (#10927)
Update twenty-e2e-testing to reflect actual state of app
2025-03-19 11:29:36 +01:00
ecc21656cb i18n - translations (#11019)
Created by Github action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2025-03-19 11:20:44 +01:00
Lucas BordeauandGitHub 62a58816e3 Fix view groups update after field metadata update (#10995)
This PR fixes a difficult to reproduce bug, where a race condition
appears if we go back to a table with view groups before the update
field metadata logic finishes its work.

In order to reproduce this bug on localhost, you'll have to simulate a
slow network in your browser.

The problem was that the view groups are initialized only once by
useLoadRecordIndexStates, in an effect component :
RecordIndexLoadBaseOnContextStoreEffect. And that this component as an
internal state loadedViewId, which prevents subsequent updates of view
groups by useLoadRecordIndexStates, because it considers that loading
already happened, even if it's actually stale data.

So instead of creating other states to burden the effect component with
complex state management, the solution was to add the only needed update
in a synchronous way directly in updateOneFieldMetadataItem logic. This
way we are sure that our boards and tables with view groups get updated
eventually, for each field metadata update, even if the requests take
time.

Fixes https://github.com/twentyhq/twenty/issues/10947
Fixes https://github.com/twentyhq/twenty/issues/10944
2025-03-19 11:13:14 +01:00
martmullandGitHub 7921ee5216 Stop emitting update event if before===after (#11004)
as title
2025-03-19 10:49:21 +01:00
Charles BochetandGitHub ca9c070945 Fix activity (#11015)
Deprecating unused states and making sure that the ActivityRichText
editor loads when activity.bodyV2 is present
2025-03-19 10:45:11 +01:00
3773 changed files with 101674 additions and 42826 deletions
+1
View File
@@ -1,6 +1,7 @@
module.exports = {
root: true,
extends: ['plugin:prettier/recommended', 'plugin:lingui/recommended'],
ignorePatterns: ['node_modules'],
plugins: [
'@nx',
'prefer-arrow',
+114 -5
View File
@@ -6,14 +6,74 @@ module.exports = {
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:storybook/recommended',
'plugin:prettier/recommended',
'plugin:lingui/recommended',
'plugin:@nx/typescript'
],
plugins: ['react-hooks', 'react-refresh'],
plugins: ['react-hooks', 'react-refresh', '@nx', 'prefer-arrow', 'import', 'unused-imports', 'unicorn', '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,
'no-debugger': 'error',
'no-duplicate-imports': 'error',
'no-undef': 'off',
'no-unused-vars': 'off',
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{
sourceTag: 'scope:shared',
onlyDependOnLibsWithTags: ['scope:shared'],
},
{
sourceTag: 'scope:backend',
onlyDependOnLibsWithTags: ['scope:shared', 'scope:backend'],
},
{
sourceTag: 'scope:frontend',
onlyDependOnLibsWithTags: ['scope:shared', 'scope:frontend'],
},
{
sourceTag: 'scope:zapier',
onlyDependOnLibsWithTags: ['scope:shared'],
},
],
},
],
'import/no-relative-packages': 'error',
'import/no-useless-path-segments': 'error',
'import/no-duplicates': ['error', { considerQueryString: true }],
'prefer-arrow/prefer-arrow-functions': [
'error',
{
disallowPrototype: true,
singleReturnOnly: false,
classPropertiesAllowed: false,
},
],
'unused-imports/no-unused-imports': 'warn',
'unused-imports/no-unused-vars': [
'warn',
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
},
overrides: [
{
files: ['*.ts', '*.tsx'],
parserOptions: {
project: ['./tsconfig.base.{json,*.json}'],
},
files: ['**/*.ts', '**/*.tsx'],
rules: {
'no-restricted-imports': [
'error',
@@ -37,6 +97,14 @@ module.exports = {
],
},
],
'@typescript-eslint/no-empty-interface': [
'error',
{
allowSingleExtends: true,
},
],
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@nx/workspace-effect-components': 'error',
'@nx/workspace-no-hardcoded-colors': 'error',
'@nx/workspace-matching-state-variable': 'error',
@@ -86,5 +154,46 @@ module.exports = {
],
},
},
{
files: ['*.js', '*.jsx'],
extends: ['plugin:@nx/javascript'],
rules: {},
},
{
files: [
'*.test.@(ts|tsx|js|jsx)',
],
env: {
jest: true,
},
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
{
files: ['**/*.constants.ts'],
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'variable',
format: ['UPPER_CASE'],
},
],
'unicorn/filename-case': [
'warn',
{
cases: {
pascalCase: true,
},
},
],
'@nx/workspace-max-consts-per-file': ['error', { max: 1 }],
},
},
{
files: ['*.json'],
parser: 'jsonc-eslint-parser',
},
],
};
+3 -7
View File
@@ -13,14 +13,10 @@ Good first issues are a great way to start contributing and get familiar with th
## Issue assignment
Having multiple contributors address the same issue can cause frustration.
To avoid conflicts, we follow these guidelines:
1. If a core team member assigned you the issue within the last three days, your PR takes priority.
2. Otherwise, the first submitted PR is prioritized.
3. For "size: long" PRs, the assignment period extends to one week.
Please ensure you're assigned to an issue before starting work.
1. For `Good First Issue` and `Experienced Contributor` issues without `size: long` labels, we'll merge the first PRs that meet our [code quality standards](https://twenty.com/developers). **We don't assign contributors to these issues**. For `priority: high` issues, our core team will step in within days if no adequate contributions are received.
2. For `size: long` Issues, assigned contributors have one week to submit their first draft PR.
## How to Contribute
@@ -67,4 +63,4 @@ git push origin your-branch-name
## Reporting Issues
If you face any issues or have suggestions, please feel free to (create an issue on Twenty's GitHub repository)[https://github.com/twentyhq/twenty/issues/new]. Please provide as much detail as possible.
If you face any issues or have suggestions, please feel free to (create an issue on Twenty's GitHub repository)[https://github.com/twentyhq/twenty/issues/new]. Please provide as much detail as possible.
@@ -20,7 +20,7 @@ runs:
id: cache-primary-key-builder
shell: bash
run: |
echo "CACHE_PRIMARY_KEY_PREFIX=${{ inputs.key }}-${{ github.ref_name }}" >> "${GITHUB_OUTPUT}"
echo "CACHE_PRIMARY_KEY_PREFIX=v3-${{ inputs.key }}-${{ github.ref_name }}" >> "${GITHUB_OUTPUT}"
- name: Restore cache
uses: actions/cache/restore@v4
id: restore-cache
@@ -25,8 +25,8 @@ runs:
id: cache-node-modules
uses: actions/cache/restore@v4
with:
key: ${{ steps.globals.outputs.CACHE_KEY_PREFIX }}-${{github.sha}}
restore-keys: ${{ steps.globals.outputs.CACHE_KEY_PREFIX }}-
key: v3-${{ steps.globals.outputs.CACHE_KEY_PREFIX }}-${{github.sha}}
restore-keys: v3-${{ steps.globals.outputs.CACHE_KEY_PREFIX }}-
path: ${{ steps.globals.outputs.PATH_TO_CACHE }}
- name: Install Dependencies
if: ${{ steps.cache-node-modules.outputs.cache-hit != 'true' && steps.cache-node-modules.outputs.cache-matched-key == '' }}
+62
View File
@@ -0,0 +1,62 @@
name: CI Emails
on:
push:
branches:
- main
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changed-files-check:
uses: ./.github/workflows/changed-files.yaml
with:
files: |
package.json
packages/twenty-emails/**
emails-test:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
timeout-minutes: 10
runs-on: depot-ubuntu-24.04-8
env:
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/workflows/actions/yarn-install
- name: Build twenty-emails
run: npx nx build twenty-emails
- name: Run email tests
run: |
# Start the email server in the background
npx nx run twenty-emails:start &
SERVER_PID=$!
# Wait for server to start
sleep 20
# Check if server is running
if ! curl -s http://localhost:4001/preview/test.email > /dev/null; then
echo "Email server failed to start"
kill $SERVER_PID
exit 1
fi
# Kill the server
kill $SERVER_PID
ci-emails-status-check:
if: always() && !cancelled()
timeout-minutes: 1
runs-on: depot-ubuntu-24.04-8
needs: [changed-files-check, emails-test]
steps:
- name: Fail job if any needs failed
if: contains(needs.*.result, 'failure')
run: exit 1
+21 -1
View File
@@ -168,9 +168,25 @@ jobs:
image: redis
ports:
- 6379:6379
clickhouse:
image: clickhouse/clickhouse-server:latest
env:
CLICKHOUSE_PASSWORD: clickhousePassword
CLICKHOUSE_URL: "http://default:clickhousePassword@localhost:8123/twenty"
ports:
- 8123:8123
- 9000:9000
options: >-
--health-cmd "clickhouse-client --host=localhost --port=9000 --user=default --password=clickhousePassword --query='SELECT 1'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
NODE_ENV: test
ANALYTICS_ENABLED: true
CLICKHOUSE_URL: "http://default:clickhousePassword@localhost:8123/twenty"
CLICKHOUSE_PASSWORD: clickhousePassword
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@v4
@@ -178,7 +194,7 @@ jobs:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/workflows/actions/yarn-install
- name: Update .env.test for billing
- name: Update .env.test for integrations tests
run: |
echo "IS_BILLING_ENABLED=true" >> .env.test
echo "BILLING_STRIPE_API_KEY=test-api-key" >> .env.test
@@ -198,6 +214,10 @@ jobs:
- name: Server / Create Test DB
run: |
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
- name: Run ClickHouse migrations
run: npx nx clickhouse:migrate twenty-server
- name: Run ClickHouse seeds
run: npx nx clickhouse:seed twenty-server
- name: Server / Run Integration Tests
uses: ./.github/workflows/actions/nx-affected
with:
-26
View File
@@ -1,26 +0,0 @@
name: CI Tinybird
on:
push:
branches:
- main
paths:
- 'package.json'
- 'packages/twenty-tinybird/**'
pull_request:
paths:
- 'package.json'
- 'packages/twenty-tinybird/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
ci:
uses: tinybirdco/ci/.github/workflows/ci.yml@main
with:
data_project_dir: packages/twenty-tinybird
secrets:
tb_admin_token: ${{ secrets.TB_ADMIN_TOKEN }}
tb_host: https://api.eu-central-1.aws.tinybird.co
+1 -1
View File
@@ -21,7 +21,7 @@ concurrency:
jobs:
danger-js:
timeout-minutes: 3
timeout-minutes: 5
runs-on: ubuntu-latest
if: github.event.action != 'closed'
steps:
+2 -1
View File
@@ -42,4 +42,5 @@ dump.rdb
/flake.lock
/flake.nix
.crowdin.yml
.crowdin.yml
.react-email/
+4 -1
View File
@@ -17,4 +17,7 @@ postgres-on-docker:
-c "CREATE DATABASE \"test\" WITH OWNER postgres;"
redis-on-docker:
docker run -d --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
docker run -d --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
clickhouse-on-docker:
docker run -d --name twenty_clickhouse -p 8123:8123 -p 9000:9000 -e CLICKHOUSE_PASSWORD=devPassword clickhouse/clickhouse-server:latest
+6 -4
View File
@@ -44,7 +44,7 @@
"@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.32",
"@react-email/components": "0.0.35",
"@react-email/render": "0.0.17",
"@sentry/node": "^8",
"@sentry/profiling-node": "^8",
@@ -99,7 +99,9 @@
"graphql-fields": "^2.0.3",
"graphql-middleware": "^6.1.35",
"graphql-rate-limit": "^3.3.0",
"graphql-redis-subscriptions": "^2.7.0",
"graphql-scalars": "^1.23.0",
"graphql-sse": "^2.5.4",
"graphql-subscriptions": "2.0.0",
"graphql-tag": "^2.12.6",
"graphql-type-json": "^0.3.2",
@@ -140,8 +142,6 @@
"next-mdx-remote": "^4.4.1",
"nodemailer": "^6.9.8",
"openapi-types": "^12.1.3",
"overlayscrollbars": "^2.6.1",
"overlayscrollbars-react": "^0.5.4",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
@@ -183,6 +183,7 @@
"semver": "^7.5.4",
"sharp": "^0.32.1",
"slash": "^5.1.0",
"storybook-addon-mock-date": "^0.6.0",
"stripe": "^17.3.1",
"ts-key-enum": "^2.0.12",
"tslib": "^2.3.0",
@@ -356,7 +357,8 @@
"type-fest": "4.10.1",
"typescript": "5.3.3",
"prosemirror-model": "1.23.0",
"yjs": "13.6.18"
"yjs": "13.6.18",
"graphql-redis-subscriptions/ioredis": "^5.6.0"
},
"version": "0.2.1",
"nx": {},
@@ -1,11 +1,16 @@
module.exports = {
extends: ['../../.eslintrc.cjs', '../../.eslintrc.react.cjs'],
ignorePatterns: ['!**/*', 'node_modules', 'dist', '**/generated/*'],
root: true,
extends: ['../../.eslintrc.global.cjs', '../../.eslintrc.react.cjs'],
ignorePatterns: [
'node_modules',
'dist',
'**/generated/*',
],
overrides: [
{
files: ['*.ts', '*.tsx'],
files: ['**/*.ts', '**/*.tsx'],
parserOptions: {
project: ['packages/twenty-chrome-extension/tsconfig.{json,*.json}'],
project: ['packages/twenty-chrome-extension/tsconfig.*.json'],
},
rules: {
'@nx/workspace-explicit-boolean-predicates-in-if': 'warn',
@@ -1,5 +1,4 @@
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
// Open options page programmatically in a new tab.
// chrome.runtime.onInstalled.addListener((details) => {
// if (details.reason === 'install') {
@@ -1,5 +1,4 @@
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
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 'twenty-shared/utils';
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 'twenty-shared/utils';
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 'twenty-shared/utils';
// 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,5 +1,4 @@
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
const btn = document.getElementById('twenty-settings-btn');
if (!isDefined(btn)) {
const div = document.createElement('div');
@@ -1,5 +1,4 @@
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
const changeSidePanelUrl = async (url: string) => {
if (isDefined(url)) {
chrome.storage.local.set({ navigateSidepanel: 'sidepanel' });
@@ -1,7 +1,4 @@
// 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 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
// "https://www.linkedin.com/company/twenty/" "https://www.linkedin.com/company/twenty/about/" "https://www.linkedin.com/company/twenty/people/".
const extractCompanyLinkedinLink = (activeTabUrl: string) => {
// Regular expression to match the company ID
@@ -1,4 +1,3 @@
import { isDefined } from 'twenty-shared';
import {
ExchangeAuthCodeInput,
ExchangeAuthCodeResponse,
@@ -6,6 +5,7 @@ import {
} from '~/db/types/auth.types';
import { EXCHANGE_AUTHORIZATION_CODE } from '~/graphql/auth/mutations';
import { callMutation } from '~/utils/requestDb';
import { isDefined } from 'twenty-shared/utils';
export const exchangeAuthorizationCode = async (
exchangeAuthCodeInput: ExchangeAuthCodeInput,
@@ -1,4 +1,3 @@
import { isDefined } from 'twenty-shared';
import {
CompanyInput,
CreateCompanyResponse,
@@ -9,6 +8,7 @@ import { CREATE_COMPANY } from '~/graphql/company/mutations';
import { FIND_COMPANY } from '~/graphql/company/queries';
import { callMutation, callQuery } from '../utils/requestDb';
import { isDefined } from 'twenty-shared/utils';
export const fetchCompany = async (
companyfilerInput: CompanyFilterInput,
@@ -1,4 +1,3 @@
import { isDefined } from 'twenty-shared';
import {
CreatePersonResponse,
FindPersonResponse,
@@ -9,6 +8,7 @@ import { CREATE_PERSON } from '~/graphql/person/mutations';
import { FIND_PERSON } from '~/graphql/person/queries';
import { callMutation, callQuery } from '../utils/requestDb';
import { isDefined } from 'twenty-shared/utils';
export const fetchPerson = async (
personFilterData: PersonFilterInput,
@@ -1,8 +1,7 @@
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 'twenty-shared/utils';
export const renewToken = async (
appToken: string,
@@ -1,8 +1,7 @@
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared';
import Settings from '~/options/Settings';
import Sidepanel from '~/options/Sidepanel';
import { isDefined } from 'twenty-shared/utils';
const App = () => {
const [currentScreen, setCurrentScreen] = useState('');
@@ -3,8 +3,8 @@ 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 'twenty-shared/utils';
const StyledWrapper = styled.div`
align-items: center;
@@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useCallback, useEffect, useRef, useState } from 'react';
import { MainButton } from '@/ui/input/button/MainButton';
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
const StyledIframe = styled.iframe`
display: block;
@@ -1,8 +1,7 @@
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
export type ToggleSize = 'small' | 'medium';
@@ -1,8 +1,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 'twenty-shared';
import { isDefined } from 'twenty-shared/utils';
export const clearStore = () => {
chrome.storage.local.remove([
@@ -1,8 +1,7 @@
import { OperationVariables } from '@apollo/client';
import { DocumentNode } from 'graphql';
import { isDefined } from 'twenty-shared';
import getApolloClient from '~/utils/apolloClient';
import { isDefined } from 'twenty-shared/utils';
export const callQuery = async <T>(
query: DocumentNode,
+18 -5
View File
@@ -1,14 +1,27 @@
# Makefile for building Twenty CRM docker images.
# Set the tag and/or target build platform using make command-line variables assignments.
#
# Optional make variables:
# PLATFORM - defaults to 'linux/amd64'
# TAG - defaults to 'latest'
#
# Example: make
# Example: make PLATFORM=linux/aarch64 TAG=my-tag
PLATFORM ?= linux/amd64
TAG ?= latest
prod-build:
@cd ../.. && docker build -f ./packages/twenty-docker/twenty/Dockerfile --tag twenty . && cd -
@cd ../.. && docker build -f ./packages/twenty-docker/twenty/Dockerfile --platform $(PLATFORM) --tag twenty:$(TAG) . && cd -
prod-run:
@docker run -d -p 3000:3000 --name twenty twenty
@docker run -d -p 3000:3000 --name twenty twenty:$(TAG)
prod-postgres-run:
@docker run -d -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres --name twenty-postgres twenty-postgres
@docker run -d -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres --name twenty-postgres twenty-postgres:$(TAG)
prod-website-build:
@cd ../.. && docker build -f ./packages/twenty-docker/twenty-website/Dockerfile --tag twenty-website . && cd -
@cd ../.. && docker build -f ./packages/twenty-docker/twenty-website/Dockerfile --platform $(PLATFORM) --tag twenty-website:$(TAG) . && cd -
prod-website-run:
@docker run -d -p 3000:3000 --name twenty-website twenty-website
@docker run -d -p 3000:3000 --name twenty-website twenty-website:$(TAG)
@@ -70,6 +70,8 @@ services:
worker:
image: twentycrm/twenty:${TAG:-latest}
volumes:
- server-local-data:/app/packages/twenty-server/${STORAGE_LOCAL_PATH:-.local-storage}
command: ["yarn", "worker:prod"]
environment:
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
+3
View File
@@ -1,4 +1,7 @@
# README
DISCLAIMER: The k8s and podman deployments are not maintained by the core team.
These files are provided and maintained by the community. Twenty core team
maintains support for docker deployment.
## Overview
+48
View File
@@ -0,0 +1,48 @@
# How to deploy twenty on podman
DISCLAIMER: The k8s and podman deployments are not maintained by the core team.
These files are provided and maintained by the community. Twenty core team
maintains support for docker deployment.
## How to use
1. Edit `.env` file. At the minimum set `POSTGRES_PASSWORD`, `SERVER_URL`, and `APP_SECRET`.
2. Start twenty by running `podman-compose up -d`.
If you need to stop twenty, you can do so by running `podman-compose down`.
### Install systemd service (optional)
If you want to install a systemd service to run twenty, you can use the provided systemd service.
Edit `twentycrm.service` and change these two variables:
WorkingDirectory=/opt/apps/twenty
EnvironmentFile=/opt/apps/twenty/.env
`WorkingDirectory` should be changed to the path in which `podman-compose.yml` is located.
`EnvironmentFile` should be changed to the path in which your `.env`file is located.
You can run the script `install-systemd-user-service` to install the systemd service under the current user.
./install-systemd-user-service
Note: this script will enable the service and also start it. So it will assume that twenty is not currently running.
If you started it previously, bring it down using:
podman-compose down
## Compatibility
These files should be compatible with podman 4.3+.
I have tested this on Debian GNU/Linux 12 (bookworm) and with the podman that is distributed with the official Debian stable mirrors (podman v4.3.1+ds1-8+deb12u1, podman-compose v1.0.3-3).
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
set -e
mkdir -p ~/.config/systemd/user
[ -f twentycrm.service ] || { echo "Error: twentycrm.service not found"; exit 1; }
cp twentycrm.service ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable twentycrm.service
systemctl --user start twentycrm.service
@@ -0,0 +1,71 @@
#!/bin/bash
set -e
if [ ! -f "$(dirname "$0")/.env" ]; then
echo "Error: .env file not found"
exit 1
fi
source "$(dirname "$0")/.env"
## steps to cleanup and start over from scratch - useful when testing
#podman pod stop twenty-pod
#podman pod rm twenty-pod
#podman volume rm twenty-db-data twenty-server-data twenty-redis-data
# end of cleanup
podman volume create twenty-db-data
podman volume create twenty-server-data
podman volume create twenty-redis-data
podman pod create --name twenty-pod -p 127.0.0.1:8080:3000
podman run -d --pod twenty-pod --name twenty-db \
-e POSTGRES_DB=twenty \
-e POSTGRES_USER=twenty \
-e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
-v twenty-db-data:/var/lib/postgresql/data:Z \
docker.io/library/postgres:16
podman run -d --pod twenty-pod --name twenty-redis \
-v twenty-redis-data:/data:Z \
docker.io/library/redis:latest
podman run -d --pod twenty-pod --name twenty-server \
-e NODE_PORT=3000 \
-e SERVER_URL="$SERVER_URL" \
-e PG_DATABASE_URL="postgresql://twenty:$POSTGRES_PASSWORD@twenty-db:5432/twenty" \
-e REDIS_URL="redis://twenty-redis:6379" \
-e APP_SECRET="$APP_SECRET" \
-e NODE_ENV=production \
-e LOG_LEVEL=info \
-v twenty-server-data:/app/docker-data:Z \
docker.io/twentycrm/twenty:latest
podman run -d --pod twenty-pod --name twenty-worker \
--init \
-e SERVER_URL="$SERVER_URL" \
-e PG_DATABASE_URL="postgresql://twenty:$POSTGRES_PASSWORD@twenty-db:5432/twenty" \
-e REDIS_URL="redis://twenty-redis:6379" \
-e APP_SECRET="$APP_SECRET" \
-e DISABLE_DB_MIGRATIONS=true \
-e NODE_ENV=production \
-e LOG_LEVEL=info \
-v twenty-server-data:/app/docker-data:Z \
docker.io/twentycrm/twenty:latest \
yarn worker:prod
# wait some time, check status
sleep 30
podman ps --pod -f name=twenty-pod
mkdir -p ~/.config/systemd/user
if [ ! -f "twentycrm.service" ]; then
echo "Error: twentycrm.service file not found"
exit 1
fi
cp twentycrm.service ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable twentycrm.service
systemctl --user start twentycrm.service
@@ -0,0 +1,55 @@
version: "3.8"
services:
db:
container_name: twenty-db
image: postgres:16
environment:
POSTGRES_DB: twenty
POSTGRES_USER: twenty
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- twenty-db-data:/var/lib/postgresql/data:Z
redis:
container_name: twenty-redis
image: redis:latest
volumes:
- twenty-redis-data:/data:Z
server:
container_name: twenty-server
image: twentycrm/twenty:latest
environment:
NODE_PORT: "3000"
SERVER_URL: ${SERVER_URL}
PG_DATABASE_URL: "postgresql://twenty:${POSTGRES_PASSWORD}@twenty-db:5432/twenty"
REDIS_URL: "redis://twenty-redis:6379"
APP_SECRET: ${APP_SECRET}
NODE_ENV: production
LOG_LEVEL: info
volumes:
- twenty-server-data:/app/docker-data:Z
ports:
- 127.0.0.1:8080:3000
worker:
container_name: twenty-worker
image: twentycrm/twenty:latest
command: yarn worker:prod
environment:
SERVER_URL: ${SERVER_URL}
PG_DATABASE_URL: "postgresql://twenty:${POSTGRES_PASSWORD}@twenty-db:5432/twenty"
REDIS_URL: "redis://twenty-redis:6379"
APP_SECRET: ${APP_SECRET}
DISABLE_DB_MIGRATIONS: "true"
NODE_ENV: production
LOG_LEVEL: info
volumes:
- twenty-server-data:/app/docker-data:Z
init: true
volumes:
twenty-db-data:
twenty-server-data:
twenty-redis-data:
@@ -0,0 +1,15 @@
[Unit]
Description=TwentyCRM Container Stack via Podman-Compose
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/apps/twenty
EnvironmentFile=/opt/apps/twenty/.env
ExecStart=/usr/bin/podman-compose -f podman-compose.yml up -d
ExecStop=/usr/bin/podman-compose -f podman-compose.yml down
RemainAfterExit=yes
[Install]
WantedBy=default.target
@@ -8,6 +8,8 @@ COPY ./yarn.lock .
COPY ./.yarnrc.yml .
COPY ./.yarn/releases /app/.yarn/releases
COPY ./tools/eslint-rules /app/tools/eslint-rules
COPY ./packages/twenty-ui/package.json /app/packages/twenty-ui/
COPY ./packages/twenty-shared/package.json /app/packages/twenty-shared/
COPY ./packages/twenty-website/package.json /app/packages/twenty-website/package.json
RUN yarn
@@ -17,6 +19,7 @@ ENV KEYSTATIC_GITHUB_CLIENT_SECRET="<fake build value>"
ENV KEYSTATIC_SECRET="<fake build value>"
ENV NEXT_PUBLIC_KEYSTATIC_GITHUB_APP_SLUG="<fake build value>"
COPY ./packages/twenty-ui /app/packages/twenty-ui
COPY ./packages/twenty-website /app/packages/twenty-website
RUN npx nx build twenty-website
+1 -2
View File
@@ -7,6 +7,7 @@ WORKDIR /app
COPY ./package.json ./yarn.lock ./.yarnrc.yml ./tsconfig.base.json ./nx.json /app/
COPY ./.yarn/releases /app/.yarn/releases
COPY ./.prettierrc /app/
COPY ./packages/twenty-emails/package.json /app/packages/twenty-emails/
COPY ./packages/twenty-server/package.json /app/packages/twenty-server/
COPY ./packages/twenty-server/patches /app/packages/twenty-server/patches
@@ -63,8 +64,6 @@ WORKDIR /app/packages/twenty-server
ARG REACT_APP_SERVER_BASE_URL
ENV REACT_APP_SERVER_BASE_URL $REACT_APP_SERVER_BASE_URL
ARG SENTRY_RELEASE
ENV SENTRY_RELEASE $SENTRY_RELEASE
ARG APP_VERSION
ENV APP_VERSION $APP_VERSION
+4 -2
View File
@@ -10,12 +10,14 @@ if [ "${DISABLE_DB_MIGRATIONS}" != "true" ] && [ ! -f /app/docker-data/db_status
PGPASS=$(echo $PG_DATABASE_URL | awk -F ':' '{print $3}' | awk -F '@' '{print $1}')
PGHOST=$(echo $PG_DATABASE_URL | awk -F '@' '{print $2}' | awk -F ':' '{print $1}')
PGPORT=$(echo $PG_DATABASE_URL | awk -F ':' '{print $4}' | awk -F '/' '{print $1}')
PGPASSWORD=${PGPASS} psql -h ${PGHOST} -p ${PGPORT} -U ${PGUSER} -d postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'default'" | grep -q 1 || \
PGPASSWORD=${PGPASS} psql -h ${PGHOST} -p ${PGPORT} -U ${PGUSER} -d postgres -c "CREATE DATABASE \"default\""
PGDATABASE=$(echo $PG_DATABASE_URL | awk -F ':' '{print $4}' | awk -F '/' '{print $2}')
PGPASSWORD=${PGPASS} psql -h ${PGHOST} -p ${PGPORT} -U ${PGUSER} -d postgres -tc "SELECT 1 FROM pg_database WHERE datname = '${PGDATABASE}'" | grep -q 1 || \
PGPASSWORD=${PGPASS} psql -h ${PGHOST} -p ${PGPORT} -U ${PGUSER} -d postgres -c "CREATE DATABASE \"${PGDATABASE}\""
# Run setup and migration scripts
NODE_OPTIONS="--max-old-space-size=1500" tsx ./scripts/setup-db.ts
yarn database:migrate:prod
yarn command:prod upgrade
# Mark initialization as done
echo "Successfuly migrated DB!"
+1 -15
View File
@@ -3,18 +3,4 @@ FRONTEND_BASE_URL=http://localhost:3001
BACKEND_BASE_URL=http://localhost:3000
DEFAULT_LOGIN=tim@apple.dev
DEFAULT_PASSWORD=Applecar2025
WEBSITE_URL=https://twenty.com
# === DO NOT USE, WORK IN PROGRESS ===
# This URL must have trailing forward slash as all REST API endpoints have object after it
# Documentation for REST API: https://twenty.com/developers/rest-api/core#/
# REST_API_BASE_URL=http://localhost:3000/rest/
# Documentation for GraphQL API: https://twenty.com/developers/graphql/core
# GRAPHQL_BASE_URL=http://localhost:3000/graphql
# Without this key, all API tests will fail, to generate this key
# Log in to Twenty workspace, go to Settings > Developers, generate new key and paste it here
# In order to use it, header Authorization: Bearer token must be used
# This key works for both REST and GraphQL API
# API_DEV_KEY=fill_with_proper_key
WEBSITE_URL=https://twenty.com
@@ -17,6 +17,7 @@ export class WorkflowVisualizerPage {
readonly deactivateWorkflowButton: Locator;
readonly addTriggerButton: Locator;
readonly commandMenu: Locator;
readonly stepHeaderInCommandMenu: Locator;
readonly workflowNameLabel: Locator;
readonly triggerNode: Locator;
readonly background: Locator;
@@ -68,6 +69,9 @@ export class WorkflowVisualizerPage {
});
this.addTriggerButton = page.getByText('Add a Trigger');
this.commandMenu = page.getByTestId('command-menu');
this.stepHeaderInCommandMenu = this.commandMenu.getByTestId(
'workflow-step-header',
);
this.workflowNameLabel = page
.getByTestId('top-bar-title')
.getByText(this.workflowName);
@@ -114,7 +118,7 @@ export class WorkflowVisualizerPage {
.getByTestId(`row-id-${this.workflowId}`)
.getByRole('link', { name: this.workflowName });
await workflowLink.click();
await workflowLink.click({ force: true });
await this.waitForWorkflowVisualizerLoad();
}
@@ -25,7 +25,7 @@ export const test = base.extend<{ screenshotHook: void }>({
),
});
},
{ auto: true }, // automatic fixture runs with every test
{ auto: true },
],
});
@@ -44,7 +44,7 @@ export class InsertFieldData {
'//label[contains(., "POST CODE")]/../div[last()]/input',
);
this.countrySelect = page.locator(
'//span[contains(., "COUNTRY")]/../div[last()]/input',
'//span[contains(., "COUNTRY")]/../div[last()]/div',
);
this.arrayValueInput = page.locator("//input[@placeholder='Enter value']");
this.arrayAddValueButton = page.locator(
@@ -1,5 +1,55 @@
import { Locator, Page } from '@playwright/test';
export class StripePage {
// TODO: implement all necessary methods (staging/sandbox page - does it differ anyhow from normal page?)
private readonly cardNumberInput: Locator;
private readonly cardExpiryInput: Locator;
private readonly cardCvcInput: Locator;
private readonly cardholderNameInput: Locator;
private readonly startTrialButton: Locator;
private readonly cancelSubscriptionButton: Locator;
private readonly confirmButton: Locator;
private readonly returnToTwentyLink: Locator;
constructor(public readonly page: Page) {
this.cardNumberInput = page.getByPlaceholder('1234 1234 1234 1234');
this.cardExpiryInput = page.getByPlaceholder('MM / YY');
this.cardCvcInput = page.getByPlaceholder('CVC');
this.cardholderNameInput = page.getByPlaceholder('Full name on card');
this.startTrialButton = page.getByTestId('hosted-payment-submit-button');
this.cancelSubscriptionButton = page.locator(
'a[data-test="cancel-subscription"]',
);
this.confirmButton = page.getByTestId('confirm');
this.returnToTwentyLink = page.getByTestId('return-to-business-link');
}
async fillCardNumber(number: string) {
await this.cardNumberInput.fill(number);
}
async fillCardExpiry(expiry: string) {
await this.cardExpiryInput.fill(expiry);
}
async fillCardCvc(cvc: string) {
await this.cardCvcInput.fill(cvc);
}
async fillCardholderName(name: string) {
await this.cardholderNameInput.fill(name);
}
async startTrial() {
await this.startTrialButton.click();
}
async clickCancelSubscription() {
await this.cancelSubscriptionButton.click();
await this.confirmButton.click();
await this.page.getByTestId('cancellation_reason_cancel').click();
}
async returnToTwenty() {
await this.returnToTwentyLink.click();
}
}
@@ -6,6 +6,7 @@ export class AccountsSection {
private readonly addBlocklistField: Locator;
private readonly addBlocklistButton: Locator;
private readonly connectWithGoogleButton: Locator;
private readonly connectWithMicrosoftButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
@@ -22,6 +23,9 @@ export class AccountsSection {
this.connectWithGoogleButton = page.getByRole('button', {
name: 'Connect with Google',
});
this.connectWithMicrosoftButton = page.getByRole('button', {
name: 'Connect with Microsoft',
});
}
async clickAddAccount() {
@@ -51,4 +55,8 @@ export class AccountsSection {
async linkGoogleAccount() {
await this.connectWithGoogleButton.click();
}
async linkMicrosoftAccount() {
await this.connectWithMicrosoftButton.click();
}
}
@@ -0,0 +1,64 @@
import { Locator, Page } from '@playwright/test';
export class APIsSection {
private readonly createAPIKeyButton: Locator;
private readonly regenerateAPIKeyButton: Locator;
private readonly nameOfAPIKeyInput: Locator;
private readonly expirationDateAPIKeySelect: Locator;
private readonly cancelButton: Locator;
private readonly saveButton: Locator;
private readonly deleteButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.createAPIKeyButton = page.getByRole('link', {
name: 'Create API Key',
});
this.nameOfAPIKeyInput = page
.getByPlaceholder('E.g. backoffice integration')
.first();
this.expirationDateAPIKeySelect = page.locator(
'div[aria-controls="object-field-type-select-options"]',
);
this.regenerateAPIKeyButton = page.getByRole('button', {
name: 'Regenerate Key',
});
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
this.saveButton = page.getByRole('button', { name: 'Save' });
this.deleteButton = page.getByRole('button', { name: 'Delete' });
}
async createAPIKey() {
await this.createAPIKeyButton.click();
}
async typeAPIKeyName(name: string) {
await this.nameOfAPIKeyInput.clear();
await this.nameOfAPIKeyInput.fill(name);
}
async selectAPIExpirationDate(date: string) {
await this.expirationDateAPIKeySelect.click();
await this.page.getByText(date, { exact: true }).click();
}
async regenerateAPIKey() {
await this.regenerateAPIKeyButton.click();
}
async deleteAPIKey() {
await this.deleteButton.click();
}
async clickCancelButton() {
await this.cancelButton.click();
}
async clickSaveButton() {
await this.saveButton.click();
}
async checkAPIKeyDetails(name: string) {
await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
}
}
@@ -1,123 +0,0 @@
import { Locator, Page } from '@playwright/test';
export class DevelopersSection {
private readonly readDocumentationButton: Locator;
private readonly createAPIKeyButton: Locator;
private readonly regenerateAPIKeyButton: Locator;
private readonly nameOfAPIKeyInput: Locator;
private readonly expirationDateAPIKeySelect: Locator;
private readonly createWebhookButton: Locator;
private readonly webhookURLInput: Locator;
private readonly webhookDescription: Locator;
private readonly webhookFilterObjectSelect: Locator;
private readonly webhookFilterActionSelect: Locator;
private readonly cancelButton: Locator;
private readonly saveButton: Locator;
private readonly deleteButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.readDocumentationButton = page.getByRole('link', {
name: 'Read documentation',
});
this.createAPIKeyButton = page.getByRole('link', {
name: 'Create API Key',
});
this.createWebhookButton = page.getByRole('link', {
name: 'Create Webhook',
});
this.nameOfAPIKeyInput = page
.getByPlaceholder('E.g. backoffice integration')
.first();
this.expirationDateAPIKeySelect = page
.locator('div')
.filter({ hasText: /^Never$/ })
.nth(3); // good enough if expiration date will change only 1 time
this.regenerateAPIKeyButton = page.getByRole('button', {
name: 'Regenerate Key',
});
this.webhookURLInput = page.getByPlaceholder('URL');
this.webhookDescription = page.getByPlaceholder('Write a description');
this.webhookFilterObjectSelect = page
.locator('div')
.filter({ hasText: /^All Objects$/ })
.nth(3); // works only for first filter
this.webhookFilterActionSelect = page
.locator('div')
.filter({ hasText: /^All Actions$/ })
.nth(3); // works only for first filter
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
this.saveButton = page.getByRole('button', { name: 'Save' });
this.deleteButton = page.getByRole('button', { name: 'Delete' });
}
async openDocumentation() {
await this.readDocumentationButton.click();
}
async createAPIKey() {
await this.createAPIKeyButton.click();
}
async typeAPIKeyName(name: string) {
await this.nameOfAPIKeyInput.clear();
await this.nameOfAPIKeyInput.fill(name);
}
async selectAPIExpirationDate(date: string) {
await this.expirationDateAPIKeySelect.click();
await this.page.getByText(date, { exact: true }).click();
}
async regenerateAPIKey() {
await this.regenerateAPIKeyButton.click();
}
async deleteAPIKey() {
await this.deleteButton.click();
}
async deleteWebhook() {
await this.deleteButton.click();
}
async createWebhook() {
await this.createWebhookButton.click();
}
async typeWebhookURL(url: string) {
await this.webhookURLInput.fill(url);
}
async typeWebhookDescription(description: string) {
await this.webhookDescription.fill(description);
}
async selectWebhookObject(object: string) {
// TODO: finish
}
async selectWebhookAction(action: string) {
// TODO: finish
}
async deleteWebhookFilter() {
// TODO: finish
}
async clickCancelButton() {
await this.cancelButton.click();
}
async clickSaveButton() {
await this.saveButton.click();
}
async checkAPIKeyDetails(name: string) {
await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
}
async checkWebhookDetails(name: string) {
await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
}
}
@@ -7,11 +7,12 @@ export class ExperienceSection {
private readonly dateFormatDropdown: Locator;
private readonly timeFormatDropdown: Locator;
private readonly searchInput: Locator;
private readonly languageDropdown: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.lightThemeButton = page.getByText('AaLight'); // it works
this.darkThemeButton = page.getByText('AaDark');
this.lightThemeButton = page.locator('div[variant="Light"]').first();
this.darkThemeButton = page.locator('div[variant="Dark"]').first();
this.timezoneDropdown = page.locator(
'//span[contains(., "Time zone")]/../div/div/div',
);
@@ -22,6 +23,9 @@ export class ExperienceSection {
'//span[contains(., "Time format")]/../div/div/div',
);
this.searchInput = page.getByPlaceholder('Search');
this.languageDropdown = page.locator(
'//h2[contains(., "Language")]/../../../div/div/div',
);
}
async changeThemeToLight() {
@@ -52,4 +56,9 @@ export class ExperienceSection {
await this.timeFormatDropdown.click();
await this.page.getByText(timeFormat, { exact: true }).click();
}
async selectLanguage(language: string) {
await this.languageDropdown.click();
await this.page.getByText(language, { exact: true }).click();
}
}
@@ -1,159 +0,0 @@
import { Locator, Page } from '@playwright/test';
export class FunctionsSection {
private readonly newFunctionButton: Locator;
private readonly functionNameInput: Locator;
private readonly functionDescriptionInput: Locator;
private readonly editorTab: Locator;
private readonly codeEditorField: Locator;
private readonly resetButton: Locator;
private readonly publishButton: Locator;
private readonly testButton: Locator;
private readonly testTab: Locator;
private readonly runFunctionButton: Locator;
private readonly inputField: Locator;
private readonly settingsTab: Locator;
private readonly searchVariableInput: Locator;
private readonly addVariableButton: Locator;
private readonly nameVariableInput: Locator;
private readonly valueVariableInput: Locator;
private readonly cancelVariableButton: Locator;
private readonly saveVariableButton: Locator;
private readonly editVariableButton: Locator;
private readonly deleteVariableButton: Locator;
private readonly cancelButton: Locator;
private readonly saveButton: Locator;
private readonly deleteButton: Locator;
constructor(public readonly page: Page) {
this.newFunctionButton = page.getByRole('button', { name: 'New Function' });
this.functionNameInput = page.getByPlaceholder('Name');
this.functionDescriptionInput = page.getByPlaceholder('Description');
this.editorTab = page.getByTestId('tab-editor');
this.codeEditorField = page.getByTestId('dummyInput'); // TODO: fix
this.resetButton = page.getByRole('button', { name: 'Reset' });
this.publishButton = page.getByRole('button', { name: 'Publish' });
this.testButton = page.getByRole('button', { name: 'Test' });
this.testTab = page.getByTestId('tab-test');
this.runFunctionButton = page.getByRole('button', { name: 'Run Function' });
this.inputField = page.getByTestId('dummyInput'); // TODO: fix
this.settingsTab = page.getByTestId('tab-settings');
this.searchVariableInput = page.getByPlaceholder('Search a variable');
this.addVariableButton = page.getByRole('button', { name: 'Add Variable' });
this.nameVariableInput = page.getByPlaceholder('Name').nth(1);
this.valueVariableInput = page.getByPlaceholder('Value');
this.cancelVariableButton = page.locator('.css-uwqduk').first(); // TODO: fix
this.saveVariableButton = page.locator('.css-uwqduk').nth(1); // TODO: fix
this.editVariableButton = page.getByText('Edit', { exact: true });
this.deleteVariableButton = page.getByText('Delete', { exact: true });
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
this.saveButton = page.getByRole('button', { name: 'Save' });
this.deleteButton = page.getByRole('button', { name: 'Delete function' });
}
async clickNewFunction() {
await this.newFunctionButton.click();
}
async typeFunctionName(name: string) {
await this.functionNameInput.fill(name);
}
async typeFunctionDescription(description: string) {
await this.functionDescriptionInput.fill(description);
}
async checkFunctionDetails(name: string) {
await this.page.getByRole('link', { name: `${name} nodejs18.x` }).click();
}
async clickEditorTab() {
await this.editorTab.click();
}
async clickResetButton() {
await this.resetButton.click();
}
async clickPublishButton() {
await this.publishButton.click();
}
async clickTestButton() {
await this.testButton.click();
}
async typeFunctionCode() {
// TODO: finish once utils are merged
}
async clickTestTab() {
await this.testTab.click();
}
async runFunction() {
await this.runFunctionButton.click();
}
async typeFunctionInput() {
// TODO: finish once utils are merged
}
async clickSettingsTab() {
await this.settingsTab.click();
}
async searchVariable(name: string) {
await this.searchVariableInput.fill(name);
}
async addVariable() {
await this.addVariableButton.click();
}
async typeVariableName(name: string) {
await this.nameVariableInput.fill(name);
}
async typeVariableValue(value: string) {
await this.valueVariableInput.fill(value);
}
async editVariable(name: string) {
await this.page
.locator(
`//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`,
)
.click();
await this.editVariableButton.click();
}
async deleteVariable(name: string) {
await this.page
.locator(
`//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`,
)
.click();
await this.deleteVariableButton.click();
}
async cancelVariable() {
await this.cancelVariableButton.click();
}
async saveVariable() {
await this.saveVariableButton.click();
}
async clickCancelButton() {
await this.cancelButton.click();
}
async clickSaveButton() {
await this.saveButton.click();
}
async clickDeleteButton() {
await this.deleteButton.click();
}
}
@@ -4,6 +4,8 @@ export class GeneralSection {
private readonly workspaceNameField: Locator;
private readonly supportSwitch: Locator;
private readonly deleteWorkspaceButton: Locator;
private readonly customizeDomainButton: Locator;
private readonly subdomainInput: Locator;
constructor(public readonly page: Page) {
this.page = page;
@@ -12,6 +14,12 @@ export class GeneralSection {
this.deleteWorkspaceButton = page.getByRole('button', {
name: 'Delete workspace',
});
this.customizeDomainButton = page.getByRole('button', {
name: 'Customize domain',
});
this.subdomainInput = page.locator(
'//div[contains(.,".twenty-main.com")]/../input',
);
}
async changeWorkspaceName(workspaceName: string) {
@@ -26,4 +34,10 @@ export class GeneralSection {
async clickDeleteWorkSpaceButton() {
await this.deleteWorkspaceButton.click();
}
async changeSubdomain(subdomain: string) {
await this.customizeDomainButton.click();
await this.subdomainInput.fill(subdomain);
await this.page.getByRole('button', { name: 'Save' }).click();
}
}
@@ -29,6 +29,7 @@ export class NewFieldSection {
private readonly relationFieldLink: Locator;
private readonly relationTypeSelect: Locator;
private readonly objectDestinationSelect: Locator;
private readonly relationIconSelect: Locator;
private readonly relationFieldNameInput: Locator;
private readonly fullNameFieldLink: Locator;
private readonly UUIDFieldLink: Locator;
@@ -225,6 +226,11 @@ export class NewFieldSection {
await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
}
async selectRelationIcon(name: string) {
await this.relationIconSelect.click();
await this.page.getByTitle(name).click();
}
async typeRelationName(name: string) {
await this.relationFieldNameInput.clear();
await this.relationFieldNameInput.fill(name);
@@ -0,0 +1,22 @@
import { Locator, Page } from '@playwright/test';
export class RolesSection {
private readonly page: Page;
private readonly createRoleButton: Locator;
private readonly defaultRoleDropdown: Locator;
constructor(page: Page) {
this.page = page;
this.createRoleButton = page.getByRole('button', { name: 'Create Role' });
this.defaultRoleDropdown = page.getByTestId('tooltip');
}
async clickCreateRoleButton() {
await this.createRoleButton.click();
}
async selectDefaultRole(role: string) {
await this.defaultRoleDropdown.click();
await this.page.getByTestId('tooltip').getByText(role).click();
}
}
@@ -1,10 +1,28 @@
import { Locator, Page } from '@playwright/test';
export class SecuritySection {
private readonly googleToggle: Locator;
private readonly microsoftToggle: Locator;
private readonly passwordToggle: Locator;
private readonly inviteByLinkToggle: Locator;
constructor(public readonly page: Page) {
this.inviteByLinkToggle = page.locator('input[type="checkbox"]').nth(1);
this.googleToggle = page.getByLabel('Google');
this.microsoftToggle = page.getByLabel('Microsoft');
this.passwordToggle = page.getByLabel('Password');
this.inviteByLinkToggle = page.getByLabel('Invite by Link');
}
async toggleGoogle() {
await this.googleToggle.click();
}
async toggleMicrosoft() {
await this.microsoftToggle.click();
}
async togglePassword() {
await this.passwordToggle.click();
}
async toggleInviteByLink() {
@@ -0,0 +1,65 @@
import { Locator, Page } from '@playwright/test';
export class WebhooksSection {
private readonly createWebhookButton: Locator;
private readonly webhookURLInput: Locator;
private readonly webhookDescription: Locator;
private readonly deleteButton: Locator;
constructor(public readonly page: Page) {
this.page = page;
this.createWebhookButton = page.getByRole('link', {
name: 'Create Webhook',
});
this.webhookURLInput = page.getByPlaceholder('URL');
this.webhookDescription = page.getByPlaceholder('Write a description');
this.deleteButton = page.getByRole('button', { name: 'Delete' });
}
async deleteWebhook() {
await this.deleteButton.click();
}
async createWebhook() {
await this.createWebhookButton.click();
}
async typeWebhookURL(url: string) {
await this.webhookURLInput.clear();
await this.webhookURLInput.fill(url);
}
async typeWebhookDescription(description: string) {
await this.webhookDescription.fill(description);
}
async selectWebhookObject(index: number, object: string) {
await this.page
.locator(
`//div[aria-controls="object-webhook-type-select-${index}-options"]`,
)
.click();
await this.page.getByRole('option').getByText(object).click();
}
async selectWebhookAction(index: number, action: string) {
await this.page
.locator(
`//div[aria-controls="operation-webhook-type-select-${index}-options"]`,
)
.click();
await this.page.getByRole('option').getByText(action).click();
}
async deleteWebhookFilter(index: number) {
await this.page
.locator(
`//div[aria-controls="object-webhook-type-select-${index}-options"]/../..//button`,
)
.click();
}
async checkWebhookDetails(name: string) {
await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
}
}
@@ -9,13 +9,15 @@ export class SettingsPage {
private readonly calendarsLink: Locator;
private readonly generalLink: Locator;
private readonly membersLink: Locator;
private readonly rolesLink: Locator;
private readonly dataModelLink: Locator;
private readonly developersLink: Locator;
private readonly functionsLink: Locator;
private readonly securityLink: Locator;
private readonly integrationsLink: Locator;
private readonly securityLink: Locator;
private readonly apisLink: Locator;
private readonly webhooksLink: Locator;
private readonly adminPanelLink: Locator;
private readonly labLink: Locator;
private readonly releasesLink: Locator;
private readonly logoutLink: Locator;
private readonly advancedToggle: Locator;
constructor(public readonly page: Page) {
@@ -28,13 +30,15 @@ export class SettingsPage {
this.calendarsLink = page.getByRole('link', { name: 'Calendars' });
this.generalLink = page.getByRole('link', { name: 'General' });
this.membersLink = page.getByRole('link', { name: 'Members' });
this.rolesLink = page.getByRole('link', { name: 'Roles' });
this.dataModelLink = page.getByRole('link', { name: 'Data model' });
this.developersLink = page.getByRole('link', { name: 'Developers' });
this.functionsLink = page.getByRole('link', { name: 'Functions' });
this.integrationsLink = page.getByRole('link', { name: 'Integrations' });
this.securityLink = page.getByRole('link', { name: 'Security' });
this.apisLink = page.getByRole('link', { name: 'APIs' });
this.webhooksLink = page.getByRole('link', { name: 'Webhooks' });
this.adminPanelLink = page.getByRole('link', { name: 'Admin Panel' });
this.labLink = page.getByRole('link', { name: 'Lab' });
this.releasesLink = page.getByRole('link', { name: 'Releases' });
this.logoutLink = page.getByText('Logout');
this.advancedToggle = page.locator('input[type="checkbox"]').first();
}
@@ -70,24 +74,36 @@ export class SettingsPage {
await this.membersLink.click();
}
async goToRolesSection() {
await this.rolesLink.click();
}
async goToDataModelSection() {
await this.dataModelLink.click();
}
async goToDevelopersSection() {
await this.developersLink.click();
}
async goToFunctionsSection() {
await this.functionsLink.click();
async goToIntegrationsSection() {
await this.integrationsLink.click();
}
async goToSecuritySection() {
await this.securityLink.click();
}
async goToIntegrationsSection() {
await this.integrationsLink.click();
async goToAPIsSection() {
await this.apisLink.click();
}
async goToWebhooksSection() {
await this.webhooksLink.click();
}
async goToAdminPanelSection() {
await this.adminPanelLink.click();
}
async goToLabSection() {
await this.labLink.click();
}
async goToReleasesIntegration() {
@@ -95,7 +111,7 @@ export class SettingsPage {
}
async logout() {
await this.logoutLink.click();
await this.page.getByText('Logout').click();
}
async toggleAdvancedSettings() {
+1 -2
View File
@@ -1,11 +1,10 @@
{
"name": "twenty-e2e-testing",
"version": "0.44.5",
"description": "",
"author": "",
"private": true,
"license": "AGPL-3.0",
"devDependencies": {
"@playwright/test": "^1.49.0"
"@playwright/test": "^1.51.0"
}
}
@@ -10,7 +10,9 @@ test('Create workflow', async ({ page }) => {
const workflowsLink = page.getByRole('link', { name: 'Workflows' });
await workflowsLink.click();
const createWorkflowButton = page.getByRole('button', { name: 'New record' });
const createWorkflowButton = page.getByRole('button', {
name: 'Create new workflow',
});
const [createWorkflowResponse] = await Promise.all([
page.waitForResponse(async (response) => {
@@ -26,7 +28,7 @@ test('Create workflow', async ({ page }) => {
createWorkflowButton.click(),
]);
const recordName = page.getByTestId('top-bar-title').getByTestId('tooltip');
const recordName = page.getByTestId('top-bar-title').getByText('Untitled');
await recordName.click();
const nameInput = page.getByTestId('top-bar-title').getByRole('textbox');
@@ -1,60 +1,58 @@
import { expect } from '@playwright/test';
import { test } from '../lib/fixtures/blank-workflow';
test.fixme(
'The workflow run visualizer shows the executed draft version without the last draft changes',
async ({ workflowVisualizer, page }) => {
await workflowVisualizer.createInitialTrigger('manual');
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',
});
const manualTriggerAvailabilitySelect = page.getByRole('button', {
name: 'When record(s) are selected',
});
await manualTriggerAvailabilitySelect.click();
await manualTriggerAvailabilitySelect.click();
const alwaysAvailableOption = page.getByText(
'When no record(s) are selected',
);
const alwaysAvailableOption = page.getByText(
'When no record(s) are selected',
);
await alwaysAvailableOption.click();
await alwaysAvailableOption.click();
await workflowVisualizer.closeSidePanel();
await workflowVisualizer.closeSidePanel();
const { createdStepId: firstStepId } =
await workflowVisualizer.createStep('create-record');
const { createdStepId: firstStepId } =
await workflowVisualizer.createStep('create-record');
await workflowVisualizer.closeSidePanel();
await workflowVisualizer.closeSidePanel();
const launchTestButton = page.getByLabel('Test Workflow');
const launchTestButton = page.getByLabel(workflowVisualizer.workflowName);
await launchTestButton.click();
await launchTestButton.click();
const goToExecutionPageLink = page.getByRole('link', {
name: 'View execution details',
});
const executionPageUrl = await goToExecutionPageLink.getAttribute('href');
expect(executionPageUrl).not.toBeNull();
const goToExecutionPageLink = page.getByRole('link', {
name: 'View execution details',
});
const executionPageUrl = await goToExecutionPageLink.getAttribute('href');
expect(executionPageUrl).not.toBeNull();
await workflowVisualizer.deleteStep(firstStepId);
await workflowVisualizer.deleteStep(firstStepId);
await page.goto(executionPageUrl!);
await page.goto(executionPageUrl!);
const workflowRunName = page.getByText('Execution of v1');
const workflowRunName = page
.getByText(`#1 - ${workflowVisualizer.workflowName}`)
.nth(1);
await expect(workflowRunName).toBeVisible();
await expect(workflowRunName).toBeVisible();
const flowTab = page.getByText('Flow', { exact: true });
const executedFirstStepNode = workflowVisualizer.getStepNode(firstStepId);
await flowTab.click();
await expect(executedFirstStepNode).toBeVisible();
const executedFirstStepNode = workflowVisualizer.getStepNode(firstStepId);
await executedFirstStepNode.click();
await expect(executedFirstStepNode).toBeVisible();
await executedFirstStepNode.click();
await expect(
workflowVisualizer.commandMenu.getByRole('textbox').first(),
).toHaveValue('Create Record');
},
);
await expect(workflowVisualizer.stepHeaderInCommandMenu).toContainText(
'Create Record',
);
});
@@ -57,7 +57,7 @@ test('Use an old version as draft', async ({ workflowVisualizer, page }) => {
},
);
await linkToFirstWorkflowVersion.click();
await linkToFirstWorkflowVersion.click({ force: true });
await expect(workflowVisualizer.workflowStatus).toHaveText('Archived');
await expect(workflowVisualizer.useAsDraftButton).toBeVisible();
@@ -139,7 +139,7 @@ test('Use an old version as draft while having a pending draft version', async (
},
);
await linkToFirstWorkflowVersion.click();
await linkToFirstWorkflowVersion.click({ force: true });
await expect(workflowVisualizer.workflowStatus).toHaveText('Active');
await expect(workflowVisualizer.useAsDraftButton).toBeVisible();
-12
View File
@@ -1,12 +0,0 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
+3 -4
View File
@@ -1,11 +1,10 @@
module.exports = {
extends: ['../../.eslintrc.cjs', '../../.eslintrc.react.cjs'],
ignorePatterns: ['!**/*'],
extends: ['../../.eslintrc.global.cjs', '../../.eslintrc.react.cjs'],
overrides: [
{
files: ['*.ts', '*.tsx'],
files: ['**/*.ts', '**/*.tsx'],
parserOptions: {
project: ['packages/twenty-emails/tsconfig.{json,*.json}'],
project: ['packages/twenty-emails/tsconfig.*.json'],
},
rules: {
'@nx/dependency-checks': 'error',
+1
View File
@@ -1 +1,2 @@
dist
.react-email/
+33
View File
@@ -0,0 +1,33 @@
# Twenty Emails
This package contains the email templates used by Twenty.
## Features
- Email templates built with [React Email](https://react.email/)
- Internationalization (i18n) support via [@lingui/react](https://lingui.dev/)
- Local preview server for testing email templates
## Getting Started
### Starting the Local Preview Server
To start the local preview server for email development:
```bash
npx nx start twenty-emails
```
This will run the development server on port 4001. You can then view your email templates at [http://localhost:4001](http://localhost:4001).
### Building Emails
To build the email templates:
```bash
npx nx build twenty-emails
```
## Email Structure
Each email template is located in the `src/emails` directory. The templates use various components from the `src/components` directory to maintain consistent styling and functionality.
@@ -1,13 +1,13 @@
import { defineConfig } from '@lingui/cli';
import { defineConfig } from '@lingui/conf';
import { formatter } from '@lingui/format-po';
import { APP_LOCALES } from 'twenty-shared';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
export default defineConfig({
sourceLocale: 'en',
locales: Object.values(APP_LOCALES),
pseudoLocale: 'pseudo-en',
fallbackLocales: {
'pseudo-en': 'en',
default: SOURCE_LOCALE,
},
extractorParserOptions: {
tsExperimentalDecorators: true,
+8 -2
View File
@@ -1,6 +1,5 @@
{
"name": "twenty-emails",
"version": "0.44.5",
"description": "",
"author": "",
"private": true,
@@ -14,10 +13,17 @@
"@lingui/react": "^5.1.2",
"twenty-shared": "workspace:*"
},
"peerDependencies": {
"react": "^18.2.0 || ^19.0.0",
"react-dom": "^18.2.0 || ^19.0.0"
},
"devDependencies": {
"@lingui/cli": "^5.1.2",
"@lingui/swc-plugin": "^5.1.0",
"@lingui/vite-plugin": "^5.1.2"
"@lingui/vite-plugin": "^5.1.2",
"@types/react": "^19",
"@types/react-dom": "^19",
"react-email": "4.0.3"
},
"exports": {
".": {
+7
View File
@@ -11,6 +11,13 @@
},
"dependsOn": ["^build"]
},
"start": {
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"command": "email dev -d src/emails -p 4001"
}
},
"typecheck": {},
"lint": {
"options": {
@@ -1,11 +1,11 @@
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 { Footer } from 'src/components/Footer';
import { Logo } from 'src/components/Logo';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
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';
@@ -38,10 +38,11 @@ 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<{
type BaseEmailProps = {
children: JSX.Element | JSX.Element[] | string;
width?: number;
locale: keyof typeof APP_LOCALES;
}>;
};
const messages: Record<keyof typeof APP_LOCALES, Messages> = {
en: enMessages,
@@ -95,6 +96,7 @@ export const BaseEmail = ({ children, width, locale }: BaseEmailProps) => {
<Container width={width || 290}>
<Logo />
{children}
<Footer />
</Container>
</Html>
</I18nProvider>
@@ -1,5 +1,4 @@
import { ReactNode } from 'react';
import { Button } from '@react-email/button';
import { Button } from '@react-email/components';
import { emailTheme } from 'src/common-style';
@@ -17,7 +16,7 @@ const callToActionStyle = {
type CallToActionProps = {
href: string;
value: ReactNode;
value: JSX.Element | JSX.Element[] | string;
};
export const CallToAction = ({ value, href }: CallToActionProps) => {
@@ -1,17 +1,22 @@
import { Column, Row } from '@react-email/components';
import { i18n } from '@lingui/core';
import { Column, Container, Row } from '@react-email/components';
import { Link } from 'src/components/Link';
import { ShadowText } from 'src/components/ShadowText';
const footerContainerStyle = {
marginTop: '12px',
};
export const Footer = () => {
return (
<>
<Container style={footerContainerStyle}>
<Row>
<Column>
<ShadowText>
<Link
href="https://twenty.com/"
value="Website"
aria-label="Visit Twenty's website"
value={i18n._('Website')}
aria-label={i18n._("Visit Twenty's website")}
/>
</ShadowText>
</Column>
@@ -19,8 +24,8 @@ export const Footer = () => {
<ShadowText>
<Link
href="https://github.com/twentyhq/twenty"
value="Github"
aria-label="Visit Twenty's GitHub repository"
value={i18n._('Github')}
aria-label={i18n._("Visit Twenty's GitHub repository")}
/>
</ShadowText>
</Column>
@@ -28,8 +33,8 @@ export const Footer = () => {
<ShadowText>
<Link
href="https://twenty.com/user-guide"
value="User guide"
aria-label="Read Twenty's user guide"
value={i18n._('User guide')}
aria-label={i18n._("Read Twenty's user guide")}
/>
</ShadowText>
</Column>
@@ -37,19 +42,19 @@ export const Footer = () => {
<ShadowText>
<Link
href="https://docs.twenty.com/"
value="Developers"
aria-label="Visit Twenty's developer documentation"
value={i18n._('Developers')}
aria-label={i18n._("Visit Twenty's developer documentation")}
/>
</ShadowText>
</Column>
</Row>
<ShadowText>
Twenty.com Public Benefit Corporation
<br />
2261 Market Street #5275
<br />
San Francisco, CA 94114
<>
{i18n._('Twenty.com, Public Benefit Corporation')}
<br />
{i18n._('San Francisco / Paris')}
</>
</ShadowText>
</>
</Container>
);
};
@@ -1,9 +1,11 @@
import { Column, Container, Row } from '@react-email/components';
import React, { PropsWithChildren } from 'react';
import React from 'react';
import { emailTheme } from 'src/common-style';
type HighlightedContainerProps = PropsWithChildren;
type HighlightedContainerProps = {
children: JSX.Element | JSX.Element[] | string;
};
const highlightedContainerStyle = {
background: emailTheme.background.colors.highlight,
@@ -1,5 +1,5 @@
import { Text } from '@react-email/text';
import { ReactNode } from 'react';
import { Text } from '@react-email/components';
import { JSX } from 'react';
import { emailTheme } from 'src/common-style';
@@ -13,7 +13,7 @@ const highlightedStyle = {
};
type HighlightedTextProps = {
value: ReactNode;
value: JSX.Element | JSX.Element[] | string | undefined;
centered?: boolean;
};
@@ -1,5 +1,4 @@
import { Link as EmailLink } from '@react-email/components';
import { ReactNode } from 'react';
import { emailTheme } from 'src/common-style';
@@ -8,7 +7,7 @@ const linkStyle = {
};
type LinkProps = {
value: ReactNode;
value: JSX.Element | JSX.Element[] | string;
href: string;
color?: string;
};
@@ -1,14 +1,16 @@
import { PropsWithChildren as MainTextProps } from 'react';
import { Text } from '@react-email/text';
import { Text } from '@react-email/components';
import { emailTheme } from 'src/common-style';
type MainTextProps = {
children: JSX.Element | JSX.Element[] | string;
};
const mainTextStyle = {
fontFamily: emailTheme.font.family,
fontSize: emailTheme.font.size.md,
fontWeight: emailTheme.font.weight.regular,
color: emailTheme.font.colors.primary,
margin: '0 0 12px 0',
lineHeight: emailTheme.font.lineHeight,
};
@@ -1,8 +1,11 @@
import { PropsWithChildren as ShadowTextProps } from 'react';
import { Text } from '@react-email/text';
import { Text } from '@react-email/components';
import { emailTheme } from 'src/common-style';
type ShadowTextProps = {
children: JSX.Element | JSX.Element[] | string;
};
const shadowTextStyle = {
fontSize: emailTheme.font.size.sm,
fontWeight: emailTheme.font.weight.regular,
@@ -1,10 +1,9 @@
import { ReactNode } from 'react';
import { Heading } from '@react-email/components';
import { emailTheme } from 'src/common-style';
type SubTitleProps = {
value: ReactNode;
value: JSX.Element | JSX.Element[] | string;
};
const subTitleStyle = {
@@ -1,10 +1,9 @@
import { ReactNode } from 'react';
import { Heading } from '@react-email/components';
import { emailTheme } from 'src/common-style';
type TitleProps = {
value: ReactNode;
value: JSX.Element | JSX.Element[] | string;
};
const titleStyle = {
@@ -1,16 +1,16 @@
import { Footer } from 'src/components/Footer';
import { i18n } from '@lingui/core';
import { MainText } from 'src/components/MainText';
import { SubTitle } from 'src/components/SubTitle';
export const WhatIsTwenty = () => {
return (
<>
<SubTitle value="What is Twenty?" />
<SubTitle value={i18n._('What is Twenty?')} />
<MainText>
It's a CRM, a software to help businesses manage their customer data and
relationships efficiently.
{i18n._(
"It's a CRM, a software to help businesses manage their customer data and relationships efficiently.",
)}
</MainText>
<Footer />
</>
);
};
@@ -1,9 +1,10 @@
import { Trans } from '@lingui/react/macro';
import { i18n } from '@lingui/core';
import { Trans } from '@lingui/react';
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';
import { APP_LOCALES } from 'twenty-shared/translations';
type CleanSuspendedWorkspaceEmailProps = {
daysSinceInactive: number;
@@ -20,32 +21,40 @@ export const CleanSuspendedWorkspaceEmail = ({
}: CleanSuspendedWorkspaceEmailProps) => {
return (
<BaseEmail width={333} locale={locale}>
<Title value={<Trans>Deleted Workspace</Trans>} />
<Title value={i18n._('Deleted Workspace')} />
<MainText>
{userName?.length > 1 ? (
<Trans>Dear {userName},</Trans>
<Trans id="Dear {userName}," values={{ userName }} />
) : (
<Trans>Hello,</Trans>
<Trans id="Hello," />
)}
<br />
<br />
<Trans>
Your workspace <b>{workspaceDisplayName}</b> has been deleted as your
subscription expired {daysSinceInactive} days ago.
</Trans>
<Trans
id="Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
values={{ workspaceDisplayName, daysSinceInactive }}
components={{ 0: <b /> }}
/>
<br />
<br />
<Trans>All data in this workspace has been permanently deleted.</Trans>
<Trans id="All data in this workspace has been permanently deleted." />
<br />
<br />
<Trans>
If you wish to use Twenty again, you can create a new workspace.
</Trans>
<Trans id="If you wish to use Twenty again, you can create a new workspace." />
</MainText>
<CallToAction
href="https://app.twenty.com/"
value={<Trans>Create a new workspace</Trans>}
value={i18n._('Create a new workspace')}
/>
</BaseEmail>
);
};
CleanSuspendedWorkspaceEmail.PreviewProps = {
daysSinceInactive: 1,
userName: 'John Doe',
workspaceDisplayName: 'My Workspace',
locale: 'en',
} as CleanSuspendedWorkspaceEmailProps;
export default CleanSuspendedWorkspaceEmail;
@@ -1,10 +1,11 @@
import { Trans } from '@lingui/react/macro';
import { i18n } from '@lingui/core';
import { Trans } from '@lingui/react';
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';
import { APP_LOCALES } from 'twenty-shared/translations';
type PasswordResetLinkEmailProps = {
duration: string;
@@ -19,16 +20,24 @@ export const PasswordResetLinkEmail = ({
}: PasswordResetLinkEmailProps) => {
return (
<BaseEmail locale={locale}>
<Title value={<Trans>Reset your password 🗝</Trans>} />
<CallToAction href={link} value={<Trans>Reset</Trans>} />
<Title value={i18n._('Reset your password 🗝')} />
<CallToAction href={link} value={i18n._('Reset')} />
<MainText>
<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>
<Trans
id="This link is only valid for the next {duration}. If the link does not work, you can use the login verification link directly:"
values={{ duration }}
/>
<br />
<Link href={link} value={link} />
</MainText>
</BaseEmail>
);
};
PasswordResetLinkEmail.PreviewProps = {
duration: '24 hours',
link: 'https://app.twenty.com/reset-password/123',
locale: 'en',
} as PasswordResetLinkEmailProps;
export default PasswordResetLinkEmail;
@@ -1,12 +1,10 @@
import { i18n } from '@lingui/core';
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { Trans } from '@lingui/react';
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';
import { APP_LOCALES } from 'twenty-shared/translations';
type PasswordUpdateNotifyEmailProps = {
userName: string;
@@ -21,29 +19,38 @@ export const PasswordUpdateNotifyEmail = ({
link,
locale,
}: PasswordUpdateNotifyEmailProps) => {
const helloString = userName?.length > 1 ? t`Dear ${userName}` : t`Hello`;
const formattedDate = i18n.date(new Date());
return (
<BaseEmail locale={locale}>
<Title value={<Trans>Password updated</Trans>} />
<Title value={i18n._('Password updated')} />
<MainText>
{helloString},
{userName?.length > 1 ? (
<Trans id="Dear {userName}," values={{ userName }} />
) : (
<Trans id="Hello," />
)}
<br />
<br />
<Trans>
This is a confirmation that password for your account ({email}) was
successfully changed on {formattedDate}.
</Trans>
<Trans
id="This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
values={{ email, formattedDate }}
/>
<br />
<br />
<Trans>
If you did not initiate this change, please contact your workspace
owner immediately.
</Trans>
<Trans id="If you did not initiate this change, please contact your workspace owner immediately." />
<br />
</MainText>
<CallToAction value={<Trans>Connect to Twenty</Trans>} href={link} />
<CallToAction value={i18n._('Connect to Twenty')} href={link} />
</BaseEmail>
);
};
PasswordUpdateNotifyEmail.PreviewProps = {
userName: 'John Doe',
email: 'john.doe@example.com',
link: 'https://app.twenty.com',
locale: 'en',
} as PasswordUpdateNotifyEmailProps;
export default PasswordUpdateNotifyEmail;
@@ -1,11 +1,10 @@
import { Trans } from '@lingui/react/macro';
import { i18n } from '@lingui/core';
import { Trans } from '@lingui/react';
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';
import { APP_LOCALES } from 'twenty-shared/translations';
type SendEmailVerificationLinkEmailProps = {
link: string;
@@ -18,18 +17,20 @@ export const SendEmailVerificationLinkEmail = ({
}: SendEmailVerificationLinkEmailProps) => {
return (
<BaseEmail width={333} locale={locale}>
<Title value={<Trans>Confirm your email address</Trans>} />
<CallToAction href={link} value={<Trans>Verify Email</Trans>} />
<Title value={i18n._('Confirm your email address')} />
<CallToAction href={link} value={i18n._('Verify Email')} />
<br />
<br />
<MainText>
<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>
<Trans id="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." />
</MainText>
<Footer />
</BaseEmail>
);
};
SendEmailVerificationLinkEmail.PreviewProps = {
link: 'https://app.twenty.com/verify-email/123',
locale: 'en',
};
export default SendEmailVerificationLinkEmail;
@@ -1,4 +1,5 @@
import { Trans } from '@lingui/react/macro';
import { i18n } from '@lingui/core';
import { Trans } from '@lingui/react';
import { Img } from '@react-email/components';
import { emailTheme } from 'src/common-style';
@@ -11,7 +12,8 @@ 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';
import { APP_LOCALES } from 'twenty-shared/translations';
import { getImageAbsoluteURI } from 'twenty-shared/utils';
type SendInviteLinkEmailProps = {
link: string;
@@ -36,26 +38,58 @@ export const SendInviteLinkEmail = ({
? getImageAbsoluteURI({ imageUrl: workspace.logo, baseUrl: serverUrl })
: null;
const senderName = capitalize(sender.firstName);
const senderEmail = sender.email;
const workspaceName = workspace.name;
return (
<BaseEmail width={333} locale={locale}>
<Title value={<Trans>Join your team on Twenty</Trans>} />
<Title value={i18n._('Join your team on Twenty')} />
<MainText>
{capitalize(sender.firstName)} (
<Link
href={`mailto:${sender.email}`}
value={sender.email}
color={emailTheme.font.colors.blue}
<Trans
id="{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
values={{ senderName, senderEmail, workspaceName }}
components={{
0: (
<Link
href={`mailto:${senderEmail}`}
value={senderEmail}
color={emailTheme.font.colors.blue}
/>
),
1: <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={<Trans>Accept invite</Trans>} />
{workspaceLogo ? (
<Img
src={workspaceLogo}
width={40}
height={40}
alt="Workspace logo"
/>
) : (
<></>
)}
{workspace.name ? <HighlightedText value={workspace.name} /> : <></>}
<CallToAction href={link} value={i18n._('Accept invite')} />
</HighlightedContainer>
<WhatIsTwenty />
</BaseEmail>
);
};
SendInviteLinkEmail.PreviewProps = {
link: 'https://app.twenty.com/invite/123',
workspace: {
name: 'Acme Inc.',
logo: 'https://fakeimg.pl/200x200/?text=ACME&font=lobster',
},
sender: { email: 'john.doe@example.com', firstName: 'John', lastName: 'Doe' },
serverUrl: 'https://app.twenty.com',
locale: 'en',
} as SendInviteLinkEmailProps;
export default SendInviteLinkEmail;
@@ -0,0 +1,24 @@
import { i18n } from '@lingui/core';
import { BaseEmail } from 'src/components/BaseEmail';
import { Title } from 'src/components/Title';
import { APP_LOCALES } from 'twenty-shared/translations';
type TestEmailProps = {
locale: keyof typeof APP_LOCALES;
};
// This is a test email which isn't used in production
// It's useful to do tests and play in a local environment
export const TestEmail = ({ locale }: TestEmailProps) => {
return (
<BaseEmail locale={locale}>
<Title value={i18n._('Test email')} />
</BaseEmail>
);
};
TestEmail.PreviewProps = {
locale: 'en',
} as TestEmailProps;
export default TestEmail;
@@ -1,5 +1,5 @@
import { t } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { i18n } from '@lingui/core';
import { Trans } from '@lingui/react';
import { Img } from '@react-email/components';
import { emailTheme } from 'src/common-style';
@@ -10,9 +10,9 @@ 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';
import { APP_LOCALES } from 'twenty-shared/translations';
import { getImageAbsoluteURI } from 'twenty-shared/utils';
type SendApprovedAccessDomainValidationProps = {
link: string;
@@ -39,30 +39,61 @@ export const SendApprovedAccessDomainValidation = ({
? getImageAbsoluteURI({ imageUrl: workspace.logo, baseUrl: serverUrl })
: null;
const senderName = capitalize(sender.firstName);
const senderEmail = sender.email;
return (
<BaseEmail width={333} locale={locale}>
<Title value={t`Validate domain`} />
<Title value={i18n._('Validate domain')} />
<MainText>
{capitalize(sender.firstName)} (
<Link
href={`mailto:${sender.email}`}
value={sender.email}
color={emailTheme.font.colors.blue}
<Trans
id="{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
values={{ senderName, senderEmail, domain }}
components={{
0: (
<Link
href={`mailto:${senderEmail}`}
value={senderEmail}
color={emailTheme.font.colors.blue}
/>
),
1: <b />,
}}
/>
) <Trans>Please validate this domain to allow users with</Trans>{' '}
<b>@{domain}</b>{' '}
<Trans>
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`} />
{workspaceLogo ? (
<Img
src={workspaceLogo}
width={40}
height={40}
alt="Workspace logo"
/>
) : (
<></>
)}
{workspace.name ? <HighlightedText value={workspace.name} /> : <></>}
<CallToAction href={link} value={i18n._('Validate domain')} />
</HighlightedContainer>
<WhatIsTwenty />
</BaseEmail>
);
};
SendApprovedAccessDomainValidation.PreviewProps = {
link: 'https://app.twenty.com/validate-domain',
domain: 'example.com',
workspace: {
name: 'Acme Inc.',
logo: 'https://fakeimg.pl/200x200/?text=ACME&font=lobster',
},
sender: {
email: 'john.doe@example.com',
firstName: 'John',
lastName: 'Doe',
},
serverUrl: 'https://app.twenty.com',
locale: 'en',
} as SendApprovedAccessDomainValidationProps;
export default SendApprovedAccessDomainValidation;
@@ -1,9 +1,10 @@
import { Trans } from '@lingui/react/macro';
import { i18n } from '@lingui/core';
import { Trans } from '@lingui/react';
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';
import { APP_LOCALES } from 'twenty-shared/translations';
type WarnSuspendedWorkspaceEmailProps = {
daysSinceInactive: number;
@@ -22,38 +23,51 @@ export const WarnSuspendedWorkspaceEmail = ({
}: WarnSuspendedWorkspaceEmailProps) => {
const daysLeft = inactiveDaysBeforeDelete - daysSinceInactive;
const dayOrDays = daysLeft > 1 ? 'days' : 'day';
const remainingDays = daysLeft > 0 ? `${daysLeft} ` : '';
const helloString = userName?.length > 1 ? `Hello ${userName}` : 'Hello';
const remainingDays = daysLeft > 0 ? daysLeft : 0;
return (
<BaseEmail width={333} locale={locale}>
<Title value={<Trans>Suspended Workspace </Trans>} />
<Title value={i18n._('Suspended Workspace')} />
<MainText>
{helloString},
{userName?.length > 1 ? (
<Trans id="Dear {userName}," values={{ userName }} />
) : (
<Trans id="Hello," />
)}
<br />
<br />
<Trans>
It appears that your workspace <b>{workspaceDisplayName}</b> has been
suspended for {daysSinceInactive} days.
</Trans>
<Trans
id="It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
values={{ workspaceDisplayName, daysSinceInactive }}
components={{ 0: <b /> }}
/>
<br />
<br />
<Trans>
The workspace will be deactivated in {remainingDays} {dayOrDays}, and
all its data will be deleted.
</Trans>
<Trans
id="The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
values={{ remainingDays, dayOrDays }}
/>
<br />
<br />
<Trans>
If you wish to continue using Twenty, please update your subscription
within the next {remainingDays} {dayOrDays}.
</Trans>
<Trans
id="If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
values={{ remainingDays, dayOrDays }}
/>
</MainText>
<CallToAction
href="https://app.twenty.com/settings/billing"
value={<Trans>Update your subscription</Trans>}
value={i18n._('Update your subscription')}
/>
</BaseEmail>
);
};
WarnSuspendedWorkspaceEmail.PreviewProps = {
daysSinceInactive: 10,
inactiveDaysBeforeDelete: 14,
userName: 'John Doe',
workspaceDisplayName: 'Acme Inc.',
locale: 'en',
};
export default WarnSuspendedWorkspaceEmail;
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Aanvaar uitnodiging"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Aanvaar uitnodiging"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Alle data in hierdie werkruimte is permanent verwyder."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Bevestig jou e-posadres"
#~ msgid "Confirm your email address"
#~ msgstr "Bevestig jou e-posadres"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Konnekteer met Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Konnekteer met Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Skep 'n nuwe werksruimte"
#~ msgid "Create a new workspace"
#~ msgstr "Skep 'n nuwe werksruimte"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Skep 'n nuwe werksruimte"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Liewe {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Liewe {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Liewe {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Liewe {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Verwyderde Werkruimte"
#~ msgid "Deleted Workspace"
#~ msgstr "Verwyderde Werkruimte"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "e-posadresse om by jou werkruimte aan te sluit sonder om 'n uitnodiging te vereis."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "e-posadresse om by jou werkruimte aan te sluit sonder om 'n uitnodiging te vereis."
#. js-lingui-id: tGme7M
#: 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 "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "het jou genooi om aan te sluit by 'n werkruimte genaamd "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hallo"
#~ msgid "Hello"
#~ msgstr "Hallo"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hallo,"
#~ msgid "Hello,"
#~ msgstr "Hallo,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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."
#~ 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."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Sluit aan by jou span op Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Sluit aan by jou span op Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Wagwoord opgedateer"
#~ msgid "Password updated"
#~ msgstr "Wagwoord opgedateer"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Valideer asseblief hierdie domein om gebruikers met"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Valideer asseblief hierdie domein om gebruikers met"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Valideer asseblief hierdie domein om gebruikers met"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Stel terug"
#~ msgid "Reset"
#~ msgstr "Stel terug"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Stel jou wagwoord terug 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Stel jou wagwoord terug 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Geskorsde Werkruimte "
#~ msgid "Suspended Workspace "
#~ msgstr "Geskorsde Werkruimte "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Dit is 'n bevestiging dat die wagwoord vir jou rekening ({email}) sukses
#. js-lingui-id: R4gMjN
#: 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:"
#~ 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:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Hierdie skakel is slegs geldig vir die volgende {duration}. Indien die s
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Opdateer jou intekening"
#~ msgid "Update your subscription"
#~ msgstr "Opdateer jou intekening"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Valideer domein"
#~ msgid "Validate domain"
#~ msgstr "Valideer domein"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verifieer E-pos"
#~ msgid "Verify Email"
#~ msgstr "Verifieer E-pos"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "قبول الدعوة"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "قبول الدعوة"
#. js-lingui-id: Yxj+Uc
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "تم حذف جميع البيانات في هذه الواجهة بشكل دائم."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "تم حذف جميع البيانات في هذه الواجهة بشكل دائم."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "تأكد من عنوان بريدك الإلكتروني"
#~ msgid "Confirm your email address"
#~ msgstr "تأكد من عنوان بريدك الإلكتروني"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "الاتصال بـ Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "الاتصال بـ Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "إنشاء مساحة عمل جديدة"
#~ msgid "Create a new workspace"
#~ msgstr "إنشاء مساحة عمل جديدة"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "إنشاء مساحة عمل جديدة"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "عزيزي {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "عزيزي {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "عزيزي {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "عزيزي {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "مساحة العمل المحذوفة"
#~ msgid "Deleted Workspace"
#~ msgstr "مساحة العمل المحذوفة"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "عناوين البريد الإلكتروني للانضمام إلى مساحة العمل الخاصة بك دون الحاجة إلى دعوة."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "عناوين البريد الإلكتروني للانضمام إلى مساحة العمل الخاصة بك دون الحاجة إلى دعوة."
#. js-lingui-id: tGme7M
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "لقد دُعيت للإنضمام إلى مساحة عمل تسمى "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "لقد دُعيت للإنضمام إلى مساحة عمل تسمى "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "مرحبًا"
#~ msgid "Hello"
#~ msgstr "مرحبًا"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "مرحبًا,"
#~ msgid "Hello,"
#~ msgstr "مرحبًا,"
#. js-lingui-id: eE1nG1
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "إذا لم تكن قد بدأت هذا التغيير، يرجى الاتصال بمالك مساحة العمل الخاصة بك على الفور."
#~ msgid "If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "إذا لم تكن قد بدأت هذا التغيير، يرجى الاتصال بمالك مساحة العمل الخاصة بك على الفور."
#. js-lingui-id: Gz91L8
#: 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} القادمة."
#~ msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
#~ msgstr "إذا كنت ترغب في الاستمرار في استخدام Twenty، يُرجى تحديث اشتراكك خلال الأيام {remainingDays} {dayOrDays} القادمة."
#. js-lingui-id: 0weyko
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "إذا كنت ترغب في استخدام Twenty مرة أخرى، يمكنك إنشاء مساحة عمل جديدة."
#~ msgid "If you wish to use Twenty again, you can create a new workspace."
#~ msgstr "إذا كنت ترغب في استخدام Twenty مرة أخرى، يمكنك إنشاء مساحة عمل جديدة."
#. js-lingui-id: 7JuhZQ
#: 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} أيام."
#~ msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
#~ msgstr "يبدو أن مساحة عملك <0>{workspaceDisplayName}</0> قد تم تعليقها لمدة {daysSinceInactive} أيام."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "انضم إلى فريقك في Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "انضم إلى فريقك في Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "تم تحديث كلمة المرور"
#~ msgid "Password updated"
#~ msgstr "تم تحديث كلمة المرور"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "يرجى التحقق من صحة هذا النطاق للسماح للمستخدمين ب"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "يرجى التحقق من صحة هذا النطاق للسماح للمستخدمين ب"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "يرجى التحقق من صحة هذا النطاق للسماح لل
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "إعادة تعيين"
#~ msgid "Reset"
#~ msgstr "إعادة تعيين"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "إعادة تعيين كلمة مرورك 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "إعادة تعيين كلمة مرورك 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "فاعِل مساحة العمل المعلّقة "
#~ msgid "Suspended Workspace "
#~ msgstr "فاعِل مساحة العمل المعلّقة "
#. js-lingui-id: 7yDt8q
#: 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! قبل أن نبدأ، نحتاج فقط إلى التأكد من أن هذا أنت. انقر أعلاه للتحقق من عنوان بريدك الإلكتروني."
#~ 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! قبل أن نبدأ، نحتاج فقط إلى التأكد من أن هذا أنت. انقر أعلاه للتحقق من عنوان بريدك الإلكتروني."
#. js-lingui-id: igorB1
#: 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}، وسيتم حذف كل بياناتها."
#~ msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
#~ msgstr "سيتم تعطيل مساحة العمل في غضون {remainingDays} {dayOrDays}، وسيتم حذف كل بياناتها."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
#~ msgstr "هذا تأكيد أن كلمة مرور حسابك ({email}) قد تم تغييرها بنجاح في {formattedDate}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "هذا تأكيد أن كلمة مرور حسابك ({email}) قد تم
#. js-lingui-id: R4gMjN
#: 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} القادمة. إذا لم يعمل الرابط، يمكنك استخدام رابط التحقق من تسجيل الدخول مباشرة:"
#~ 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} القادمة. إذا لم يعمل الرابط، يمكنك استخدام رابط التحقق من تسجيل الدخول مباشرة:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "هذا الرابط صالح فقط للمدة {duration} القادمة
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "تحديث الاشتراك"
#~ msgid "Update your subscription"
#~ msgstr "تحديث الاشتراك"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "تحقق من النطاق"
#~ msgid "Validate domain"
#~ msgstr "تحقق من النطاق"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "تحقق من البريد الإلكتروني"
#~ msgid "Verify Email"
#~ msgstr "تحقق من البريد الإلكتروني"
#. js-lingui-id: 9MqLGX
#: 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} أيام."
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
#~ msgstr "تم حذف مساحة العمل <0>{workspaceDisplayName}</0> كون اشتراكك قد انتهى منذ {daysSinceInactive} أيام."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accepta la invitació"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Accepta la invitació"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Totes les dades en aquest espai de treball s'han esborrat permanentment."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirma la teva adreça de correu electrònic"
#~ msgid "Confirm your email address"
#~ msgstr "Confirma la teva adreça de correu electrònic"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Connecta't a Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Connecta't a Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Crea un nou espai de treball"
#~ msgid "Create a new workspace"
#~ msgstr "Crea un nou espai de treball"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Crea un nou espai de treball"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Benvolgut {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Benvolgut {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Benvolgut {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Benvolgut {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Espai de treball esborrat"
#~ msgid "Deleted Workspace"
#~ msgstr "Espai de treball esborrat"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "adreces de correu electrònic per unir-se al teu espai de treball sense requerir una invitació."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "adreces de correu electrònic per unir-se al teu espai de treball sense requerir una invitació."
#. js-lingui-id: tGme7M
#: 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 "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "t'ha convidat a unir-te a un espai de treball anomenat "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hola"
#~ msgid "Hello"
#~ msgstr "Hola"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hola,"
#~ msgid "Hello,"
#~ msgstr "Hola,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Uneix-te al teu equip a Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Uneix-te al teu equip a Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Contrasenya actualitzada"
#~ msgid "Password updated"
#~ msgstr "Contrasenya actualitzada"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Si us plau, valida aquest domini per permetre als usuaris amb"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Si us plau, valida aquest domini per permetre als usuaris amb"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Si us plau, valida aquest domini per permetre als usuaris amb"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Restableix"
#~ msgid "Reset"
#~ msgstr "Restableix"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Restableix la teva contrasenya 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Restableix la teva contrasenya 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Espai de treball suspès "
#~ msgid "Suspended Workspace "
#~ msgstr "Espai de treball suspès "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Això és una confirmació que la contrasenya del teu compte ({email}) s
#. js-lingui-id: R4gMjN
#: 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ó:"
#~ 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ó:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Aquest enllaç només és vàlid durant els propers {duration}. Si l'enl
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Actualitza la teva subscripció"
#~ msgid "Update your subscription"
#~ msgstr "Actualitza la teva subscripció"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Valida el domini"
#~ msgid "Validate domain"
#~ msgstr "Valida el domini"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verifica el correu electrònic"
#~ msgid "Verify Email"
#~ msgstr "Verifica el correu electrònic"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Přijměte pozvánku"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Přijměte pozvánku"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Všechna data v tomto pracovním prostoru byla trvale odstraněna."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Potvrďte svou e-mailovou adresu"
#~ msgid "Confirm your email address"
#~ msgstr "Potvrďte svou e-mailovou adresu"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Připojte se k Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Připojte se k Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Vytvořit nový pracovní prostor"
#~ msgid "Create a new workspace"
#~ msgstr "Vytvořit nový pracovní prostor"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Vytvořit nový pracovní prostor"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Vážený {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Vážený {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Vážený {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Vážený {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Smazaný pracovní prostor"
#~ msgid "Deleted Workspace"
#~ msgstr "Smazaný pracovní prostor"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "e-mailové adresy ke vstupu do vašeho pracovního prostoru bez nutnosti pozvánky."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "e-mailové adresy ke vstupu do vašeho pracovního prostoru bez nutnosti pozvánky."
#. js-lingui-id: tGme7M
#: 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 "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "vás pozval do pracovního prostoru s názvem "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Dobrý den"
#~ msgid "Hello"
#~ msgstr "Dobrý den"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Dobrý den,"
#~ msgid "Hello,"
#~ msgstr "Dobrý den,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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í."
#~ 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í."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Připojte se k svému týmu na Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Připojte se k svému týmu na Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Heslo bylo aktualizováno"
#~ msgid "Password updated"
#~ msgstr "Heslo bylo aktualizováno"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Prosím, ověřte tuto doménu pro umožnění přístupu uživatelům s"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Prosím, ověřte tuto doménu pro umožnění přístupu uživatelům s"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Prosím, ověřte tuto doménu pro umožnění přístupu uživatelům s
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Obnovit"
#~ msgid "Reset"
#~ msgstr "Obnovit"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Obnovte své heslo 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Obnovte své heslo 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Pozastavený pracovní prostor "
#~ msgid "Suspended Workspace "
#~ msgstr "Pozastavený pracovní prostor "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Toto je potvrzení, že heslo k vašemu účtu ({email}) bylo úspěšn
#. js-lingui-id: R4gMjN
#: 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í:"
#~ 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í:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Tento odkaz je platný pouze následující {duration}. Pokud odkaz nefu
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Aktualizujte své předplatné"
#~ msgid "Update your subscription"
#~ msgstr "Aktualizujte své předplatné"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Ověřit doménu"
#~ msgid "Validate domain"
#~ msgstr "Ověřit doménu"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Ověřit e-mail"
#~ msgid "Verify Email"
#~ msgstr "Ověřit e-mail"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accepter invitation"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Accepter invitation"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Alle data i dette arbejdsområde er blevet permanent slettet."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Bekræft din e-mailadresse"
#~ msgid "Confirm your email address"
#~ msgstr "Bekræft din e-mailadresse"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Forbind til Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Forbind til Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Opret et nyt arbejdsområde"
#~ msgid "Create a new workspace"
#~ msgstr "Opret et nyt arbejdsområde"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Opret et nyt arbejdsområde"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Kære {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Kære {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Kære {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Kære {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Slettet arbejdsområde"
#~ msgid "Deleted Workspace"
#~ msgstr "Slettet arbejdsområde"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "emailadresser for at tilslutte din arbejdsområde uden at kræve en invitation."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "emailadresser for at tilslutte din arbejdsområde uden at kræve en invitation."
#. js-lingui-id: tGme7M
#: 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 "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "har inviteret dig til at deltage i et arbejdsområde kaldet "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hej"
#~ msgid "Hello"
#~ msgstr "Hej"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hej,"
#~ msgid "Hello,"
#~ msgstr "Hej,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Deltag i dit team på Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Deltag i dit team på Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Adgangskode opdateret"
#~ msgid "Password updated"
#~ msgstr "Adgangskode opdateret"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Bekræft venligst dette domæne for at tillade brugere med"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Bekræft venligst dette domæne for at tillade brugere med"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Bekræft venligst dette domæne for at tillade brugere med"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Nulstil"
#~ msgid "Reset"
#~ msgstr "Nulstil"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Nulstil din adgangskode 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Nulstil din adgangskode 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Suspenderet arbejdsområde "
#~ msgid "Suspended Workspace "
#~ msgstr "Suspenderet arbejdsområde "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Dette er en bekræftelse på, at adgangskoden til din konto ({email}) er
#. js-lingui-id: R4gMjN
#: 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:"
#~ 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:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Dette link er kun gyldigt i de næste {duration}. Hvis linket ikke funge
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Opdater dit abonnement"
#~ msgid "Update your subscription"
#~ msgstr "Opdater dit abonnement"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Bekræft domæne"
#~ msgid "Validate domain"
#~ msgstr "Bekræft domæne"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Bekræft e-mail"
#~ msgid "Verify Email"
#~ msgstr "Bekræft e-mail"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Einladung akzeptieren"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Einladung akzeptieren"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Alle Daten in diesem Workspace wurden dauerhaft gelöscht."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Bestätigen Sie Ihre E-Mail-Adresse"
#~ msgid "Confirm your email address"
#~ msgstr "Bestätigen Sie Ihre E-Mail-Adresse"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Mit Twenty verbinden"
#~ msgid "Connect to Twenty"
#~ msgstr "Mit Twenty verbinden"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Erstellen Sie einen neuen Arbeitsbereich"
#~ msgid "Create a new workspace"
#~ msgstr "Erstellen Sie einen neuen Arbeitsbereich"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Erstellen Sie einen neuen Arbeitsbereich"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Sehr geehrte/r {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Sehr geehrte/r {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Sehr geehrte/r {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Sehr geehrte/r {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Gelöschter Arbeitsbereich"
#~ msgid "Deleted Workspace"
#~ msgstr "Gelöschter Arbeitsbereich"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "E-Mail-Adressen, um Ihrem Arbeitsbereich ohne Einladung beizutreten."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "E-Mail-Adressen, um Ihrem Arbeitsbereich ohne Einladung beizutreten."
#. js-lingui-id: tGme7M
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "hat Sie eingeladen, einem Workspace namens "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "hat Sie eingeladen, einem Workspace namens "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hallo"
#~ msgid "Hello"
#~ msgstr "Hallo"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hallo,"
#~ msgid "Hello,"
#~ msgstr "Hallo,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Treten Sie Ihrem Team auf Twenty bei"
#~ msgid "Join your team on Twenty"
#~ msgstr "Treten Sie Ihrem Team auf Twenty bei"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Passwort wurde aktualisiert"
#~ msgid "Password updated"
#~ msgstr "Passwort wurde aktualisiert"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Bitte validieren Sie diese Domain, um Benutzern mit"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Bitte validieren Sie diese Domain, um Benutzern mit"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Bitte validieren Sie diese Domain, um Benutzern mit"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Zurücksetzen"
#~ msgid "Reset"
#~ msgstr "Zurücksetzen"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Setzen Sie Ihr Passwort zurück 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Setzen Sie Ihr Passwort zurück 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Ausgesetzter Arbeitsbereich "
#~ msgid "Suspended Workspace "
#~ msgstr "Ausgesetzter Arbeitsbereich "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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."
#~ 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."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Dies ist eine Bestätigung, dass das Passwort für Ihr Konto ({email}) a
#. js-lingui-id: R4gMjN
#: 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:"
#~ 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:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Dieser Link ist nur für die nächsten {duration} gültig. Wenn der Link
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Aktualisieren Sie Ihr Abonnement"
#~ msgid "Update your subscription"
#~ msgstr "Aktualisieren Sie Ihr Abonnement"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Domain validieren"
#~ msgid "Validate domain"
#~ msgstr "Domain validieren"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "E-Mail verifizieren"
#~ msgid "Verify Email"
#~ msgstr "E-Mail verifizieren"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Αποδοχή πρόσκλησης"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Αποδοχή πρόσκλησης"
#. js-lingui-id: Yxj+Uc
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr "Όλα τα δεδομένα σε αυτόν τον χώρο εργασίας έχουν διαγραφεί μόνιμα."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Όλα τα δεδομένα σε αυτόν τον χώρο εργασίας έχουν διαγραφεί μόνιμα."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Επιβεβαιώστε τη διεύθυνση email σας"
#~ msgid "Confirm your email address"
#~ msgstr "Επιβεβαιώστε τη διεύθυνση email σας"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Συνδεθείτε στο Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Συνδεθείτε στο Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Δημιουργία νέου χώρου εργασίας"
#~ msgid "Create a new workspace"
#~ msgstr "Δημιουργία νέου χώρου εργασίας"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Δημιουργία νέου χώρου εργασίας"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Αγαπητέ/ή {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Αγαπητέ/ή {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Αγαπητέ/ή {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Αγαπητέ/ή {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Διαγραμμένος Χώρος Εργασίας"
#~ msgid "Deleted Workspace"
#~ msgstr "Διαγραμμένος Χώρος Εργασίας"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "διευθύνσεις ηλεκτρονικού ταχυδρομείου για να συμμετάσχουν στο χώρο εργασίας σας χωρίς να απαιτείται πρόσκληση."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "διευθύνσεις ηλεκτρονικού ταχυδρομείου για να συμμετάσχουν στο χώρο εργασίας σας χωρίς να απαιτείται πρόσκληση."
#. js-lingui-id: tGme7M
#: src/emails/send-invite-link.email.tsx
msgid "has invited you to join a workspace called "
msgstr "σας έχει προσκαλέσει να συμμετάσχετε σε έναν χώρο εργασίας με την ονομασία "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "σας έχει προσκαλέσει να συμμετάσχετε σε έναν χώρο εργασίας με την ονομασία "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Γεια σας"
#~ msgid "Hello"
#~ msgstr "Γεια σας"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Γεια σας,"
#~ msgid "Hello,"
#~ msgstr "Γεια σας,"
#. js-lingui-id: eE1nG1
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr "Εάν δεν έχετε προβεί εσείς σε αυτήν την αλλαγή, παρακαλώ επικοινωνήστε άμεσα με τον ιδιοκτήτη του χώρου εργασίας σας."
#~ msgid "If you did not initiate this change, please contact your workspace owner immediately."
#~ msgstr "Εάν δεν έχετε προβεί εσείς σε αυτήν την αλλαγή, παρακαλώ επικοινωνήστε άμεσα με τον ιδιοκτήτη του χώρου εργασίας σας."
#. js-lingui-id: Gz91L8
#: 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}."
#~ msgid "If you wish to continue using Twenty, please update your subscription within the next {remainingDays} {dayOrDays}."
#~ msgstr "Εάν επιθυμείτε να συνεχίστε να χρησιμοποιείτε το Twenty, παρακαλούμε ενημερώστε την συνδρομή σας εντός των επόμενων {remainingDays} {dayOrDays}."
#. js-lingui-id: 0weyko
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr "Εάν επιθυμείτε να χρησιμοποιήσετε ξανά το Twenty, μπορείτε να δημιουργήσετε ένα νέο χώρο εργασίας."
#~ msgid "If you wish to use Twenty again, you can create a new workspace."
#~ msgstr "Εάν επιθυμείτε να χρησιμοποιήσετε ξανά το Twenty, μπορείτε να δημιουργήσετε ένα νέο χώρο εργασίας."
#. js-lingui-id: 7JuhZQ
#: 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} ημέρες."
#~ msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
#~ msgstr "Φαίνεται ότι ο χώρος εργασίας σας <0>{workspaceDisplayName}</0> έχει ανασταλεί για {daysSinceInactive} ημέρες."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Συμμετέχετε στην ομάδα σας στο Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Συμμετέχετε στην ομάδα σας στο Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Ο κωδικός έχει ενημερωθεί"
#~ msgid "Password updated"
#~ msgstr "Ο κωδικός έχει ενημερωθεί"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Παρακαλώ επικυρώστε αυτόν τον τομέα για να επιτρέψετε στους χρήστες με"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Παρακαλώ επικυρώστε αυτόν τον τομέα για να επιτρέψετε στους χρήστες με"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Παρακαλώ επικυρώστε αυτόν τον τομέα γι
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Επαναφορά"
#~ msgid "Reset"
#~ msgstr "Επαναφορά"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Επαναφέρετε τον κωδικό σας 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Επαναφέρετε τον κωδικό σας 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Ανασταλμένος Χώρος Εργασίας "
#~ msgid "Suspended Workspace "
#~ msgstr "Ανασταλμένος Χώρος Εργασίας "
#. js-lingui-id: 7yDt8q
#: 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 σας."
#~ 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 σας."
#. js-lingui-id: igorB1
#: 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}, και όλα τα δεδομένα του θα διαγραφούν."
#~ msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
#~ msgstr "Ο χώρος εργασίας θα απενεργοποιηθεί σε {remainingDays} {dayOrDays}, και όλα τα δεδομένα του θα διαγραφούν."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
#~ msgstr "Αυτό είναι ένα επιβεβαίωση ότι ο κωδικός για τον λογαριασμό σας ({email}) άλλαξε με επιτυχία στις {formattedDate}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Αυτό είναι ένα επιβεβαίωση ότι ο κωδικό
#. js-lingui-id: R4gMjN
#: 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}. Εάν ο σύνδεσμος δεν λειτουργεί, μπορείτε να χρησιμοποιήσετε τον σύνδεσμο επαλήθευσης σύνδεσης απευθείας:"
#~ 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}. Εάν ο σύνδεσμος δεν λειτουργεί, μπορείτε να χρησιμοποιήσετε τον σύνδεσμο επαλήθευσης σύνδεσης απευθείας:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Αυτός ο σύνδεσμος ισχύει μόνο για τις ε
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Ενημερώστε τη συνδρομή σας"
#~ msgid "Update your subscription"
#~ msgstr "Ενημερώστε τη συνδρομή σας"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Επικύρωση τομέα"
#~ msgid "Validate domain"
#~ msgstr "Επικύρωση τομέα"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Επαληθεύστε το Email"
#~ msgid "Verify Email"
#~ msgstr "Επαληθεύστε το Email"
#. js-lingui-id: 9MqLGX
#: 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} ημέρες πριν."
#~ msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
#~ msgstr "Ο χώρος εργασίας σας <0>{workspaceDisplayName}</0> έχει διαγραφεί καθώς η συνδρομή σας έληξε {daysSinceInactive} ημέρες πριν."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+264 -59
View File
@@ -13,31 +13,236 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr "Suspended Workspace"
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Dear {userName},"
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hello,"
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: 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}."
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Update your subscription"
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Validate domain"
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr "Test email"
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Join your team on Twenty"
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accept invite"
#. js-lingui-id: Yxj+Uc
#: 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."
#. js-lingui-id: RPHFhC
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirm your email address"
#. js-lingui-id: nvkBPN
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verify Email"
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Password updated"
#. js-lingui-explicit-id
#: 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}."
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Connect to Twenty"
#. js-lingui-id: jPQSEz
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Reset your password 🗝"
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Reset"
#. js-lingui-explicit-id
#: 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:"
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Deleted Workspace"
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: 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."
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Create a new workspace"
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr "What is Twenty?"
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr "Website"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr "Visit Twenty's website"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr "Github"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr "Visit Twenty's GitHub repository"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr "User guide"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr "Read Twenty's user guide"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr "Developers"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr "Visit Twenty's developer documentation"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr "Twenty.com, Public Benefit Corporation"
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr "San Francisco / Paris"
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Accept invite"
#. js-lingui-id: Yxj+Uc
#: 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."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
#~ msgid "Confirm your email address"
#~ msgstr "Confirm your email address"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
#~ msgid "Connect to Twenty"
#~ msgstr "Connect to Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
#~ msgid "Create a new workspace"
#~ msgstr "Create a new workspace"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
#~ msgid "Dear"
@@ -45,73 +250,73 @@ msgstr "Create a new workspace"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Dear {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Dear {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Dear {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Dear {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Deleted Workspace"
#~ msgid "Deleted Workspace"
#~ msgstr "Deleted Workspace"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "email addresses to join your workspace without requiring an invitation."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "email addresses to join your workspace without requiring an invitation."
#. js-lingui-id: tGme7M
#: 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 "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "has invited you to join a workspace called "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hello"
#~ msgid "Hello"
#~ msgstr "Hello"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hello,"
#~ msgid "Hello,"
#~ msgstr "Hello,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Join your team on Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Join your team on Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Password updated"
#~ msgid "Password updated"
#~ msgstr "Password updated"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Please validate this domain to allow users with"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Please validate this domain to allow users with"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -120,33 +325,33 @@ msgstr "Please validate this domain to allow users with"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Reset"
#~ msgid "Reset"
#~ msgstr "Reset"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Reset your password 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Reset your password 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Suspended Workspace "
#~ msgid "Suspended Workspace "
#~ msgstr "Suspended Workspace "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -155,8 +360,8 @@ msgstr "This is a confirmation that password for your account ({email}) was succ
#. js-lingui-id: R4gMjN
#: 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:"
#~ 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:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -165,24 +370,24 @@ msgstr "This link is only valid for the next {duration}. If the link does not wo
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Update your subscription"
#~ msgid "Update your subscription"
#~ msgstr "Update your subscription"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Validate domain"
#~ msgid "Validate domain"
#~ msgstr "Validate domain"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verify Email"
#~ msgid "Verify Email"
#~ msgstr "Verify Email"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Aceptar invitación"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Aceptar invitación"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Todos los datos de este espacio de trabajo han sido eliminados permanentemente."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirma tu dirección de correo electrónico"
#~ msgid "Confirm your email address"
#~ msgstr "Confirma tu dirección de correo electrónico"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Conéctate a Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Conéctate a Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Crea un nuevo espacio de trabajo"
#~ msgid "Create a new workspace"
#~ msgstr "Crea un nuevo espacio de trabajo"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Crea un nuevo espacio de trabajo"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Estimado/a {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Estimado/a {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Estimado/a {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Estimado/a {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Espacio de trabajo eliminado"
#~ msgid "Deleted Workspace"
#~ msgstr "Espacio de trabajo eliminado"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "direcciones de correo electrónico para unirse a su espacio de trabajo sin requerir una invitación."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "direcciones de correo electrónico para unirse a su espacio de trabajo sin requerir una invitación."
#. js-lingui-id: tGme7M
#: 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 "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "te ha invitado a unirte a un espacio de trabajo llamado "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hola"
#~ msgid "Hello"
#~ msgstr "Hola"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hola,"
#~ msgid "Hello,"
#~ msgstr "Hola,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Únete a tu equipo en Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Únete a tu equipo en Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Contraseña actualizada"
#~ msgid "Password updated"
#~ msgstr "Contraseña actualizada"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Por favor, valide este dominio para permitir a los usuarios con"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Por favor, valide este dominio para permitir a los usuarios con"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Por favor, valide este dominio para permitir a los usuarios con"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Restablecer"
#~ msgid "Reset"
#~ msgstr "Restablecer"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Restablece tu contraseña 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Restablece tu contraseña 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Espacio de trabajo suspendido "
#~ msgid "Suspended Workspace "
#~ msgstr "Espacio de trabajo suspendido "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Esta es una confirmación de que la contraseña de tu cuenta ({email}) s
#. js-lingui-id: R4gMjN
#: 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:"
#~ 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:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Este enlace solo es válido por los próximos {duration}. Si el enlace n
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Actualiza tu suscripción"
#~ msgid "Update your subscription"
#~ msgstr "Actualiza tu suscripción"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Validar dominio"
#~ msgid "Validate domain"
#~ msgstr "Validar dominio"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verificar correo electrónico"
#~ msgid "Verify Email"
#~ msgstr "Verificar correo electrónico"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Hyväksy kutsu"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Hyväksy kutsu"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ msgid "All data in this workspace has been permanently deleted."
#~ msgstr "Kaikki tiedot tässä työtilassa on pysyvästi poistettu."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Vahvista sähköpostiosoitteesi"
#~ msgid "Confirm your email address"
#~ msgstr "Vahvista sähköpostiosoitteesi"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Yhdistä Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Yhdistä Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Luo uusi työtila"
#~ msgid "Create a new workspace"
#~ msgstr "Luo uusi työtila"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Luo uusi työtila"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Hyvä {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Hyvä {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Hyvä {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Hyvä {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Poistettu työtila"
#~ msgid "Deleted Workspace"
#~ msgstr "Poistettu työtila"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "sähköpostiosoitteet liittymään työtilaasi ilman kutsua."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "sähköpostiosoitteet liittymään työtilaasi ilman kutsua."
#. js-lingui-id: tGme7M
#: 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ä "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "on kutsunut sinut liittymään työtilaan nimeltä "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Hei"
#~ msgid "Hello"
#~ msgstr "Hei"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Hei,"
#~ msgid "Hello,"
#~ msgstr "Hei,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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."
#~ 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."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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ää."
#~ 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ää."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Liity tiimiisi Twentyssä"
#~ msgid "Join your team on Twenty"
#~ msgstr "Liity tiimiisi Twentyssä"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Salasana päivitetty"
#~ msgid "Password updated"
#~ msgstr "Salasana päivitetty"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Vahvista tämä verkkotunnus, jotta tämä sallitaan käyttäjille, joilla on"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Vahvista tämä verkkotunnus, jotta tämä sallitaan käyttäjille, joilla on"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Vahvista tämä verkkotunnus, jotta tämä sallitaan käyttäjille, joil
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Nollaa"
#~ msgid "Reset"
#~ msgstr "Nollaa"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Nollaa salasanasi 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Nollaa salasanasi 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Keskeytetty työtila "
#~ msgid "Suspended Workspace "
#~ msgstr "Keskeytetty työtila "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Tämä on vahvistus siitä, että tilisi ({email}) salasana on vaihdettu
#. js-lingui-id: R4gMjN
#: 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ä:"
#~ 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ä:"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Tämä linkki on voimassa vain seuraavat {duration}. Jos linkki ei toimi
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Päivitä tilauksesi"
#~ msgid "Update your subscription"
#~ msgstr "Päivitä tilauksesi"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Vahvista verkkotunnus"
#~ msgid "Validate domain"
#~ msgstr "Vahvista verkkotunnus"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Vahvista sähköposti"
#~ msgid "Verify Email"
#~ msgstr "Vahvista sähköposti"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
+265 -60
View File
@@ -18,30 +18,235 @@ msgstr ""
"X-Crowdin-File: /packages/twenty-emails/src/locales/en.po\n"
"X-Crowdin-File-ID: 27\n"
#. js-lingui-id: 4WPI3S
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
#: src/emails/password-update-notify.email.tsx
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "It appears that your workspace <0>{workspaceDisplayName}</0> has been suspended for {daysSinceInactive} days."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "The workspace will be deactivated in {remainingDays} {dayOrDays}, and all its data will be deleted."
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/validate-approved-access-domain.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>): Please validate this domain to allow users with <1>@{domain}</1> email addresses to join your workspace without requiring an invitation."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/test.email.tsx
msgid "Test email"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "{senderName} (<0>{senderEmail}</0>) has invited you to join a workspace called <1>{workspaceName}</1>."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-invite-link.email.tsx
msgid "Accept invite"
msgstr "Accepter l'invitation"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "This is a confirmation that password for your account ({email}) was successfully changed on {formattedDate}."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "If you did not initiate this change, please contact your workspace owner immediately."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
#: 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 ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Your workspace <0>{workspaceDisplayName}</0> has been deleted as your subscription expired {daysSinceInactive} days ago."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "All data in this workspace has been permanently deleted."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "If you wish to use Twenty again, you can create a new workspace."
msgstr ""
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "What is Twenty?"
msgstr ""
#. js-lingui-explicit-id
#: src/components/WhatIsTwenty.tsx
msgid "It's a CRM, a software to help businesses manage their customer data and relationships efficiently."
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's website"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Github"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's GitHub repository"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "User guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Read Twenty's user guide"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Developers"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Visit Twenty's developer documentation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "Twenty.com, Public Benefit Corporation"
msgstr ""
#. js-lingui-explicit-id
#: src/components/Footer.tsx
msgid "San Francisco / Paris"
msgstr ""
#. js-lingui-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
#~ msgstr "Accepter l'invitation"
#. js-lingui-id: Yxj+Uc
#: 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."
#~ 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."
#. js-lingui-id: RPHFhC
#: src/emails/send-email-verification-link.email.tsx
msgid "Confirm your email address"
msgstr "Confirmez votre adresse e-mail"
#~ msgid "Confirm your email address"
#~ msgstr "Confirmez votre adresse e-mail"
#. js-lingui-id: nvkBPN
#: src/emails/password-update-notify.email.tsx
msgid "Connect to Twenty"
msgstr "Connectez-vous à Twenty"
#~ msgid "Connect to Twenty"
#~ msgstr "Connectez-vous à Twenty"
#. js-lingui-id: jPQSEz
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Create a new workspace"
msgstr "Créez un nouvel espace de travail"
#~ msgid "Create a new workspace"
#~ msgstr "Créez un nouvel espace de travail"
#. js-lingui-id: JRzgV7
#: src/emails/password-update-notify.email.tsx
@@ -50,73 +255,73 @@ msgstr "Créez un nouvel espace de travail"
#. js-lingui-id: Lm5BBI
#: src/emails/password-update-notify.email.tsx
msgid "Dear {userName}"
msgstr "Cher {userName}"
#~ msgid "Dear {userName}"
#~ msgstr "Cher {userName}"
#. js-lingui-id: lIdkf2
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Dear {userName},"
msgstr "Cher {userName},"
#~ msgid "Dear {userName},"
#~ msgstr "Cher {userName},"
#. js-lingui-id: NTwcnq
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Deleted Workspace"
msgstr "Espace de travail supprimé"
#~ msgid "Deleted Workspace"
#~ msgstr "Espace de travail supprimé"
#. js-lingui-id: S3uuQj
#: src/emails/validate-approved-access-domain.email.tsx
msgid "email addresses to join your workspace without requiring an invitation."
msgstr "adresses e-mail pour rejoindre votre espace de travail sans nécessiter d'invitation."
#~ msgid "email addresses to join your workspace without requiring an invitation."
#~ msgstr "adresses e-mail pour rejoindre votre espace de travail sans nécessiter d'invitation."
#. js-lingui-id: tGme7M
#: 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é "
#~ msgid "has invited you to join a workspace called "
#~ msgstr "vous a invité à rejoindre un espace de travail appelé "
#. js-lingui-id: uzTaYi
#: src/emails/password-update-notify.email.tsx
msgid "Hello"
msgstr "Bonjour"
#~ msgid "Hello"
#~ msgstr "Bonjour"
#. js-lingui-id: Xa0d85
#: src/emails/clean-suspended-workspace.email.tsx
msgid "Hello,"
msgstr "Bonjour,"
#~ msgid "Hello,"
#~ msgstr "Bonjour,"
#. js-lingui-id: eE1nG1
#: 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."
#~ 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."
#. js-lingui-id: Gz91L8
#: 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}."
#~ 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}."
#. js-lingui-id: 0weyko
#: 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."
#~ 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."
#. js-lingui-id: 7JuhZQ
#: 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."
#~ 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."
#. js-lingui-id: PviVyk
#: src/emails/send-invite-link.email.tsx
msgid "Join your team on Twenty"
msgstr "Rejoignez votre équipe sur Twenty"
#~ msgid "Join your team on Twenty"
#~ msgstr "Rejoignez votre équipe sur Twenty"
#. js-lingui-id: ogtYkT
#: src/emails/password-update-notify.email.tsx
msgid "Password updated"
msgstr "Mot de passe mis à jour"
#~ msgid "Password updated"
#~ msgstr "Mot de passe mis à jour"
#. js-lingui-id: Yucjaa
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Please validate this domain to allow users with"
msgstr "Veuillez valider ce domaine pour permettre aux utilisateurs avec"
#~ msgid "Please validate this domain to allow users with"
#~ msgstr "Veuillez valider ce domaine pour permettre aux utilisateurs avec"
#. js-lingui-id: u3Ns4p
#: src/emails/validate-approved-access-domain.email.tsx
@@ -125,33 +330,33 @@ msgstr "Veuillez valider ce domaine pour permettre aux utilisateurs avec"
#. js-lingui-id: OfhWJH
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Réinitialiser"
#~ msgid "Reset"
#~ msgstr "Réinitialiser"
#. js-lingui-id: RE5NiU
#: src/emails/password-reset-link.email.tsx
msgid "Reset your password 🗝"
msgstr "Réinitialisez votre mot de passe 🗝"
#~ msgid "Reset your password 🗝"
#~ msgstr "Réinitialisez votre mot de passe 🗝"
#. js-lingui-id: UBadaJ
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Suspended Workspace "
msgstr "Espace de travail suspendu "
#~ msgid "Suspended Workspace "
#~ msgstr "Espace de travail suspendu "
#. js-lingui-id: 7yDt8q
#: 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."
#~ 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."
#. js-lingui-id: igorB1
#: 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."
#~ 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."
#. js-lingui-id: 7OEHy1
#: 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}."
#~ 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}."
#. js-lingui-id: wSOsS+
#: src/emails/password-update-notify.email.tsx
@@ -160,8 +365,8 @@ msgstr "Ceci est une confirmation que le mot de passe de votre compte ({email})
#. js-lingui-id: R4gMjN
#: 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 :"
#~ 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 :"
#. js-lingui-id: 2oA637
#: src/emails/password-reset-link.email.tsx
@@ -170,24 +375,24 @@ msgstr "Ce lien n'est valable que pour les {duration} suivants. Si le lien ne fo
#. js-lingui-id: H0v4yC
#: src/emails/warn-suspended-workspace.email.tsx
msgid "Update your subscription"
msgstr "Mettez à jour votre abonnement"
#~ msgid "Update your subscription"
#~ msgstr "Mettez à jour votre abonnement"
#. js-lingui-id: QbiUqd
#: src/emails/validate-approved-access-domain.email.tsx
#: src/emails/validate-approved-access-domain.email.tsx
msgid "Validate domain"
msgstr "Valider le domaine"
#~ msgid "Validate domain"
#~ msgstr "Valider le domaine"
#. js-lingui-id: wCKkSr
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Vérifiez l'e-mail"
#~ msgid "Verify Email"
#~ msgstr "Vérifiez l'e-mail"
#. js-lingui-id: 9MqLGX
#: 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."
#~ 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."
#. js-lingui-id: KFmFrQ
#: src/emails/clean-suspended-workspace.email.tsx
File diff suppressed because one or more lines are too long

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