Compare commits

...
72 Commits
Author SHA1 Message Date
6bb8fa8559 Fix workspace relation sync (#11963)
While deploying the IS_NEW_RELATION_ENABLED (we don't compute relation
based on relationMetadata anymore) to existing workspace, I've tested to
run a sync-metadata post feature flag activation. This has raised two
issues:
- the workspaceMigration generator (which is over-complex and should be
refactored later) for fieldMetadata of type RELATION was not handling
settings update properly ;
- we need to delete existing fieldMetadata corresponding to the UUID
foreignKey as they are not needed anymore. This is handled as a 0.53
upgrade command as 0.53 will also come with the full removal of the old
relation system

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: prastoin <paul@twenty.com>
2025-05-10 11:54:59 +02:00
Paul Rastoinandprastoin a771216f6e Fix field currency form settings (#11948)
# Introduction
Close #11947 

From my understanding this `singleQuote` manipulation is legacy due to
front preparing sql queries in some way.

We might wanna standardize this behavior for each `CURRENCIES` usage,
such as within the `ObjectFilterDropdownCurrencySelect` that does not
send nor expect singled quoted encapsulated currencies values.

Created core-team-issues tech issue
https://github.com/twentyhq/core-team-issues/issues/941


## Field creation variables
```json
{
  "input": {
    "field": {
      "defaultValue": {
        "amountMicros": null,
        "currencyCode": "'BYN'"
      },
      "description": "",
      "icon": "IconMoneybag",
      "label": "Maquito",
      "name": "maquito",
      "isLabelSyncedWithName": true,
      "objectMetadataId": "6ef5da20-cf03-49b5-81fe-73aa2c9856a9",
      "type": "CURRENCY"
    }
  }
}
```

## Filtering variables
```json
{
  "filter": {
    "test": {
      "currencyCode": {
        "in": [
          "ALL",
          "AED",
          "AFN",
          "ANG"
        ]
      }
    }
  },
  "orderBy": [
    {
      "position": "AscNullsFirst"
    }
  ]
}
```
2025-05-09 11:10:24 +02:00
Etienneandetiennejouan fe371eb4c4 fix on field relation preview (#11926)
To reproduce : 
Modify object destination to an object with a 'simple' field as
labelIdentifier from data model settings, during creation of a relation
field from an object which has a composite field as labelIdentifier +
Relation type 'has many'

Fix : 
fieldValue and fieldDefinition does not update at the same time (when
object destination is modified) in RelationToManyFieldDisplay component.
Need to better define recordId to be sure fieldValue doesn't point on
previous record.

Tested : 
Relation field creation from Person with switch on relationType and
objectDestination (company/workspaceMember)
Relation field creation from Company with switch on relationType and
objectDestination (pet/person)
closes https://github.com/twentyhq/twenty/issues/11826
2025-05-07 17:10:41 +02:00
Guillimandguillim 1db92ce662 Critical email sync bug (#11885)
Removing the upsert to avoid the ON CONFLICT
2025-05-06 11:22:09 +02:00
prastoin 6bb0212a70 fix(server): upgrade command 2025-05-05 16:48:10 +02:00
Etienneandetiennejouan 4a543a0e0a fix relation creation bug (#11848)
Context : In `RelationToOneFieldDisplay`, the `objectNameSingular` used
for the chip calculation was incorrect - it was using the value from the
opposite side of the relationship. Then, labelIdentifier based on
composite fields were causing errors (People and WorkspaceMembers with
name)


<img width="300" alt="Screenshot 2025-05-03 at 08 03 46"
src="https://github.com/user-attachments/assets/8d034700-5244-4b1b-978e-f77ae78b6ceb"
/>


Tested everywhere FieldDisplay is used. 
Tested for each both relation type. 

closes https://github.com/twentyhq/twenty/issues/11826
2025-05-03 08:57:53 +02:00
Paul RastoinandGitHub 17334f8800 Fix undefined read attempt (#11816)
# Introduction
Followup of https://github.com/twentyhq/twenty/pull/11784
Again some propaganda for the
[noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig/#noUncheckedIndexedAccess)
that involved this @charlesBochet 👀
That's very risky to have this setup to false especially in the backend
regarding the twenty-server metadata nature

Also suggested that we do some integration testing ( e2e nestsJs tests )
on a related endpoint that could related we always retrieve the same
form result output

We could also do some unit testing of the method but like the idea to
ship it through the api itself
2025-04-30 17:58:27 +02:00
EtienneandGitHub c657caf74f force cache refresh for views after new object creation (#11806)
**Context** 
When creating a new object, it creates the "All ...." view associated.
After new object is created, in `PrefetchRunViewQueryEffect`,
findManyViews returns cached results WITHOUT the new view.

git bisect - regression introduced with
https://github.com/twentyhq/twenty/pull/10272

**Attempt** : Update to 'network-only' fetch policy in
`PrefetchRunViewQueryEffect` -> not working on useQuery apollo hook (🤯)
: query is handle by cache and not network

**Solution** 
Based on pattern used for view creation
(`useCreateViewFromCurrentView`), refreshing the view cache with a
`useLazyFindManyRecords` solves the issue. Then, `prefetchViewsState` is
updated in `PrefetchRunViewQueryEffect`


closes : https://github.com/twentyhq/core-team-issues/issues/845
2025-04-30 17:54:48 +02:00
EtienneandGitHub 6b9128d1ae fix rich text focus (#11821)
Before 


https://github.com/user-attachments/assets/419227fb-fc0e-4d6e-b89a-932b4b76666f

After


https://github.com/user-attachments/assets/e4db323c-2f98-46f6-84e7-8b9bb452e900


Tested in command Menu + table cell edit mode + "plain text" command
menu
2025-04-30 17:28:12 +02:00
4ce7834f94 i18n - translations (#11822)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-30 17:20:54 +02:00
martmullandGitHub 849a35955a 866 refactor cron trigger only one cron each minutes triggers all cron triggers (#11809)
<img width="1123" alt="image"
src="https://github.com/user-attachments/assets/75447922-81dd-4cfc-805d-f511f73cc778"
/>
2025-04-30 17:08:47 +02:00
GuillimandGitHub 357649db95 fix-discord-timeline (#11784)
In some cases, the person is undefined so we fallback on the
workspacemember, and sometimes he is missing the firstname/lastname.

Not sure how this can happen, but one user experiencd such a thing so
better to catch this


![image](https://github.com/user-attachments/assets/c91445fe-365e-4fc9-bf14-69f71d344aa7)

fix https://discord.com/channels/1130383047699738754/1366145144838951022

close https://github.com/twentyhq/core-team-issues/issues/918
2025-04-30 15:11:00 +02:00
Lucas BordeauandGitHub 0439b96eca Implement new parallel code path for object filter dropdown current record filter (#11807)
This PR implements a new clean parallel code path for handling the
filter manipulated in an object filter dropdown.

Remember that the object filter dropdown module is the generic, shared
module, that must be vertically implemented in those places : view bar
filter button, record table column header cell, view bar details filter
chip.

So here we implement, just for the text filter input (for example a
FULL_NAME field type), a new parallel code path logic, that runs on a
new state : objectFilterDropdownCurrentRecordFilterState

We still update the selectedFilter state, that is very close to the new
objectFilterDropdownCurrentRecordFilterState, but in order to be
cautious, and allow us to refactor incrementally, we implement a new
parallel code path and let the rest run on selectedFilterState for now.

The new way of working with the filter in the object filter dropdown,
includes smaller and more specific hooks :
- useApplyObjectFilterDropdownFilterValue instead of applyRecordFilter
which API generates a lot of tech debt
- useObjectFilterDropdownFilterValue to get the current value (might be
later removed if too thin)
- useUpsertObjectFilterDropdownCurrentFilter, to abstract a bit some
duplicated logic in useApplyObjectFilterDropdownFilterValue
- useCreateRecordFilterFromObjectFilterDropdownCurrentStates which
differs from the existing
useCreateEmptyRecordFilterFromFieldMetadataItem in that it uses the
current states instead of creating an empty filter with default values.
Those two logics are still very confusing and duplicated everywhere,
creating specific hooks makes it clear now.

This PR shouldn't cause any change in the behavior of the filtering
feature.

Fixes https://github.com/twentyhq/core-team-issues/issues/717
2025-04-30 14:55:37 +02:00
Félix MalfaitGitHubCopilotgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
cb513bc7a8 Set default locale according to browser locale (#11805)
We didn't get much complaints on Localization so I guess we can expand
it more and make it the default behavior to use the browser's locale
when you signup

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-30 13:11:40 +02:00
Félix MalfaitandGitHub e55ff5ac4a Change workflow dispatch permissions (#11812)
It seems that content: write is needed so that external contributors can
dispatch workflows
2025-04-30 13:06:57 +02:00
Félix MalfaitandGitHub 6343eaeb4f Change workflow preview env to pull_request_target (#11810)
It didn't work with external contributors
2025-04-30 12:31:56 +02:00
Abdul RahmanandGitHub 36ddf48ef0 Fix ⌘A selecting records instead of input text when creating a new view (#11783)
### Demo

https://github.com/user-attachments/assets/772e5b45-f55d-4d5f-9256-5a325b027d5d

Closes #11677
2025-04-30 09:40:25 +02:00
ea25498625 Fix dragging behavior below the last card when dragging below the new CTA button (#11781)
- Fixed an issue where dragging an item below the last card didn't work
when the card was dragged below the new CTA button (`destination.index
=== items.length` case).
- Moved the "new record" button outside of the draggable list

### Demo

https://github.com/user-attachments/assets/370f2c1f-4bb2-403b-b8ed-4afda064c98d

Closes #10197

---------

Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-30 09:34:56 +02:00
e6c1b70d9c i18n - translations (#11801)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-30 09:20:32 +02:00
e957b1acd6 Twenty config admin panel integration (#11755)
closes https://github.com/twentyhq/core-team-issues/issues/761
closes https://github.com/twentyhq/core-team-issues/issues/762

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-30 09:12:59 +02:00
842367f7bb i18n - translations (#11800)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-29 18:48:53 +02:00
69f92a697b i18n - translations (#11799)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-29 18:37:01 +02:00
2367ffd452 i18n - translations (#11798)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-29 18:21:20 +02:00
Thomas des FrancsGitHubFélix Malfaitgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
df95ce9ed8 Updated several emails template styles (#11797)
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-29 18:08:45 +02:00
Lucas BordeauandGitHub d52cb26599 Refactored ObjectFilterDropdown into ViewBarFilterDropdown (#11794)
This PR refactors the non-generic part around ObjectFilterDropdown which
has been left in statu quo for months.

It also removes unused components. 

Overall this PR is doing renaming and it re-organizes files into their
relevant modules.

This clarifies a lot what's at the intersection between
object-filter-dropdown and views modules.

This PR was originally about removing any remaining useEffect around
ObjectFilterDropdown but there wasn't any.

## Details

### Removed unused files

- GenericEntityFilterChip
- SingleEntityObjectFilterDropdownButton (was used for the Task/Note
standalone page which doesn't exist anymore)

### Re-organized non-generic components into ViewBarFilterDropdown

- Use VIEW_BAR_FILTER_DROPDOWN_ID instead of OBJECT_FILTER_DROPDOWN_ID
- Use FILTER_FIELD_LIST_ID for selectable list
- Refactored ObjectFilterDropdownButton into a simple
ViewBarFilterDropdown
- Renamed MultipleFiltersDropdownContent to ViewBarFilterDropdownContent
- Renamed MultipleFiltersButton to ViewBarFilterButton
- Integrated MultipleFiltersDropdownButton to ViewBarFilterDropdown
- Renamed AdvancedFilterButton to ViewBarDetailsAddFilterButton

### Tests 

Fixed storybook test for ViewBarFilterDrodpown
2025-04-29 18:05:12 +02:00
Thomas TrompetteandGitHub d8b2e1fb34 Start using next step ids (#11683)
- update workflow executor
- update next step ids on step creation/deletion
- use these in workflow run
- use these in variables
2025-04-29 16:29:25 +02:00
Baptiste DevessierandGitHub 19f46a0091 Fix e2e tests (#11792) 2025-04-29 15:51:59 +02:00
Thomas TrompetteandGitHub fef627b7c2 Fix output schema type not defined (#11788)
Fixes https://github.com/twentyhq/twenty/issues/11778
2025-04-29 11:29:11 +00:00
MarieandGitHub 2482271545 Fix filter with empty field (#11787)
Attempt to fix https://github.com/twentyhq/core-team-issues/issues/899

Could not reproduce but that's the only place in the code where I found
that we build filters as such, with values for "in" being equal to
select options and not recordIds, as it seems to be the case from the
sentries (ex:
<img width="174" alt="Capture d’écran 2025-04-29 à 11 13 56"
src="https://github.com/user-attachments/assets/a0b62de1-fe23-4394-9f86-a205ee32b56c"
/>
)
2025-04-29 09:56:14 +00:00
Lucas BordeauandGitHub fda793ced2 Fix formatDateString test (#11786)
This PR fixes a broken test that makes the CI crash.

This is due to the library `date-fns` which now doesn't use the word
"about" in its relative date formatting, if we're not precisely on minus
2 months, which can change for example today the 29th of April, where
the 29th of February doesn't exist.

So instead of using months and falling into hard to test cases, we use
days instead, which is an easy and predictable relative computation.
2025-04-29 11:45:58 +02:00
GuillimandGitHub 4833ee2914 fix-default-picture-url (#11779)
Fixing the default avatar picture in /settings/profile
2025-04-29 10:30:05 +02:00
MarieandGitHub 23d71915f6 Improve metadata version caching (#11775)
Investigating https://github.com/twentyhq/core-team-issues/issues/756, I
found that the error actually stemmed from "Object metadata collection
not found" error.

While this is planned to be fixed by metadata performance improvements
(as stated in [sentry-boss
doc](https://docs.google.com/document/d/1ytbC5W6ZFUSJ3PoJ4IfKi2IehKZYw65mqCnc24aP4RM/edit?tab=t.0)
in "known issues"), I tried some easy improvements to reduce the number
of errors.
2025-04-29 09:53:19 +02:00
Raphaël BosiandGitHub 7be56862e4 Fix object metadata not found for id (#11777)
Fixes https://github.com/twentyhq/twenty/issues/11668

Fixed an error due to falling back to the main context store object
metadata when the one in the context store wasn't defined.

When closing the command menu, the
`contextStoreCurrentObjectMetadataItemIdComponentState` is reset, but
the ActionMenuContextProvider was falling back to the main context store
object metadata id, so the action components were mounted. But they
still tried to access the
`contextStoreCurrentObjectMetadataItemIdComponentState` which is
`undefined`, which lead to an error being thrown.

Fix:
- Removed the fallback to the main context store object metadata item
2025-04-28 18:18:15 +02:00
Lucas BordeauandGitHub 7563b8b919 Refactored editable filter chip dropdown opening (#11765)
This PR is refactoring a part of the ongoing filter refactor that was
blocking other refactor in that area.

Precisely, the dropdown filter that was used with the editable filter
chip was initialized by two conflicting useEffect, causing many unwanted
and hard to tackle bugs when modifying other places in the code that
used the same dropdown.

We also remove a difficult to maintain pattern around
onToggleColumnFilterComponentState, which was storing a click handler in
a state, we want to avoid this pattern.

The hook useHandleToggleColumnFilter is also removed and replaced by
useOpenRecordFilterChipFromTableHeader.

The code is now synchronous and starts from the user click event that is
triggered on a table cell header filter button click.

Also : 

- Created a useSetEditableFilterChipDropdownStates that allows to
separate the code path of filter chip dropdown from the code path of
view bar global filter dropdown (will be continued in another refactor)
- Added useCreateEmptyFilterFromFieldMetadataItem to abstract empty
filter creation when opening a filter dropdown (will be used for other
refactor)
- Created a useOpenDropdownFromOutside hook that will also be used for
other refactor on filter
- Deleted EditableFilterDropdownButtonEffect
- Removed call to ViewBarFilterEffect (will be completely removed in
other refactors)
2025-04-28 17:36:48 +02:00
Raphaël BosiandGitHub 6cf44ef3c2 Fix open record in menu (#11772)
Fix open record in menu
2025-04-28 16:39:44 +02:00
Thomas TrompetteandGitHub b182a676d7 Fix next step ids backfill command (#11769)
Twenty prod DB has been exported for testing.

Main updates:
- do not process workflow runs with less than 2 steps. Nothing to do.
- update runs by batches of 500. Will avoid java heap space issue
- add more logs
2025-04-28 16:24:15 +02:00
MarieGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
0881e35d64 Throw error if attempting to open RecordPage without recordId (#11768)
Closes https://github.com/twentyhq/core-team-issues/issues/857

The issue was caused by the fact that the preview chip was accidentally
made clickable while not linked to any record id:
<img width="763" alt="Capture d’écran 2025-04-28 à 15 17 32"
src="https://github.com/user-attachments/assets/c1d9bf61-edcb-442f-a914-eccc627ee190"
/>
this was fixed by [this
PR](https://github.com/twentyhq/twenty/pull/11745) (@etiennejouan)

It was causing the side panel to open while the record id was empty,
while this recordId is used in query filters (as it should be to fetch
record data), leading the queries to fail.
Let's early return with an error instead as it does not make sense to
open the record page with an empty recordId.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-28 13:36:19 +00:00
EtienneandGitHub 2586d0ec4f fix workflow chips not clickable (#11767)
bug introduced in this PR https://github.com/twentyhq/twenty/pull/1174
2025-04-28 15:30:26 +02:00
14afa57699 i18n - translations (#11758)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-27 19:01:13 +02:00
Abdul RahmanandGitHub 902cef78a1 Add Logout Confirmation Popup When Changing Workspace Subdomain (#11756)
This PR implements a confirmation popup on the Domain settings page when
a user attempts to save a subdomain change.
- When the user saves the updated subdomain, a confirmation modal is
shown.
- The modal informs the user that changing the subdomain will log them
and all other users out.
- If the user confirms, the subdomain change proceeds as normal.
- If the user cancels, the update is aborted and no changes are made.

### Demo


https://github.com/user-attachments/assets/dcea51c8-ffd2-40ca-bc75-0c0228df5344


Related Issue
Closes #11741
2025-04-27 18:47:06 +02:00
a15b87649a Twenty config core implementation (#11595)
closes https://github.com/twentyhq/core-team-issues/issues/760

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2025-04-26 09:21:59 +02:00
Félix MalfaitandGitHub bb8fa02899 Remove server preconnect (#11752)
Fixes #11738
2025-04-26 08:23:28 +02:00
7752b8b06f i18n - translations (#11750)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-25 19:45:48 +02:00
Lucas BordeauandGitHub 50cb32d122 Implement sub-field filtering on CURRENCY field type (#11726)
This PR implements sub-field filtering on CURRENCY field type and
improves many related zones.

- Created a ObjectFilterDropdownCurrencySelect dropdown component for
filtering on multiple currencies
- Added currencyCode sub-field to CurrencyFilter type
- Created getDefaultSubFieldNameForCompositeFilterableFieldType to avoid
situation where we don't have any sub field name in sub field filtering
situations.
- Implemented filtering for currencyCode in
computeFilterRecordGqlOperationFilter
- Implemented CURRENCY type in getRecordFilterOperands
- Implemented isMatchingCurrencyFilter for using in
isRecordMatchingFilter for proper optimistic rendering
- Created turnCurrencyIntoSelectableItem to help
ObjectFilterDropdownCurrencySelect

Testing : 
- Added test for currency sub fields in getOperandsForFilterType
- Completely reworked isMatchingCurrencyFilter test

Improvements : 
- Created a unique CURRENCIES constant to avoid re-creating it at
various places
- Derive the type FilterableFieldType from a constant array
FILTERABLE_FIELD_TYPES, so it's easier to work with
- Added areCompositeTypeSubFieldsFilterable
- Fixed a bug with empty value '[]' that was preventing the auto-removal
of a filter chip

Miscellaneous : 
- Created isExpectedSubFieldName util to do a type-safe check of a
subFieldName
- Better naming : renamed isCompositeField to isCompositeFieldType
- Created isCompositeTypeFilterableWithAny to specify which field types
are filterable by any sub field
- Better naming : renamed
ObjectFilterDropdownFilterSelectCompositeFieldSubMenu to
ObjectFilterDropdownSubFieldSelect
- Better naming : renamed ObjectFilterDropdownFilterSelect to
ObjectFilterDropdownFieldSelect
- Created isEmptinessOperand util instead of duplicating the same
hard-coded check in multiple places
- Better naming : used subFieldName instead of compositeFieldName for
consistency
- UseEffect removal : removed unnecessary useEffect in
MultipleSelectDropdown

Fixes a bug where Empty and Not weren't appearing in filter chip in
particular cases
Fixes https://github.com/twentyhq/core-team-issues/issues/498
Fixes https://github.com/twentyhq/twenty/issues/7558
2025-04-25 17:33:00 +00:00
f201091c68 512 Ability to navigate dropdown menus with keyboard (#11735)
# Ability to navigate dropdown menus with keyboard

The aim of this PR is to improve accessibility by allowing the user to
navigate inside the dropdown menus with the keyboard.
This PR refactors the `SelectableList` and `SelectableListItem`
components to move the Enter event handling responsibility from
`SelectableList` to the individual `SelectableListItem` components.
Closes [512](https://github.com/twentyhq/core-team-issues/issues/512)

## Key Changes:
- All dropdowns are now navigable with arrow keys

## Technical Implementation:
- Each `SelectableListItem` now has direct access to its own `Enter` key
handler, improving component encapsulation
- Removed the central `Enter` key handler logic from `SelectableList`
- Added `SelectableList` and `SelectableListItem` to all `Dropdown`
components inside the app
- Updated all component implementations to adapt to the new pattern:
  - Action menu components (`ActionDropdownItem`, `ActionListItem`)
  - Command menu components
  - Object filter, sort and options dropdowns
  - Record picker components
  - Select components

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-04-25 18:55:39 +02:00
0b1b81429e fix relation fields preview in settings (#11745)
Fixes :
- display relation fields in preview settings
- display note and task relation field in preview settings - 
- fix design (align and background color)

closes https://github.com/twentyhq/twenty/issues/7084

---------

Co-authored-by: guillim <guigloo@msn.com>
2025-04-25 18:26:27 +02:00
MarieGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
11d9d964cc Add upgrade instructions since v0.50.0 (#11748)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-04-25 18:22:59 +02:00
GuillimandGitHub 95808b06b9 Messaging-logging (#11723)
parseBatchResponse:
we need more logs to understand why we have some empty repsonse in the
body.

For anothe PR:  
~~UNKNOWN_NETWORK_ERROR : moving from handleUnknownException to
handleTemporaryException. This is important since in the logs I saw "The
service is currently unavailable" which makes me think we should handle
this type of error with as temporary~~
2025-04-25 18:01:53 +02:00
Félix MalfaitandGitHub dd446ef441 Fix CSV upload (#11749)
I inadvertently broke the upload button on the CSV import
2025-04-25 17:49:50 +02:00
Thomas des FrancsandGitHub 904558e7bb 0.52.0 changelog (#11743) 2025-04-25 17:20:30 +02:00
Raphaël BosiandGitHub 5207836566 Table hover and click outside fixes (#11737)
# Table hover and click outside fixes

This PR improves table interaction behavior by refining cell hover
states and click-outside handling in the record table component.

## Changes

### Click Outside Handling
- Removed conditional rendering of
`RecordTableBodyFocusClickOutsideEffect`

### Hover State Management
Implemented hover state cleanup in multiple components:
- Added `recordTableHoverPosition` state reset in `useLeaveTableFocus`
hook
- Integrated mouse leave handler in `RecordTableBodyDroppable` to clear
hover position

### Fixes double focus bug
- Reset the focus and the hover when the table data changes

## Videos

### Before


https://github.com/user-attachments/assets/f815b65c-c545-4841-a0d9-04c58771e69f

### After


https://github.com/user-attachments/assets/046cc7df-18b8-46ca-b2cc-bdfa3125311b
2025-04-25 16:50:48 +02:00
Félix MalfaitandGitHub 535a8d4410 Fix workflow preview environment repository dispatch (#11739)
This is hard to test without merging PRs unfortunately

Goal of this PR is to replace the action I had introduced since there
was already a similar one in the codebase
2025-04-25 15:52:12 +02:00
Félix MalfaitandGitHub 3fc498dba7 Attempt to add preview environments (#11734)
Just doing a quick test
2025-04-25 15:42:40 +02:00
GuillimandGitHub 1ff9f35b35 Record name missing when being created from a filter (#11733)
name of record not appearing when being created from a filter

Before :  
<img width="223" alt="Screenshot 2025-04-25 at 10 48 27"
src="https://github.com/user-attachments/assets/e3c7cc05-8664-4c14-bcaa-d5b045eb8311"
/>

After :  
<img width="236" alt="Screenshot 2025-04-25 at 10 48 53"
src="https://github.com/user-attachments/assets/778e02e0-0451-4125-b13e-0c2ac222ef41"
/>
2025-04-25 12:59:15 +00:00
Félix MalfaitandGitHub c66b7afefb Update cache policy (#11732)
I couldn't reproduce
https://github.com/twentyhq/twenty/issues/9299#issuecomment-2576868142
anymore

Closes #4914
2025-04-25 11:18:48 +02:00
Charles BochetandGitHub 0c8eb149e6 Refactor new relation sync (#11711)
In this PR:
- this should fix the sync metadata for new relation system

This goes with the recent PR:
https://github.com/twentyhq/twenty/pull/11725

What we want:
- ONE_TO_MANY relations should have no joinColumn and no onDelete
- MANY_TO_ONE should have both
2025-04-25 01:02:49 +02:00
9fb7ef5d47 Automatically Apply Values on Filtered Views (#11717)
Issue : When I create a task in the "Assigned to me" task view, it will
disappear from the view because the Assignee field isn't automatically
populated.

Solution :
We created a "buildRecordInputFromFilters" funciron that will convert
filtered into their corresponding values for the input.


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

---------

Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
2025-04-24 19:02:03 +02:00
martmullandGitHub 4d7dbb1991 Fix missing on delete cascade for relation (#11725)
Follow up from https://github.com/twentyhq/twenty/pull/10217
Adding onDelete action on Many_to_one sides
2025-04-24 18:32:14 +02:00
c0df96b383 i18n - translations (#11727)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-24 18:21:21 +02:00
MarieandGitHub e750ef28a1 [permission] Override query builders db-executing methods (#11714)
closes https://github.com/twentyhq/core-team-issues/issues/843
2025-04-24 18:20:21 +02:00
WeikoandGitHub e55ecb4dcd object level override form (#11672) 2025-04-24 18:15:43 +02:00
Baptiste DevessierandGitHub 48e2581581 Stop persisting JSON fields in workflow runs (#11715)
## Before


https://github.com/user-attachments/assets/903c5771-ac15-49a6-9175-40c8b027655b

## After


https://github.com/user-attachments/assets/26aedf77-31ac-439a-a39c-57a274010f3f
2025-04-24 17:02:26 +02:00
6a75420285 i18n - translations (#11724)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-04-24 17:01:09 +02:00
Paul RastoinandGitHub 8b63d45b70 Fix missing ObjectRecordId param (#11721)
# Introduction
Fixes https://github.com/twentyhq/twenty/issues/11718

From having
[noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig/#noUncheckedIndexedAccess)
set to false we have a flakiness resulting in a such bug right here as
the below operation can return `undefined` but not type as it should:
```ts
  const recordId = allRecordIds[position.row];
```

About to create a Tech project about the topic, activating the flag ends
in 1500 typescript erros from the style solution compilation ( which
means can contains several duplicated errors )
2025-04-24 16:49:56 +02:00
Félix MalfaitandGitHub 5343dd62d6 Update readme with partner logos (#11722)
Recognize the role of Greptile/Sentry/Crowdin/Chromatic which give
generous open source plans + some cleanup
2025-04-24 16:09:43 +02:00
Raphaël BosiandGitHub 4a61956087 Fixes on record title input (#11720)
- Fix record creation title cell opening
- Fix full name input hotkey scope
2025-04-24 15:29:21 +02:00
Paul RastoinandGitHub b545ebc53b Hide toggle isSyncLabelName for standardObject field edition (#11713)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/880
2025-04-24 13:51:10 +02:00
Baptiste DevessierandGitHub cc211550ae Workflow runs in side panel (#11669)
Vidéo explicative : https://share.cleanshot.com/VsvWknlW

Closes https://github.com/twentyhq/core-team-issues/issues/810
Closes https://github.com/twentyhq/core-team-issues/issues/806

Known issues to fix later:

- https://github.com/twentyhq/core-team-issues/issues/879
2025-04-24 11:33:17 +02:00
Paul RastoinandGitHub 0083569606 Fix object model settings label synchronization (#11708)
# Introduction
Closes https://github.com/twentyhq/twenty/issues/11150
2025-04-24 10:58:59 +02:00
EtienneandGitHub ccc6d968aa update price on subscription - command (#11698) 2025-04-24 09:32:03 +02:00
Félix MalfaitandGitHub 0b729cb000 Fix json type in clickhouse migrations (#11710)
Settings in clickhouse are only set for the duration of the session so
we need to keep the same client
2025-04-23 22:08:52 +02:00
Félix MalfaitandGitHub cec17a3ebe 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:16:30 +02:00
787 changed files with 25640 additions and 5716 deletions
@@ -0,0 +1,43 @@
name: 'Preview Environment Dispatch'
on:
# Using pull_request_target instead of pull_request to have access to secrets for external contributors
# Security note: This is safe because we're only using the repository-dispatch action with limited scope
# and not checking out or running any code from the external contributor's PR
pull_request_target:
types: [opened, synchronize, reopened, labeled]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
actions: write
pull-requests: read
jobs:
changed-files-check:
uses: ./.github/workflows/changed-files.yaml
with:
files: |
.github/workflows/preview-env-dispatch.yaml
.github/workflows/preview-env-keepalive.yaml
packages/twenty-docker/**
docker-compose.yml
packages/twenty-server/**
packages/twenty-front/**
trigger-preview:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true' || contains(github.event.pull_request.labels.*.name, 'preview')
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- name: Trigger preview environment workflow
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
event-type: preview-environment
client-payload: '{"pr_number": "${{ github.event.pull_request.number }}", "pr_head_sha": "${{ github.event.pull_request.head.sha }}", "repo_full_name": "${{ github.repository }}"}'
@@ -0,0 +1,141 @@
name: 'Preview Environment Keep Alive'
on:
repository_dispatch:
types: [preview-environment]
jobs:
preview-environment:
timeout-minutes: 310
runs-on: ubuntu-latest
steps:
- name: Checkout PR
uses: actions/checkout@v4
with:
ref: ${{ github.event.client_payload.pr_head_sha }}
- name: Run compose setup
run: |
echo "Patching docker-compose.yml..."
# change image to localbuild using yq
yq eval 'del(.services.server.image)' -i packages/twenty-docker/docker-compose.yml
yq eval '.services.server.build.context = "../../"' -i packages/twenty-docker/docker-compose.yml
yq eval '.services.server.build.dockerfile = "./packages/twenty-docker/twenty/Dockerfile"' -i packages/twenty-docker/docker-compose.yml
yq eval 'del(.services.worker.image)' -i packages/twenty-docker/docker-compose.yml
yq eval '.services.worker.build.context = "../../"' -i packages/twenty-docker/docker-compose.yml
yq eval '.services.worker.build.dockerfile = "./packages/twenty-docker/twenty/Dockerfile"' -i packages/twenty-docker/docker-compose.yml
echo "Setting up .env file..."
cp packages/twenty-docker/.env.example packages/twenty-docker/.env
echo "Generating secrets..."
echo "# === Randomly generated secrets ===" >> packages/twenty-docker/.env
echo "APP_SECRET=$(openssl rand -base64 32)" >> packages/twenty-docker/.env
echo "PG_DATABASE_PASSWORD=$(openssl rand -hex 16)" >> packages/twenty-docker/.env
# Remove line below when true becomes the default value (soon)
echo "CONFIG_VARIABLES_IN_DB_ENABLED=true" >> packages/twenty-docker/.env
echo "Docker compose build..."
cd packages/twenty-docker/
docker compose build
working-directory: ./
- name: Create Tunnel
id: expose-tunnel
uses: codetalkio/expose-tunnel@v1.5.0
with:
service: bore.pub
port: 3000
- name: Start services with correct SERVER_URL
run: |
cd packages/twenty-docker/
# Update the SERVER_URL with the tunnel URL
echo "Setting SERVER_URL to ${{ steps.expose-tunnel.outputs.tunnel-url }}"
sed -i '/SERVER_URL=/d' .env
echo "SERVER_URL=${{ steps.expose-tunnel.outputs.tunnel-url }}" >> .env
# Start the services
echo "Docker compose up..."
docker compose up -d || {
echo "Docker compose failed to start"
docker compose logs
exit 1
}
echo "Waiting for services to be ready..."
count=0
while [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-db-1) = "healthy" ] || [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-server-1) = "healthy" ]; do
sleep 5
count=$((count+1))
if [ $count -gt 60 ]; then
echo "Timeout waiting for services to be ready"
docker compose logs
exit 1
fi
echo "Still waiting for services... ($count/60)"
done
echo "All services are up and running!"
working-directory: ./
- name: Output tunnel URL to logs
run: |
echo "✅ Preview Environment Ready!"
echo "🔗 Preview URL: ${{ steps.expose-tunnel.outputs.tunnel-url }}"
echo "⏱️ This environment will be available for 5 hours"
- name: Post comment on PR
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const COMMENT_MARKER = '<!-- PR_PREVIEW_ENV -->';
const commentBody = `${COMMENT_MARKER}
🚀 **Preview Environment Ready!**
Your preview environment is available at: ${{ steps.expose-tunnel.outputs.tunnel-url }}
This environment will automatically shut down when the PR is closed or after 5 hours.`;
// Get all comments
const {data: comments} = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.client_payload.pr_number }},
});
// Find our comment
const botComment = comments.find(comment => comment.body.includes(COMMENT_MARKER));
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
console.log('Updated existing comment');
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.client_payload.pr_number }},
body: commentBody
});
console.log('Created new comment');
}
- name: Keep tunnel alive for 5 hours
run: timeout 300m sleep 18000 # Stop on whichever we reach first (300m or 5hour sleep)
- name: Cleanup
if: always()
run: |
cd packages/twenty-docker/
docker compose down -v
working-directory: ./
+45 -34
View File
@@ -1,28 +1,28 @@
<br>
<br />
<p align="center">
<a href="https://www.twenty.com">
<img src="./packages/twenty-website/public/images/core/logo.svg" width="100px" alt="Twenty logo" />
</a>
</p>
<h2 align="center" >The #1 Open-Source CRM </h3>
<h2 align="center" >The #1 Open-Source CRM </h2>
<p align="center"><a href="https://twenty.com">🌐 Website</a> · <a href="https://twenty.com/developers">📚 Documentation</a> · <a href="https://github.com/orgs/twentyhq/projects/1"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="12" height="12"/> Roadmap </a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty"><img src="./packages/twenty-website/public/images/readme/figma-icon.png" width="12" height="12"/> Figma</a><p>
<p align="center"><a href="https://twenty.com">🌐 Website</a> · <a href="https://twenty.com/developers">📚 Documentation</a> · <a href="https://github.com/orgs/twentyhq/projects/1"><img src="./packages/twenty-website/public/images/readme/planner-icon.svg" width="12" height="12"/> Roadmap </a> · <a href="https://discord.gg/cx5n4Jzs57"><img src="./packages/twenty-website/public/images/readme/discord-icon.svg" width="12" height="12"/> Discord</a> · <a href="https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty"><img src="./packages/twenty-website/public/images/readme/figma-icon.png" width="12" height="12"/> Figma</a></p>
<br />
<p align="center">
<a href="https://www.twenty.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/preview-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/preview-light.png">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/preview-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/preview-light.png" />
<img src="./packages/twenty-docs/static/img/preview-light.png" alt="Companies view" />
</picture>
</a>
</p>
<br>
<br />
# Installation
@@ -40,7 +40,7 @@ We built Twenty for three reasons:
**We believe in Open-source and community.** Hundreds of developers are already building Twenty together. Once we have plugin capabilities, a whole ecosystem will grow around it.
<br>
<br />
# What You Can Do With Twenty
We're currently developing Twenty's beta version.
@@ -60,8 +60,8 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/index-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/index-light.png">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/index-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/index-light.png" />
<img src="./packages/twenty-docs/static/img/visualise-customer-light.png" alt="Companies view" />
</picture>
</p>
@@ -70,9 +70,9 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/kanban-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/kanban-light.png">
<img src="./packages/twenty-docs/static/img/follow-your-deals-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/kanban-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/kanban-light.png" />
<img src="./packages/twenty-docs/static/img/follow-your-deals-light.png" alt="Opportunities view" />
</picture>
</p>
@@ -80,9 +80,9 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/emails-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/emails-light.png">
<img src="./packages/twenty-docs/static/img/rich-notes-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/emails-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/emails-light.png" />
<img src="./packages/twenty-docs/static/img/emails-light.png" alt="Emails" />
</picture>
</p>
@@ -90,9 +90,9 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/data-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/data-light.png">
<img src="./packages/twenty-docs/static/img/rich-notes-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/data-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/data-light.png" />
<img src="./packages/twenty-docs/static/img/data-light.png" alt="Data model" />
</picture>
</p>
@@ -100,9 +100,9 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/notes-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/notes-light.png">
<img src="./packages/twenty-docs/static/img/rich-notes-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/notes-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/notes-light.png" />
<img src="./packages/twenty-docs/static/img/notes-light.png" alt="Rich notes" />
</picture>
</p>
@@ -110,9 +110,9 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/tasks-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/tasks-light.png">
<img src="./packages/twenty-docs/static/img/create-tasks-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/tasks-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/tasks-light.png" />
<img src="./packages/twenty-docs/static/img/create-tasks-light.png" alt="Tasks" />
</picture>
</p>
@@ -120,9 +120,9 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/keyboard-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/keyboard-light.png">
<img src="./packages/twenty-docs/static/img/shortcut-navigation-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/keyboard-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/keyboard-light.png" />
<img src="./packages/twenty-docs/static/img/keyboard-dark.png" alt="Keyboard shortcuts" />
</picture>
</p>
@@ -130,22 +130,33 @@ Below are a few features we have implemented to date:
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/api-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/api-light.png">
<img src="./packages/twenty-docs/static/img/shortcut-navigation-light.png" alt="Companies view" />
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/api-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/twentyhq/twenty/v0.12.0/packages/twenty-docs/static/img/api-light.png" />
<img src="./packages/twenty-docs/static/img/api-light.png" alt="API" />
</picture>
</p>
<br>
<br />
# Stack
- [TypeScript](https://www.typescriptlang.org/)
- [Nx](https://nx.dev/)
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
- [React](https://reactjs.org/), with [Recoil](https://recoiljs.org/) and [Emotion](https://emotion.sh/)
- [Greptile](https://greptile.com) for code reviews.
- [Lingui](https://lingui.dev/) and [Crowdin](https://twenty.crowdin.com/twenty) for translations.
- [React](https://reactjs.org/), with [Recoil](https://recoiljs.org/), [Emotion](https://emotion.sh/) and [Lingui](https://lingui.dev/)
# Thanks
<p align="center">
<a href="https://www.chromatic.com/"><img src="./packages/twenty-website/public/images/readme/chromatic.png" height="30" alt="Chromatic" /></a>
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.png" height="30" alt="Greptile" /></a>
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.png" height="30" alt="Sentry" /></a>
<a href="https://crowdin.com/"><img src="./packages/twenty-website/public/images/readme/crowdin.png" height="30" alt="Crowdin" /></a>
</p>
Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
# Join the Community
View File
+1
View File
@@ -77,6 +77,7 @@
"bytes": "^3.1.2",
"class-transformer": "^0.5.1",
"clsx": "^2.1.1",
"cron-parser": "^5.1.1",
"cron-validate": "^1.4.5",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
@@ -1,7 +1,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/icons/android/android-launchericon-48-48.png" />
<link rel="icon" href="images/icons/android/android-launchericon-48-48.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Twenty</title>
</head>
@@ -1,7 +1,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/icons/android/android-launchericon-48-48.png" />
<link rel="icon" href="images/icons/android/android-launchericon-48-48.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Twenty</title>
</head>
@@ -1,7 +1,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/icons/android/android-launchericon-48-48.png" />
<link rel="icon" href="images/icons/android/android-launchericon-48-48.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Twenty</title>
<style>
@@ -24,6 +24,8 @@ export class WorkflowVisualizerPage {
readonly useAsDraftButton: Locator;
readonly overrideDraftButton: Locator;
readonly discardDraftButton: Locator;
readonly seeRunsButton: Locator;
readonly goBackInCommandMenu: Locator;
#actionNames: Record<WorkflowActionType, string> = {
'create-record': 'Create Record',
@@ -31,6 +33,7 @@ export class WorkflowVisualizerPage {
'delete-record': 'Delete Record',
code: 'Code',
'send-email': 'Send Email',
form: 'Form',
};
#createdActionNames: Record<WorkflowActionType, string> = {
@@ -39,6 +42,7 @@ export class WorkflowVisualizerPage {
'delete-record': 'Delete Record',
code: 'Code - Serverless Function',
'send-email': 'Send Email',
form: 'Form',
};
#triggerNames: Record<WorkflowTriggerType, string> = {
@@ -84,6 +88,10 @@ export class WorkflowVisualizerPage {
this.discardDraftButton = page.getByRole('button', {
name: 'Discard Draft',
});
this.seeRunsButton = page.getByRole('link', { name: 'See runs' });
this.goBackInCommandMenu = this.commandMenu
.getByRole('button')
.and(this.commandMenu.getByTestId('command-menu-go-back-button'));
}
async createOneWorkflow() {
@@ -250,29 +258,64 @@ export class WorkflowVisualizerPage {
await expect(this.#page.getByTestId('command-menu')).not.toBeVisible();
}
async goToWorkflowsIndexPage() {
await this.#page.goto('/objects/workflows');
}
async setWorkflowsOpenInMode(mode: 'side-panel' | 'record-page') {
const recordTableOptionsButton = this.#page.getByText('Options');
await recordTableOptionsButton.click();
const layoutButton = this.#page.getByText('Layout');
await layoutButton.click();
const openInButton = this.#page.getByText('Open in');
await openInButton.click();
if (mode === 'side-panel') {
const openInSidePanelOption = this.#page.getByRole('option', {
name: 'Side Panel',
});
await openInSidePanelOption.click();
} else {
const openInRecordPageOption = this.#page.getByRole('option', {
name: 'Record Page',
});
await openInRecordPageOption.click();
}
// Close the dropdown
await recordTableOptionsButton.click();
}
}
export const test = base.extend<{ workflowVisualizer: WorkflowVisualizerPage }>(
{
workflowVisualizer: async ({ page }, use) => {
const workflowVisualizer = new WorkflowVisualizerPage({
page,
workflowName: 'Test Workflow',
});
export const test = base.extend<{
workflowVisualizer: WorkflowVisualizerPage;
}>({
workflowVisualizer: async ({ page }, use) => {
const workflowVisualizer = new WorkflowVisualizerPage({
page,
workflowName: 'Test Workflow',
});
await workflowVisualizer.createOneWorkflow();
await workflowVisualizer.goToWorkflowVisualizerPage();
await workflowVisualizer.createOneWorkflow();
await workflowVisualizer.goToWorkflowVisualizerPage();
await use(workflowVisualizer);
await use(workflowVisualizer);
await deleteWorkflow({
page,
workflowId: workflowVisualizer.workflowId,
});
await destroyWorkflow({
page,
workflowId: workflowVisualizer.workflowId,
});
},
await deleteWorkflow({
page,
workflowId: workflowVisualizer.workflowId,
});
await destroyWorkflow({
page,
workflowId: workflowVisualizer.workflowId,
});
await workflowVisualizer.goToWorkflowsIndexPage();
await workflowVisualizer.setWorkflowsOpenInMode('record-page');
},
);
});
@@ -9,4 +9,5 @@ export type WorkflowActionType =
| 'update-record'
| 'delete-record'
| 'code'
| 'send-email';
| 'send-email'
| 'form';
@@ -56,3 +56,98 @@ test('The workflow run visualizer shows the executed draft version without the l
'Create Record',
);
});
// FIXME: Documented bug. See https://github.com/twentyhq/core-team-issues/issues/921
test.fail('Workflow Runs with a pending form step can be opened in the side panel and then in full screen', async ({
workflowVisualizer,
page,
}) => {
await workflowVisualizer.createInitialTrigger('manual');
const manualTriggerAvailabilitySelect = page.getByRole('button', {
name: 'When record(s) are selected',
});
await manualTriggerAvailabilitySelect.click();
const alwaysAvailableOption = page.getByText(
'When no record(s) are selected',
);
await alwaysAvailableOption.click();
await workflowVisualizer.closeSidePanel();
const { createdStepId: firstStepId } =
await workflowVisualizer.createStep('form');
await workflowVisualizer.closeSidePanel();
const launchTestButton = page.getByLabel(workflowVisualizer.workflowName);
await launchTestButton.click();
const goToExecutionPageLink = page.getByRole('link', {
name: 'View execution details',
});
await expect(goToExecutionPageLink).toBeVisible();
await workflowVisualizer.seeRunsButton.click();
const workflowRunName = `#1 - ${workflowVisualizer.workflowName}`;
const workflowRunNameCell = page.getByRole('cell', { name: workflowRunName });
await expect(workflowRunNameCell).toBeVisible();
await workflowVisualizer.setWorkflowsOpenInMode('side-panel');
// 1. Exit the dropdown
await workflowRunNameCell.click();
// 2. Actually open the workflow run in the side panel
await workflowRunNameCell.click();
await expect(workflowVisualizer.stepHeaderInCommandMenu).toContainText(
'Form',
);
await workflowVisualizer.goBackInCommandMenu.click();
const workflowRunNameInCommandMenu =
workflowVisualizer.commandMenu.getByText(workflowRunName);
await expect(workflowRunNameInCommandMenu).toBeVisible();
await workflowVisualizer.triggerNode.click();
await expect(workflowVisualizer.stepHeaderInCommandMenu).toContainText(
'Launch manually',
);
await workflowVisualizer.goBackInCommandMenu.click();
const formStep = workflowVisualizer.getStepNode(firstStepId);
await formStep.click();
await workflowVisualizer.goBackInCommandMenu.click();
const openInFullScreenButton = workflowVisualizer.commandMenu.getByRole(
'button',
{ name: 'Open' },
);
await openInFullScreenButton.click();
const workflowRunNameInShowPage = page
.getByText(`#1 - ${workflowVisualizer.workflowName}`)
.nth(1);
await expect(workflowRunNameInShowPage).toBeVisible();
// Expect the side panel to be opened by default on the form.
await expect(workflowVisualizer.stepHeaderInCommandMenu).toContainText(
'Form',
);
});
@@ -8,25 +8,19 @@ test('Use an old version as draft', async ({ workflowVisualizer, page }) => {
await workflowVisualizer.background.click();
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Active'),
await workflowVisualizer.activateWorkflowButton.click();
workflowVisualizer.activateWorkflowButton.click(),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Active');
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Draft'),
await workflowVisualizer.createStep('delete-record');
workflowVisualizer.createStep('delete-record'),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Draft');
await workflowVisualizer.background.click();
await workflowVisualizer.closeSidePanel();
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Active'),
await workflowVisualizer.activateWorkflowButton.click();
workflowVisualizer.activateWorkflowButton.click(),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Active');
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
@@ -41,6 +35,8 @@ test('Use an old version as draft', async ({ workflowVisualizer, page }) => {
const workflowsLink = page.getByRole('link', { name: 'Workflows' });
await workflowsLink.click();
await workflowVisualizer.setWorkflowsOpenInMode('record-page');
const recordTableRowForWorkflow = page.getByRole('row', {
name: workflowVisualizer.workflowName,
});
@@ -48,7 +44,7 @@ test('Use an old version as draft', async ({ workflowVisualizer, page }) => {
const linkToWorkflow = recordTableRowForWorkflow.getByRole('link', {
name: workflowVisualizer.workflowName,
});
expect(linkToWorkflow).toBeVisible();
await expect(linkToWorkflow).toBeVisible();
const linkToFirstWorkflowVersion = recordTableRowForWorkflow.getByRole(
'link',
@@ -69,11 +65,9 @@ test('Use an old version as draft', async ({ workflowVisualizer, page }) => {
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(1);
await Promise.all([
page.waitForURL(`/object/workflow/${workflowVisualizer.workflowId}`),
await workflowVisualizer.useAsDraftButton.click();
workflowVisualizer.useAsDraftButton.click(),
]);
await page.waitForURL(`/object/workflow/${workflowVisualizer.workflowId}`);
await expect(workflowVisualizer.workflowStatus).toHaveText('Draft');
await expect(workflowVisualizer.useAsDraftButton).not.toBeVisible();
@@ -96,17 +90,13 @@ test('Use an old version as draft while having a pending draft version', async (
await workflowVisualizer.background.click();
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Active'),
await workflowVisualizer.activateWorkflowButton.click();
workflowVisualizer.activateWorkflowButton.click(),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Active');
await Promise.all([
expect(workflowVisualizer.workflowStatus).toHaveText('Draft'),
await workflowVisualizer.createStep('delete-record');
workflowVisualizer.createStep('delete-record'),
]);
await expect(workflowVisualizer.workflowStatus).toHaveText('Draft');
await expect(workflowVisualizer.triggerNode).toContainText(
'Record is Created',
@@ -130,7 +120,7 @@ test('Use an old version as draft while having a pending draft version', async (
const linkToWorkflow = recordTableRowForWorkflow.getByRole('link', {
name: workflowVisualizer.workflowName,
});
expect(linkToWorkflow).toBeVisible();
await expect(linkToWorkflow).toBeVisible();
const linkToFirstWorkflowVersion = recordTableRowForWorkflow.getByRole(
'link',
@@ -151,17 +141,13 @@ test('Use an old version as draft while having a pending draft version', async (
]);
await expect(workflowVisualizer.getAllStepNodes()).toHaveCount(1);
await Promise.all([
expect(workflowVisualizer.overrideDraftButton).toBeVisible(),
await workflowVisualizer.useAsDraftButton.click();
workflowVisualizer.useAsDraftButton.click(),
]);
await expect(workflowVisualizer.overrideDraftButton).toBeVisible();
await Promise.all([
page.waitForURL(`/object/workflow/${workflowVisualizer.workflowId}`),
await workflowVisualizer.overrideDraftButton.click();
workflowVisualizer.overrideDraftButton.click(),
]);
await page.waitForURL(`/object/workflow/${workflowVisualizer.workflowId}`);
await expect(workflowVisualizer.workflowStatus).toHaveText('Draft');
await expect(workflowVisualizer.useAsDraftButton).not.toBeVisible();
+1 -2
View File
@@ -54,8 +54,7 @@ export const emailTheme = {
},
background: {
colors: { highlight: grayScale.gray15 },
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
button: grayScale.gray60,
transparent: {
medium: 'rgba(0, 0, 0, 0.08)',
light: 'rgba(0, 0, 0, 0.04)',
@@ -3,15 +3,16 @@ import { Button } from '@react-email/components';
import { emailTheme } from 'src/common-style';
const callToActionStyle = {
display: 'flex',
display: 'inline-flex',
padding: '8px 32px',
borderRadius: emailTheme.border.radius.md,
border: `1px solid ${emailTheme.background.transparent.light}`,
background: emailTheme.background.radialGradient,
background: emailTheme.background.button,
boxShadow: `0px 2px 4px 0px ${emailTheme.background.transparent.light}, 0px 0px 4px 0px ${emailTheme.background.transparent.medium}`,
color: emailTheme.font.colors.inverted,
fontSize: emailTheme.font.size.md,
fontWeight: emailTheme.font.weight.bold,
width: 'auto',
};
type CallToActionProps = {
@@ -42,10 +42,13 @@ export const CleanSuspendedWorkspaceEmail = ({
<br />
<Trans id="If you wish to use Twenty again, you can create a new workspace." />
</MainText>
<br />
<CallToAction
href="https://app.twenty.com/"
value={i18n._('Create a new workspace')}
/>
<br />
<br />
</BaseEmail>
);
};
@@ -21,7 +21,6 @@ export const PasswordResetLinkEmail = ({
return (
<BaseEmail locale={locale}>
<Title value={i18n._('Reset your password 🗝')} />
<CallToAction href={link} value={i18n._('Reset')} />
<MainText>
<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:"
@@ -30,6 +29,10 @@ export const PasswordResetLinkEmail = ({
<br />
<Link href={link} value={link} />
</MainText>
<br />
<CallToAction href={link} value={i18n._('Reset')} />
<br />
<br />
</BaseEmail>
);
};
@@ -41,7 +41,10 @@ export const PasswordUpdateNotifyEmail = ({
<Trans id="If you did not initiate this change, please contact your workspace owner immediately." />
<br />
</MainText>
<br />
<CallToAction value={i18n._('Connect to Twenty')} href={link} />
<br />
<br />
</BaseEmail>
);
};
@@ -18,12 +18,13 @@ export const SendEmailVerificationLinkEmail = ({
return (
<BaseEmail width={333} locale={locale}>
<Title value={i18n._('Confirm your email address')} />
<MainText>
<Trans id="Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address." />
</MainText>
<br />
<CallToAction href={link} value={i18n._('Verify Email')} />
<br />
<br />
<MainText>
<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>
</BaseEmail>
);
};
@@ -13,6 +13,8 @@ export const TestEmail = ({ locale }: TestEmailProps) => {
return (
<BaseEmail locale={locale}>
<Title value={i18n._('Test email')} />
<br />
<br />
</BaseEmail>
);
};
@@ -76,6 +76,7 @@ export const SendApprovedAccessDomainValidation = ({
{workspace.name ? <HighlightedText value={workspace.name} /> : <></>}
<CallToAction href={link} value={i18n._('Validate domain')} />
</HighlightedContainer>
<br />
</BaseEmail>
);
};
@@ -54,10 +54,13 @@ export const WarnSuspendedWorkspaceEmail = ({
values={{ remainingDays, dayOrDays }}
/>
</MainText>
<br />
<CallToAction
href="https://app.twenty.com/settings/billing"
value={i18n._('Update your subscription')}
/>
<br />
<br />
</BaseEmail>
);
};
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+13 -8
View File
@@ -90,13 +90,13 @@ msgstr "Confirm your email address"
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgstr "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below 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 below to verify your email address."
#. 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."
msgid "Verify Email"
msgstr "Verify Email"
#. js-lingui-explicit-id
#: src/emails/password-update-notify.email.tsx
@@ -125,13 +125,13 @@ msgstr "Reset your password 🗝"
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
msgstr "Reset"
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/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 "Reset"
msgstr "Reset"
#. js-lingui-explicit-id
#: src/emails/clean-suspended-workspace.email.tsx
@@ -218,6 +218,11 @@ msgstr "Twenty.com, Public Benefit Corporation"
msgid "San Francisco / Paris"
msgstr "San Francisco / Paris"
#. 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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
@@ -90,12 +90,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -125,12 +125,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -218,6 +218,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+9 -4
View File
@@ -95,12 +95,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/send-email-verification-link.email.tsx
msgid "Verify Email"
msgid "Thanks for registering for an account on Twenty! Before we get started, we just need to confirm that this is you. Click below to verify your email address."
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."
msgid "Verify Email"
msgstr ""
#. js-lingui-explicit-id
@@ -130,12 +130,12 @@ msgstr ""
#. js-lingui-explicit-id
#: src/emails/password-reset-link.email.tsx
msgid "Reset"
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/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:"
msgid "Reset"
msgstr ""
#. js-lingui-explicit-id
@@ -223,6 +223,11 @@ msgstr ""
msgid "San Francisco / Paris"
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-id: 4WPI3S
#: src/emails/send-invite-link.email.tsx
#~ msgid "Accept invite"
+3 -2
View File
@@ -6,10 +6,11 @@
<link
rel="icon"
type="image/x-icon"
href="/icons/android/android-launchericon-48-48.png"
href="images/icons/android/android-launchericon-48-48.png"
data-rh="true"
/>
<link rel="apple-touch-icon" href="/icons/ios/192.png" />
<link rel="apple-touch-icon" href="images/icons/ios/192.png" />
<link rel="manifest" href="manifest.json" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="A modern open-source CRM" />

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 432 B

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

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