## Overview
This PR strengthens our permission system by introducing more granular
role-based access control across the platform.
## Changes
### New Permissions Added
- **Applications** - Control who can install and manage applications
- **Layouts** - Control who can customize page layouts and UI structure
- **AI** - Control access to AI features and agents
- **Upload File** - Separate permission for file uploads
- **Download File** - Separate permission for file downloads (frontend
visibility)
### Security Enhancements
- Implemented whitelist-based validation for workspace field updates
- Added explicit permission guards to core entity resolvers
- Enhanced ESLint rule to enforce permission checks on all mutations
- Created `CustomPermissionGuard` and `NoPermissionGuard` for better
code documentation
### Affected Components
- Core entity resolvers: webhooks, files, domains, applications,
layouts, postgres credentials
- Workspace update mutations now use whitelist validation
- Settings UI updated with new permission controls
### Developer Experience
- ESLint now catches missing permission guards during development
- Explicit guard markers make permission requirements clear in code
review
- Comprehensive test coverage for new permission logic
## Testing
- ✅ All TypeScript type checks pass
- ✅ ESLint validation passes
- ✅ New permission guards properly enforced
- ✅ Frontend UI displays new permissions correctly
## Migration Notes
Existing workspaces will need to assign the new permissions to roles as
needed. By default, all new permissions are set to `false` for non-admin
roles.
This PR implements the necessary tools to have `react-datepicker`
calendar and our date picker components work reliably no matter the
timezone difference between the user execution environment and the user
application timezone.
Fixes https://github.com/twentyhq/core-team-issues/issues/1781
This PR won't cover everything needed to have Twenty handle timezone
properly, here is the follow-up issue :
https://github.com/twentyhq/core-team-issues/issues/1807
# Features in this PR
This PR brings a lot of features that have to be merged together.
- DATE field type is now handled as string only, because it shouldn't
involve timezone nor the JS Date object at all, since it is a day like a
birthday date, and not an absolute point in time.
- DATE_TIME field wasn't properly handled when the user settings
timezone was different from the system one
- A timezone abbreviation suffix has been added to most DATE_TIME
display component, only when the timezone is different from the system
one in the settings.
- A lot of bugs, small features and improvements have been made here :
https://github.com/twentyhq/core-team-issues/issues/1781
# Handling of timezones
## Essential concepts
This topic is so complex and easy to misunderstand that it is necessary
to define the precise terms and concepts first. It resembles character
encoding and should be treated with the same care.
- Wall-clock time : the time expressed in the timezone of a user, it is
distinct from the absolute point in time it points to, much like a
pointer being a different value than the value that it points to.
- Absolute time : a point in time, regardless of the timezone, it is an
objective point in time, of course it has to be expressed in a given
timezone, because we have to talk about when it is located in time
between humans, but it is in fact distinct from any wall clock time, it
exists in itself without any clock running on earth. However, by
convention the low-level way to store an absolute point in time is in
UTC, which is a timezone, because there is no way to store an absolute
point in time without a referential, much like a point in space cannot
be stored without a referential.
- DST : Daylight Save Time, makes the timezone shift in a specific
period every year in a given timezone, to make better use of longer days
for various reasons, not all timezones have DST. DST can be 1 hour or 30
min, 45 min, which makes computation difficult.
- UTC : It is NOT an “absolute timezone”, it is the wall-clock time at
0° longitude without DST, which is an arbitrary and shared human
convention. UTC is often used as the standard reference wall-clock time
for talking about absolute point in time without having to do timezone
and DST arithmetic. PostgreSQL stores everything in UTC by convention,
but outputs everything in the server’s SESSION TIMEZONE.
## How should an absolute point in time be stored ?
Since an absolute point in time is essentially distinct from its
timezone it could be stored in an absolute way, but in practice it is
impossible to store an absolute point in time without a referential. We
have to say that a rocket launched at X given time, in UTC, EST, CET,
etc. And of course, someone in China will say that it launched at 10:30,
while in San Francisco it will have launched at 19:30, but it is THE
SAME absolute point in time.
Let’s take a related example in computer science with character
encoding. If a text is stored without the associated encoding table, the
correct meaning associated to the bits stored in memory can be lost
forever. It can become impossible for a program to guess what encoding
table should be used for a given text stored as bits, thus the glitches
that appeared a lot back in the early days of internet and document
processing.
The same can happen with date time storing, if we don’t have the
timezone associated with the absolute point in time, the information of
when it absolutely happened is lost.
It is NOT necessary to store an absolute point in time in UTC, it is
more of a standard and practical wall-clock time to be associated with
an absolute point in time. But an absolute point in time MUST be store
with a timezone, with its time referential, otherwise the information of
when it absolutely happened is lost.
For example, it is easier to pass around a date as a string in UTC, like
`2024-01-02T00:00:00Z` because it allows front-end and back-end code to
“talk” in the same standard and DST-free wall-clock time, BUT it is not
necessary. Because we have date libraries that operate on the standard
ISO timezone tables, we can talk in different timezone and let the
libraries handle the conversion internally.
It is false to say that UTC is an absolute timezone or an absolute point
in time, it is just the standard, conventional time referential, because
one can perfectly store every absolute points in time in UTC+10 with a
complex DST table and have the exactly correct absolute points in time,
without any loss of information, without having any UTC+0 dates
involved.
Thus storing an absolute point in time without a timezone associated,
for example with `timestamp` PostgreSQL data type, is equivalent to
storing a wall-clock time and then throwing away voluntarily the
information that allows to know when it absolutely happened, which is a
voluntary data-loss if the code that stores and retrieves those
wall-clock points in time don’t store the associated timezone somewhere.
This is why we use `timestamptz` type in PostgreSQL, so that we make
sure that the correct absolute point in time is stored at the exact time
we send it to PostgreSQL server, no matter the front-end, back-end and
SQL server's timezone differences.
## The JavaScript Date object
The native JavaScript Date object is now officially considered legacy
([source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)),
the Date object stores an absolute point in time BUT it forces the
storage to use its execution environment timezone, and one CANNOT modify
this timezone, this is a legacy behavior.
To obtain the desired result and store an absolute point in time with an
arbitrary timezone there are several options :
- The new Temporal API that is the successor of the legacy Date object.
- Moment / Luxon / @date-fns/tz that expose objects that allow to use
any timezone to store an absolute point in time.
## How PostgreSQL stores absolute point in times
PostgreSQL stores absolute points in time internally in UTC
([source](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-INPUT-TIME-STAMPS)),
but the output date is expressed in the server’s session timezone
([source](https://www.postgresql.org/docs/current/sql-set.html)) which
can be different from UTC.
Example with the object companies in Twenty seed database, on a local
instance, with a new “datetime” custom column :
<img width="374" height="554" alt="image"
src="https://github.com/user-attachments/assets/4394cb43-d97e-4479-801d-ca068f800e39"
/>
<img width="516" height="524" alt="image"
src="https://github.com/user-attachments/assets/b652f36a-d2e2-47a4-8950-647ca688cbbd"
/>
## Why can’t I just use the JavaScript native Date object with some
manual logic ?
Because the JavaScript Date object does not allow to change its internal
timezone, the libraries that are based on it will behave on the
execution environment timezone, thus leading to bugs that appear only on
the computers of users in a timezone but not for other in another
timezone.
In our case the `react-datepicker` library forces to use the `Date`
object, thus forcing the calendar to behave in the execution environment
system timezone, which causes a lot of problems when we decide to
display the Twenty application DATE_TIME values in another timezone than
the user system one, the bugs that appear will be of the off-by-one date
class, for example clicking on 23 will select 24, thus creating an
unreliable feature for some system / application timezone combinations.
A solution could be to manually compute the difference of minutes
between the application user and the system timezones, but that’s not
reliable because of DST which makes this computation unreliable when DST
are applied at different period of the year for the two timezones.
## Why can’t I compute the timezone difference manually ?
Because of DST, the work to compute the timezone difference reliably,
not just for the usual happy path, is equivalent to developing the
internal mechanism of a date timezone library, which is equivalent to
use a library that handles timezones.
## Using `@date-fns/tz` to solve this problem
We could have used `luxon` but it has a heavy bundle size, so instead we
rely here on `@date-fns/tz` (~1kB) which gives us a `TZDate` object that
allows to use any given timezone to store an absolute point-in-time.
The solution here is to trick `react-datepicker` by shifting a Date
object by the difference of timezone between the user application
timezone and the system timezone.
Let’s take a concerte example.
System timezone : Midway, ⇒ UTC-11:00, has no DST.
User application timezone : Auckland, NZ ⇒ UTC+13:00, has a DST.
We’ll take the NZ daylight time, so that will make a timezone difference
of 24 hours !
Let’s take an error-prone date : `2025-01-01T00:00:00` . This date is
usually a good test-case because it can generate three classes of bugs :
off-by-one day bugs, off-by-one month bugs and off-by-one year bugs, at
the same time.
Here is the absolute point in time we take expressed in the different
wall-clock time points we manipulate
Case | In system timezone ⇒ UTC-11 | In UTC | In user application
timezone ⇒ UTC+13
-- | -- | -- | --
Original date | `2024-12-31T00:00:00-11:00` | `2024-12-31T11:00:00Z` |
`2025-01-01T00:00:00+13:00`
Date shifted for react-datepicker | `2025-01-01T00:00:00-11:00` |
`2025-01-01T11:00:00Z` | `2025-01-02T00:00:00+13:00`
We can see with this table that we have the number part of the date that
is the same (`2025-01-01T00:00:00`) but with a different timezone to
“trick” `react-datepicker` and have it display the correct day in its
calendar.
You can find the code in the hooks
`useTurnPointInTimeIntoReactDatePickerShiftedDate` and
`useTurnReactDatePickerShiftedDateBackIntoPointInTime` that contain the
logic that produces the above table internally.
## Miscellaneous
Removed FormDateFieldInput and FormDateTimeFieldInput stories as they do
not behave the same depending of the execution environment and it would
be easier to put them back after having refactored FormDateFieldInput
and FormDateTimeFieldInput
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
# Complete color refactoring
Closes https://github.com/twentyhq/core-team-issues/issues/1779
- Updated all colors to use Radix colors with P3 color space allowing
for brighter colors
- Created our own gray scale interpolated on Radix's scale to have the
same values for grays as the old ones in the app
- Introduced dark and light colors as well as there transparent versions
- Added many new colors from radix that can be used in the tags or in
the graphs
- Updated multiple color utilities to match new behaviors
- Changed the computation of Avatar colors to return only colors from
the theme (before it was random hsl)
These changes allow the user to use new colors in tags or charts, the
colors are brighter and with better contrast. We have a full range of
color variations from 1 to 12 where before we only had 4 adaptative
colors.
All these changes will allow us to develop custom themes for the user
soon, where users can choose their accent colors, background colors and
there contrast.
Fixes https://github.com/twentyhq/twenty/issues/14766
To reproduce
1. create a view with a any field filter and save it. After, remove any
field filter clicking on the X on the chip. No "Update view" shows.
2. now add another regular filter to the view and save it. Now remove
any field filter clicking on the X on the chip and remove the regular
filter you just added. Now "Update view" shows, click it and refresh the
page. Any field filter still shows
There were two problems
1. UpdateViewButtonGroup is in charge of showing "Update View" button.
But is never mounted if shouldExpandViewBar is falsy.
shouldExpandViewBar was not considering
viewAnyFieldFilterDifferentFromCurrentAnyFieldFilter so even if any
field filter had changed, UpdateViewButtonGroup still wasn't going to
show "Update view" button because it wasn't mounted
2. At save time, we added anyFieldFilterValue to the mutation payload if
```
...(view.anyFieldFilterValue && {
anyFieldFilterValue: view.anyFieldFilterValue,
}),
```,
but in JS an empty string evaluates to falsy.
In the BE we have stored filter operand as IS_EMPTY, IS_NOT_EMPTY, etc.
For some reason in the FE we were manipulating IsEmpty, IsNotEmpty, etc.
(maybe because they were used before in Views before they were moved to
core)
So we were converting the operands in the FE from IS to Is
(convertViewFilterOperandFromCore) to read and manipulate viewFilters,
and then back to BE version to send mutations etc., from Is to IS
(convertViewFilterOperandToCore).
The migration is now over, so we can remove and simplify that code.
(In the 1-5:migrate-views-to-core command we still do the migration from
Is format to IS format.)
Closes https://github.com/twentyhq/core-team-issues/issues/1560
If viewId is defined in a groupBy query, we want to apply all filters of
the view to the query.
This required to move a lot of code from twenty-front to twenty-shared
to convert the filters as stored in the db into graphql filters,
applying the right combinations between filters etc., which was
previously only done in the FE.
This PR does not handle any field filters, it will be done in a later pr
This PR essentially refactors some hard-coded values for width and
height in the table.
It also fixes a few differences from the Figma design, on the checkbox
column width and the top bar and view bar left alignment with the table.
The blue color has been set like before the refactor of the focus portal
(`theme.adaptiveColors.blue4`)
Fixes in https://github.com/twentyhq/core-team-issues/issues/1490 :
- Fix first drag and drop column width that has changed from main (see
Figma)
- Unify all widths, heights, z-index, etc. with constants
- Put same blue as before for focused portal
Also removed `focus-active` class because we now use a portal for the
focus.
This PR brings more UX fixes for table.
Fixes in https://github.com/twentyhq/core-team-issues/issues/1490 :
- Fix any field filter chip formatting
- Empty placeholder should be centered on screenwidth and not scrollable
width on table
- First cell is automatically active on table but we can't move => We
now allow focus position to be null as it is a portal
Fixes https://github.com/twentyhq/twenty/issues/14573
Closes https://github.com/twentyhq/twenty/issues/13854
In this PR
- When evaluating whether there was a soft delete filter enabled, we
were only taking into account the filter that shows all deleted records,
that can be enabled from the side panel ("See deleted records"). Now we
are also taking into account any filter on deletedAt.
- We lacked some places where we should not offer to add a new record if
a soft delete filter is on, ex on the empty page + in the kanban headers
- I decided not to add a constraint on api-side because I think there
could be use cases when importing data where we would want to be able to
create soft deleted records
## Context
View system used to rely on workspaceSchema views and was loaded in what
we called the "prefetch". We have recently migrated views to
core.coreViews and the states should now be named after coreViews
Fixes: https://github.com/twentyhq/twenty/issues/14349
The goal of this PR is to test if Twenty can support a large number of
members, a question which was raised by a large company that is
considering moving away from Salesforce.
I was expecting the currentWorkspaceMembersState to cause a lot more
issue. It would be very hard to get rid of it in the context of actors,
I think that would require a big refactoring. I thought we'd have to do
it but it turns out the perf are pretty good. One thing we need to
improve is the pagination on the roles page, we'll wait for @Bonapara to
update that
resolves#14190
added refreshCoreViews() call after object creation to immediately
update core views state, ensuring new objects appear in the navigation
drawer without requiring a refresh
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: prastoin <paul@twenty.com>
Let's move forward and get rid of the past... !
Removing prefetchViewState and the prefetch view query.
Next steps:
- renaming Selectors to remove prefetch keywork
- remove old view type and coreViewToView util
- rework optimistic rendering for coreViews
This PR removes board field definitions usage in favor of record fields,
like we've already done on table with table columns.
This PR also introduces a first step in the refactoring of the
RecordIndexContext as a new way to store in-memory a normalized cache
for some states used pretty much everywhere like field metadata items or
field definition (for now but it will soon be removed).
Since most of those states do not change if we don't modify the
metadata, then we can safely store them in a context, which has the
fastest access time in React.
Co-authored-by: Charles Bochet <charles@twenty.com>
On field selection, we need to retrieve the fieldMetadata to set the
filter type. Current code was using the current step filter
fieldMetdataId instead of the new one.
Also adding a special behaviour for Select filter: it behaves as
arrays/multiselect but the opearands are not the same.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR introduces the usage of record fields in the manipulation of
table columns and board fields.
Since all of the actual system relies on states like tableColumns,
recordIndexFieldDefinitions and the likes, it was required to implement
temporary utils that modifies those states in parallel of the new
generic currentRecordFields, to keep a working product.
With this PR though, currentRecordFields becomes the single source of
truth that gets saved to DB, and the remaining work is just to make the
switch with this new state on all components that are plugged to
tableColumns and the like.
This will be done in another PR.
This PR prepares and simplify option dropdown for using record fields.
I had a problem while trying to use currentRecordFields directly with
ordering so I stayed with the actual column definition system.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR adds a new RecordField abstraction to mimick the behavior of
RecordFilter, RecordSort and other similar concepts that allow to have
something that is not related directly to views.
It is closely following the same pattern with an Effect component to
initialize it from views and CRUD hooks to modify it.
Although for this concept we save to view fields automatically for every
modification.
This PR does not implement saving to view fields, it is just a parallel
code path that is meant to be built to see if everything behaves the
same then we'll remove the old code path at the end by saving record
fields instead of the many states for columns, fields definitions, etc.
This PR prepares the refactor of record field definition and column
definition.
We need to separate the two concepts of FieldInput and FieldContext at
the cell level, and the new RecordField concept that will replace
FieldDefinition at the metadata and state level.
So we create a new ui sub-folder in the already existing record-field
folder, in order to separate those two concepts that are still very
close.
This PR removes useRecordTable barrel-hook and instead use the pattern
that enforces one hook per function.
This allows to have a easier to maintain React code with small dedicated
hooks.
This PR also improves the usage of those hooks by cleaning some logic
that calls them.
We introduce a new hook : `useRecordIndexIdFromCurrentContextStore` that
factorizes logic that was duplicated to retrieve the `recordTableId`.
Introduced `useHandleColumnsChange`, that saves table columns into view,
this could be discussed but is it isolated with a TODO comment so ok for
now.
Also lowered the coverage because not enough to cover with test here.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR removes any V2 naming in state management logic and some minor
utils and hooks.
It has a lot of changes but nearly all of them were made by the rename
functionality of vscode which is deterministic, so it shouldn't
introduce any regression.
QA has been made on this PR on the main features of the app without any
noticeable issue.
Also renamed some other v2 naming related items :
- TextInputV2 => TextInput
- TextInput => SettingsTextInput
- ObjectFilterDropdownFilterSelectMenuItemV2 =>
ObjectFilterDropdownFilterSelectMenuItem
- useInitDraftValueV2 => useInitDraftValue
- useOpenRecordTableCellV2 => useOpenRecordTableCell
In our Graphql schema, ids are typed as UUID.
Our FE codegen is typing it as 'any', unless we say otherwise. Fixing it
and fixing the impacts in the codebase
Fixes#13250
This PR fixes the breadcrumb display issue where API names were shown
instead of friendly object names when navigating to related objects in
the detail view.
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
This PR removes every legacy state management logic left in the code
base, we only keep our last component state management logic.
Removed :
- Scoped states logic
- Dropdown scope logic
- Scope id naming
- Component state v1
- Component state v2_alpha
# QA
| Component | Comments |
| --- | --- |
| AttachementDropdown | Ok |
| ObjectTasks | Ok |
| ActivityRichTextEditor | Ok |
| App | Ok dialogs work |
| AppRouterProviders | Ok dialogs work |
| RecordBoardScrollToFocusedCardEffect | Ok |
| RecordBoard | Ok |
| RecordBoardCard | OK |
| useSelectAllCards | Ok |
| RecordInlineCell | Ok |
| useInlineCell | Ok |
| RecordDetailRelationRecordsListItem | Ok |
| RecordDetailRelationSectionDropdownToMany | Ok |
| RecordDetailRelationSectionDropdownToOne | Ok |
| useHandleContainerMouseEnter | Ok |
| useResetTableRowSelection | Ok |
| useSelectAllRows | Ok |
| useSetRecordTableData | Ok |
| useRecordTable | Ok |
| useRecordTableMoveFocusedCell | Ok |
| useRecordTableMoveFocusedRow | Ok |
| useMoveHoverToCurrentCell | OK |
| useTriggerActionMenuDropdown | Ok |
| RecordTableBodyDragDropContextProvider | Ok |
| RecordTableHeaderCell | Ok |
| useSetCurrentRowSelected | Ok |
| useRecordTitleCell | Ok |
| SettingsObjectFieldActiveActionDropdown | Ok |
| SettingsObjectFieldInactiveActionDropdown | Ok |
| SettingsObjectFieldItemTableRow | Ok |
| SettingsObjectInactiveMenuDropDown | Ok |
| SettingsRoleEditEffect | Ok |
| SettingsRolesQueryEffect | Ok |
| Dropdown | Ok |
| useSelectableList | Ok |
| useSelectableListHotKeys | Ok |
| useSelectableListListenToEnterHotkeyOnItem | Ok |
| useNavigationSection | Ok |
| useCreateViewFromCurrentState | Ok |
| useDeleteViewFromCurrentState | Ok |
| useUpdateViewFromCurrentState | Ok |
| ViewPickerFavoriteFoldersDropdown | Ok |
| useCreateViewFromCurrentView | Ok |
| ViewBarDetails | Ok |
| WorkflowRunVisualizer | Ok |
| useHandleWorkflowRunDiagramCanvasInit | Ok |
| WorkflowDiagramCanvasBase | Ok |
| WorkflowRunVisualizerEffect | Ok |
| useRemoveStepFilter | Ok |
| useRemoveStepFilterGroup | Ok |
| useUpsertStepFilterSettings | Ok |
| WorkflowFindRecordsFilters | Ok |
| SettingsObjects | Ok |
This PR adds the ability to save an any field filter to a view.
It adds a new `anyFieldFilterValue` on both core view and workspace view
entities.
It also introduces the necessary utils that mimic the logic that manages
the save of record filters and record sorts on views.
This PR adds any field filter request generation utils with its unit
test.
It also calls this new util in the relevant requests for table and
board.
This PR also adds a new corresponding state in context store so that the
filter is handled in command menu and actions.
We also add this new filter to the aggregate queries.
The RecordShowPage story was also fixed.
This PR adds the frontend logic to handle the user input of a search any
field value.
It also adds the associated feature flag, that can be modified from the
admin panel.
This PR does not add the filtering part nor the saving on view logic,
which will come in their separate PRs.
https://github.com/user-attachments/assets/6a52c090-b957-46aa-bff7-a90b51109789
This PR improves dropdown menu headers for filter and sort dropdown in
view bar and editable filter chips.
It adds what's necessary to navigate back or close the dropdown, so that
we don't rely solely on click outside to exit the dropdown.
This PR also refactors the components so that we clearly identify the
two code paths that can use filter dropdowns : view bar and filter chip,
everything that can be DRY stays in the object-filter-dropdown module
but we try to have our wrapping components in each distinct module
instead of blending everything with ternaries inside
object-filter-dropdown module.
The vector search input value wasn't correctly handled across the
different dropdowns, due to a wrong component instance management, since
the dropdown menu header improvement put this into light, I also
refactored the state management of the vector search input.
@Bonapara please check the QA video and tell me if it's ok, I didn't add
dropdown menu header on the advanced filter field list dropdown because
it's a select more than a standalone dropdown, what do you think ?
QA :
https://github.com/user-attachments/assets/17080f32-f302-436c-937b-3577715b7e84
QA Vector search fix :
https://github.com/user-attachments/assets/6367bbf6-8a98-4b53-86cf-6ba92be130eb
Fixes https://github.com/twentyhq/core-team-issues/issues/640
Fixes https://github.com/twentyhq/core-team-issues/issues/1206
This PR removes useDropdown barrel hook and refactors the legacy
useDropdown states to the last version of our recoil component state
management.
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This PR replaces the many calls of useDropdown by the new standalone
hooks : useCloseDropdown, useOpenDropdown and useToggleDropdown.
This will allow to remove useDropdown and then the dropdown recoil
component state v1.
A big round of QA has been made, with some bugs caught along the way.
Closes https://github.com/twentyhq/core-team-issues/issues/1155
Closes https://github.com/twentyhq/core-team-issues/issues/618
## QA
Component|Status|Comment
|---|---|---|
CurrentWorkspaceMemberFavorites|Ok|
FavoriteFolderPickerFooter|Ok|
AdvancedFilterAddFilterRuleSelect|Ok|
AdvancedFilterRecordFilterGroupOptionsDropdown|Ok|
AdvancedFilterRecordFilterOperandSelectContent|Ok|
AdvancedFilterRecordFilterOptionsDropdown|Ok|
useAdvancedFilterFieldSelectDropdown|Ok|
ObjectFilterDropdownBooleanSelect|Ok|
ObjectFilterDropdownOptionSelect|Ok|
ObjectOptionsDropdown|Ok|
ObjectOptionsDropdownLayoutContent|Ok|
ObjectSortDropdownButton|Ok|
useCloseSortDropdown|Ok|
FormDateTimeFieldInput|Ok|Bug detected, cannot select a month or a year,
see issue https://github.com/twentyhq/twenty/issues/12922
FormSingleRecordPicker|Ok|
MultiItemFieldMenuItem|Ok|
RecordDetailRelationRecordsListItem|Ok|
RecordDetailRelationSection|Ok|
RecordDetailRelationSectionDropdownToMany|Ok|
RecordDetailRelationSectionDropdownToOne|Ok|
RecordTableColumnAggregateFooterDropdownSubmenuContent|Ok|
RecordTableColumnAggregateFooterAggregateOperationMenuItems|Ok|
RecordTableColumnAggregateFooterMenuContent|Ok|
RecordTableColumnAggregateFooterValueCell|Ok|
RecordTableColumnHeadDropdownMenu|Ok|
RecordTableHeaderPlusButtonContent|Ok|
useTriggerActionMenuDropdown|Ok|
MultipleSelectDropdown|Ok|
RecordBoardColumnHeaderAggregateDropdownButton|Ok|
SettingsDataModelFieldSelectFormOptionRow|Ok|
SettingsDataModelNewFieldBreadcrumbDropDown|Ok|
SettingsObjectFieldActiveActionDropdown|Ok|
SettingsObjectFieldInactiveActionDropdown|Ok|
SettingsObjectInactiveMenuDropDown|Ok|
SettingsSecurityApprovedAccessDomainRowDropdownMenu|Couldn’t test|
SettingsSecuritySSORowDropdownMenu|Couldn’t test|
SettingsAccountsRowDropdownMenu|Ok|
SettingsRoleAssignment|Ok|
SettingsServerlessFunctionTabEnvironmentVariableTableRow|Couldn’t test|
MatchColumnToFieldSelect|Ok|
SubMatchingSelectDropdownButton|Ok|Removed conflicting duplicate open
dropdown
SubMatchingSelectRowRightDropdown|Ok|
CurrencyPickerDropdownButton|Ok|
IconPicker|Ok|
DateTimePicker|Ok|
PhoneCountryPickerDropdownButton|OK|
Select|Ok|
Dropdown|Ok|Not QAing all dropdowns in the app because the ones of this
QA are enough to show up that Dropdown is behaving correctly on a lot of
use cases
DropdownMenuInnerSelect|Ok|
TabList|Ok|Removed onClickOutside called in dropdown clickable
component, validated with Raph who recently worked on this
DateInput|Ok|
MultiWorkspaceDropdownDefaultComponents|Ok|
AdvancedFilterChip|Ok|
EditableFilterDropdownButton|Ok|
UpdateViewButtonGroup|Ok|
ViewBarDetailsAddFilterButton|Ok|
ViewBarFilterButton|Ok|
ViewBarFilterDropdown|Ok|
ViewBarFilterDropdownAdvancedFilterButton|Ok|
ViewPickerDropdown|Ok|
ViewPickerListContent|Ok|
ViewPickerOptionDropdown|Ok|
WorkflowEditTriggerDatabaseEventForm|Ok|
WorkflowVariablesDropdownWorkflowStepItems|Ok|
AttachmentDropdown|Ok|
SupportDropdown|Ok|
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR fixes a bug that occurs during filter operand changes.
As a date filter can contain values that have a different shape, mainly
date ISO string and hard-coded relative dates, changing the operand
without resetting the value to its default was causing a crash.
This PR also extracts the logic that computes the right part of a filter
chip into a util instead of a difficult to understand ternary, thus
solving small bugs in the value displayed also.
Fixes https://github.com/twentyhq/twenty/issues/12778
This PR is the first part of a refactoring aiming to deprecate the
hotkey scopes api in favor of the new focus stack api which is more
robust.
The refactored components in this PR are the dropdowns and the side
panel/command menu.
- Replaced `useScopedHotkeys` by `useHotkeysOnFocusedElement` for all
dropdown components, selectable lists and the command menu
- Introduced `focusId` for all dropdowns and created a common hotkey
scope `DropdownHotkeyScope` for backward compatibility
- Replaced `setHotkeyScopeAndMemorizePreviousScope` occurrences with
`usePushFocusItemToFocusStack` and `goBackToPreviousHotkeyScope` with
`removeFocusItemFromFocusStack`
Note: Test that the shorcuts and arrow key navigation still work
properly when interacting with dropdowns and the command menu.
Bugs that I have spotted during the QA but which are already present on
main:
- Icon picker select with arrow keys doesn’t work inside dropdowns
- Some dropdowns are not selectable with arrow keys (no selectable list)
- Dropdowns in dropdowns don’t reset the hotkey scope correctly when
closing
- The table click outside is not triggered after closing a table cell
and clicking outside of the table
Opening a date picker when creating a filter now directly applies
today's date to avoid any in-between state issues.
This allows the date picker and the operand selection to behave nicely
when creating a date filter.
Fixes https://github.com/twentyhq/core-team-issues/issues/1049
This PR refactors the `DropdownMenuItemsContainer` component and
simplifies its inner parts, which have been modified over months for
different needs without taking the time to have a global approach.
It should however be noted that due to the recent refactor of the
`DropdownContent`, it is now much easier to refactor
`DropdownMenuItemsContainer`, mainly because of the width management
being nicely handled by `DropdownContent` now.
Fixes https://github.com/twentyhq/twenty/issues/11766
# Changes
The `width` props of `DropdownMenuItemsContainer` and its usage in
calling components have been removed.
The multiple ternaries inside `DropdownMenuItemsContainer` have been
reduced to one ternary on `scrollable` props.
The `ScrollWrapper` usage has been removed from
`DropdownMenuItemsContainer`, because the only thing we need is to have
a simple `overflow-y: scroll;` CSS property.
Why ? Because it was previously relevant to have a `ScrollWrapper`, when
we were using an external library, but now that `ScrollWrapper` is a
simple `div` with overflowing, which only benefit is to expose a hook to
imperatively toggle this overflowing behavior from outside (mainly
useful for table fixed row and column), and that we don’t need this for
`DropdownMenuItemsContainer`, then it follows that we just need a simple
overflowing `div` container, which simplifies everything and boils down
our `DropdownMenuItemsContainer` to a straightforward and standard CSS
stack.
We remove the temporary `scrollWrapperHeightAuto` props that was used to
fix a bug in a previous PR, we also rollback `ScrollWrapper` to its
previous state with `width: 100%` and `height: 100%` and removed
`heightAuto` props.
The `hasMaxHeight` props is kept, but the `168` pixels value is
extracted in a constant.
# QA
Component | Comment
-- | --
CommandMenuActionDropdown | Reported bug
https://github.com/twentyhq/twenty/issues/12541
RecordIndexActionMenuDropdown |
AttachmentDropdown | Cannot test because cannot add a file (currently
broken, maybe because of permissions ?)
CommandMenuContextChipGroups |
FavoriteFolderNavigationDrawerItemDropdown |
FavoriteFolderPicker |
FavoriteFolderPickerFooter |
AdvancedFilterAddFilterRuleSelect |
AdvancedFilterFieldSelectMenu |
AdvancedFilterRecordFilterGroupOptionsDropdown |
AdvancedFilterRecordFilterOperandSelect |
AdvancedFilterRecordFilterOptionsDropdown |
AdvancedFilterSubFieldSelectMenu |
ObjectFilterDropdownBooleanSelect |
ObjectFilterDropdownCountrySelect |
ObjectFilterDropdownCurrencySelect |
ObjectFilterDropdownNumberInput |
ObjectFilterDropdownOptionSelect | Fixed “No result” case
ObjectFilterDropdownRecordRemoveFilterMenuItem | Removed because unused
ObjectFilterDropdownTextInput |
ObjectOptionsDropdownFieldsContent | Spotted bug with icon eye
https://github.com/twentyhq/twenty/issues/12545
ObjectOptionsDropdownHiddenFieldsContent | Spotted bug with icon eye
https://github.com/twentyhq/twenty/issues/12545
ObjectOptionsDropdownLayoutContent | Refactored
DropdownMenuItemsContainer usage with DropdownMenuSeparator, spotted bug
switch view type https://github.com/twentyhq/twenty/issues/12546
ObjectOptionsDropdownMenuContent | Refactored DropdownMenuItemsContainer
usage with DropdownMenuSeparator
ObjectOptionsDropdownLayoutOpenInContent |
ObjectOptionsDropdownMenuViewName |
ObjectOptionsDropdownRecordGroupFieldsContent |
ObjectOptionsDropdownRecordGroupSortContent |
ObjectSortDropdownButton |
RecordBoardColumnDropdownMenu |
RecordBoardColumnDropdownMenu |
RecordBoardColumnHeaderAggregateDropdownFieldsContent |
RecordBoardColumnHeaderAggregateDropdownMenuContent |
RecordBoardColumnHeaderAggregateDropdownOptionsContent |
MultiItemFieldInput | Added hasMaxHeight on list of items
MultiItemFieldMenuItem |
RecordGroupsVisibilityDropdownSection |
MultipleRecordPicker |
MultipleRecordPickerMenuItems |
SingleRecordPickerMenuItems |
SingleRecordPickerMenuItemsWithSearch |
RecordDetailRelationRecordsListItem |
RecordTableColumnAggregateFooterDropdownSubmenuContent |
RecordTableColumnAggregateFooterMenuContent |
RecordTableHeaderPlusButtonContent |
RecordTableHeaderPlusButtonContent |
MultipleSelectDropdown |
SettingsAccountsRowDropdownMenu |
ConfigVariableDatabaseInput |
ConfigVariableOptionsDropdownContent |
SettingsDataModelNewFieldBreadcrumbDropDown |
SettingsDataModelFieldSelectFormOptionRow |
SettingsObjectFieldActiveActionDropdown |
SettingsObjectFieldInactiveActionDropdown |
SettingsObjectInactiveMenuDropDown |
SettingsIntegrationDatabaseConnectionSummaryCard |
SettingsRoleAssignmentWorkspaceMemberPickerDropdown |
SettingsRolePermissionsObjectLevelObjectPickerDropdownContent | Cannot
test
SettingsSecurityApprovedAccessDomainRowDropdownMenu | Cannot test
SettingsSecuritySSORowDropdownMenu | Cannot test
SettingsServerlessFunctionTabEnvironmentVariableTableRow | Cannot test
MatchColumnSelectFieldSelectDropdownContent |
MatchColumnSelectSubFieldSelectDropdownContent |
SubMatchingSelectInput |
SupportDropdown |
IconPicker |
Select |
SelectInput |
CurrencyPickerDropdownSelect |
PhoneCountryPickerDropdownSelect |
CustomSlashMenu |
TabListDropdown | Cannot test
MultiWorkspaceDropdownDefaultComponents | Removed unnecessary
StyledDropdownMenuItemsContainer
MultiWorkspaceDropdownThemesComponents |
MultiWorkspaceDropdownWorkspacesListComponents |
UpdateViewButtonGroup |
ViewBarFilterDropdownFieldSelectMenu |
ViewFieldsVisibilityDropdownSection |
ViewPickerContentCreateMode |
ViewPickerContentEditMode |
ViewPickerListContent | Add hasMaxHeight to limit the height of view
list
ViewPickerOptionDropdown |
WorkflowEditTriggerDatabaseEventForm |
WorkflowVariablesDropdownFieldItems |
WorkflowVariablesDropdownObjectItems |
WorkflowVariablesDropdownWorkflowStepItems |
<!-- notionvc: a3a87101-9944-4b03-a29d-b2974d5ffa9d -->
---------
Co-authored-by: Charles Bochet <charles@twenty.com>