Adding a cursor rule for nx
## Changes
- Added `.cursor/rules/nx-rules.mdc`
- The rule provides structured guidelines for AI assistants to better
help developers
For fresh install, we need the migrations to happen before the upgrade
command is triggered as the upgrade command is a NestJS command and the
app will try to load env variables from db
Introducing a class of RelationException extending CustomException to
help grouping those exception in sentries by ExceptionCode.
I did not introduce a filter as these are thrown in utils that can be
used in multiple places now or in the future, and filters are to be
added at resolver-level.
# Introduce focus stack to handle hotkeys
This PR introduces a focus stack to track the order in which the
elements are focused:
- Each focused element has a unique focus id
- When an element is focused, it is pushed on top of the stack
- When an element loses focus, we remove it from the stack
This focus stack is then used to determine which hotkeys are available.
The previous implementation lead to many regressions because of race
conditions, of wrong order of open and close operations and by
overwriting previous states. This implementation should be way more
robust than the previous one.
The new api can be incrementally implemented since it preserves
backwards compatibility by writing to the old hotkey scopes states.
For now, it has been implemented on the modal components.
To test this PR, verify that the shortcuts still work correctly,
especially for the modal components.
Chrome doesn't really respect preloading and was loading it before other
important assets, slowing down the app while in 99% of sessions people
don't check the REST API playground
We have approvedAccessDomain custom exceptions, but they were never
filtered while some of them reflects 4xx errors which we don't want to
be captured as 5xx errors
follow up #12033
in #12033, SettingsDataModelFieldRelationForm I changed the the use of
objectMetadataItems to activeObjectMetadataItems, which filtered out
system objects. The naming was one factor for this confusion
Renaming it everywhere to specify that they don't include system objects
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This pull request introduces changes to improve handling of nullable
values in link-related data structures and simplifies field value
generation logic. Key updates include adjustments to type definitions,
utility functions, and component logic to support `null` values for
links, along with the removal of the `generateDefaultFieldValue`
function in favor of `generateEmptyFieldValue`.
There will be a few more follow-up Pull Requests.
---
Closes https://github.com/twentyhq/twenty/issues/11844
### Solution
> After discussion with charles & weiko, we chose the long term
solution.
>
> Fix FE to request checkUserExists resolver with lowercased emails
> Add a decorator on User (and AppToken for invitation), to lowercase
email at user (appToken) creation. ⚠️ It works for TypeOrm .save method
only (there is no user email update in codebase, but in future it
could..)
> Add email lowercasing logic in external auth controller
> Fix FE to request sendInvitations resolver with lowercased emails
> Add migration command to lowercase all existing user emails and
invitation emails
> For other BE resolvers, we let them permissive. For example, if you
made a request on CheckUserExists resolver with uppercased email, you
will not found any user. We will not transform input before checking for
existence.
[link to comment
](https://github.com/twentyhq/twenty/pull/12130#discussion_r2098062093)
### Test 🚧
- sign-in and up from main subdomain and workspace sub domain > Google
Auth (lowercased email) ✔️ | Microsoft Auth (uppercased email ✔️ &
lowercased email) | LoginPassword (uppercased email ✔️& lowercased
email✔️)
- invite flow with uppercased and lowercased ✔️
- migration command + sign-in ( former uppercased microsoft email ✔️) /
sign-up ( former uppercased invited email ✔️)
closes https://github.com/twentyhq/private-issues/issues/278, closes
https://github.com/twentyhq/private-issues/issues/275, closes
https://github.com/twentyhq/private-issues/issues/279
Catching "no licence - removed" microsoft message channels.
Current behabiour
> ` MessageImportException [Error]: The mailbox is either inactive,
soft-deleted, or is hosted on-premise.`
Goal:
better track errors VS user mistakes
Context:
A similar logic was already implemented for the calendar channels. I
just replicated it to message channels
This PR adds back and fixes the story for RelationFromManyFieldDisplay,
which was broken due to the removal of use-context-selector state.
The story was also setting unnecessary states, we now only keep one
state set in the recoil state.
This PR removes use-context-selector completely, so that any bug
associated with state synchronization between recoil and
use-context-selector disappears.
There might be a slight performance decrease on the table, but since we
have already improved the average performance per line by a lot, and
that the performance bottleneck right now is the fetch more logic and
the windowing solution we use, it is not relevant.
Also the DX has become so hindered by this parallel state logic recently
(think [cache
invalidation](https://martinfowler.com/bliki/TwoHardThings.html)), that
the main benefit we gain from this removal is the DX improvement.
Fixes https://github.com/twentyhq/twenty/issues/12123
Fixes https://github.com/twentyhq/twenty/issues/12109
Fixes https://github.com/twentyhq/twenty/issues/12131
All instances of RecordDetailRelationRecordsListItem are sharing the
same DELETE_RELATION_MODAL_ID, this PR makes the modal ID unique for
each item.
Regarding issue #10941:
Previously, when resizing a column relative to the record's name, the
content did not properly adjust to the selected width. This issue
occurred because the parent element (the link) was not a flex container,
preventing the child elements from resizing accordingly.
This fix makes the Chip link inline-flex to allow proper content
adjustment.
Additionally, the Chip itself is now set to width: 100% so that it fully
adapts to its parent.
A small margin of 2 * theme.spacing(1) has also been added to improve
spacing.
Files changed:
packages/twenty-ui/src/components/chip/Chip.tsx
packages/twenty-ui/src/components/chip/LinkChip.tsx
**Video:**
https://github.com/user-attachments/assets/83832c25-0b70-490f-90ed-0d391addf6f8
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
**Problem:**
The previous `docker-compose.yml` included a `change-vol-ownership`
service. This service was designed to run once upon startup to `chown`
the `server-local-data` and `docker-data` volumes to user/group
`1000:1000`. This was necessary because:
1. The main `server` and `worker` containers run as the non-root user
`1000` for security.
2. Docker typically creates/mounts named volumes initially owned by
`root`.
3. The application needs write access to these volumes.
However, this run-once service pattern causes problems in certain
deployment environments (like Coolify) that don't gracefully handle
services designed to exit after completing their task. This can lead to
deployment failures or warnings.
**Solution:**
This PR refactors the Docker setup to address the volume permission
issue directly within the Docker image build process, eliminating the
need for the run-once service.
**Changes:**
1. **`packages/twenty-docker/docker-compose.yml`:**
* Removed the `change-vol-ownership` service definition entirely.
* Removed the `depends_on: change-vol-ownership` condition from the
`server` service definition.
* **Proposed Change:** Removed the `${STORAGE_LOCAL_PATH}` environment
variable from the `server-local-data` volume mounts for both `server`
and `worker` services. The path is now hardcoded to
`/app/packages/twenty-server/.local-storage`. (See Reasoning below).
2. **`packages/twenty-docker/twenty/Dockerfile`:**
* In the final stage, *before* the `USER 1000` command, added lines to:
* Create the necessary directories: `RUN mkdir -p
/app/packages/twenty-server/.local-storage /app/docker-data` (and also
`/app/.local-storage` for safety, though it's likely unused by volumes).
* Set the correct ownership: `RUN chown -R 1000:1000 /app/.local-storage
/app/packages/twenty-server/.local-storage /app/docker-data`.
3. **`packages/twenty-docker/twenty/entrypoint.sh`:**
* Added a check near the beginning of the script for the presence of the
now-potentially-unused `STORAGE_LOCAL_PATH` environment variable.
* If the variable is set, a warning message is printed to standard
output, informing the user that the variable might be deprecated and
ignored if the hardcoded path change in `docker-compose.yml` is
accepted.
**Reasoning:**
By creating the target directories
(`/app/packages/twenty-server/.local-storage` and `/app/docker-data`)
within the Docker image *and* setting their ownership to `1000:1000`
during the build (while still running as root), we leverage Docker's
volume initialization behavior. When a named volume is mounted to a
non-empty directory in the container image, Docker copies the content
and ownership from the image directory into the volume. This ensures
that when the `server` and `worker` containers start (running as user
`1000`), the volumes they mount already have the correct permissions,
eliminating the need for the separate `change-vol-ownership` service.
**Regarding `STORAGE_LOCAL_PATH`:**
The `docker-compose.yml` previously allowed configuring the path for
local storage via the `STORAGE_LOCAL_PATH` variable, defaulting to
`.local-storage`. Since the Dockerfile now explicitly creates and sets
permissions for `/app/packages/twenty-server/.local-storage`,
maintaining this configuration might be unnecessary or could potentially
lead to permission errors if a user sets it to a path *not* prepared in
the Dockerfile.
This PR proposes hardcoding the path in `docker-compose.yml` to
`/app/packages/twenty-server/.local-storage` to align with the
Dockerfile changes and simplify configuration. Is this acceptable, or is
there a specific use case for retaining the `STORAGE_LOCAL_PATH`
variable that needs to be considered? If retained, the Dockerfile would
need further changes to dynamically handle permissions based on this
variable.
**Impact:**
* Improves compatibility with deployment platforms that struggle with
run-once containers.
* Simplifies the `docker-compose.yml` setup (potentially, pending
discussion on `STORAGE_LOCAL_PATH`).
* Fixes volume permissions at the source (image build) rather than
relying on a runtime fix.
* Adds a warning for users who might have the potentially deprecated
variable set.
**Testing:**
The changes have been tested locally using `docker compose up`. The
services start correctly, the application is accessible, and the warning
message for the potentially deprecated variable appears as expected when
the variable is set.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
## Background
I'm trying to self-host twenty using [official 1-Click w/ Docker Compose
guide](https://twenty.com/developers/section/self-hosting/docker-compose)
on AWS EC2 (using `t3.small` instance type).
## What happened
I used the one-line script but it failed with
```
[skipped]
server-1 | Successfuly migrated DB!
dependency failed to start: container twenty-server-1 is unhealthy
```
Then I tried manual steps, and it failed again with same issue as above.
No configuration changed, everything default used.
I re-run manual steps multiple times with `docker compose down -v` and
`docker compose up`, everything time it failed with `server` unhealthy
as it seems server takes longer than configured health check duration
(which is 50 seconds)
Here's the `time` summary running it:
```
[skipped]
server-1 | Successfuly migrated DB!
dependency failed to start: container twenty-server-1 is unhealthy
________________________________________________________
Executed in 58.26 secs fish external
usr time 661.43 millis 362.00 micros 661.07 millis
sys time 646.10 millis 212.00 micros 645.89 millis
root@ip-10-0-10-43 ~/twenty [1]#
```
## Why it happend
My hunch (new to twenty, just used it yesterday) is that server service
takes much longer to become healthy with DB migration and etc, that
configured health check retries is not sufficient.
## What solution worked
Increased the retry for server service from 10 to 20, and it worked and
service came up healthy (everytime). The increase wasn't needed for db
or worker service, just server service.
If this all makes sense, please feel free to merge this, so it'll be
smoother experience for others new to self-hosting twenty.
fixes#11900
changes desc:
1.moved confirmation model adjacent to dropdown component instead inside
it.
2.passing variables of useRecordGroupReorderConfirmationModal from
dropdown root component so confirmations model should remount get
updated with new state
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR fixes the problem of full table re-render on any update or
keyboard navigation.
This was due to a recoil state subscribe in the RecordTable component, I
just moved it in the children effect components so that the Flux
dependency becomes inoffensive.
I also extracted one hook from the useRecordTable hook that we have to
refactor gradually.
Fixes https://github.com/twentyhq/core-team-issues/issues/979
Fixes https://github.com/twentyhq/core-team-issues/issues/932
In this PR:
- deprecating listenClickOutside ComparePixel mode as this is not
accurate. We were using to avoid portal issue with CompareHtmlRef mode
but this is still an issue when portalled content overflows the
container.
- add ClickOutsideContext to specify excluded className so portal
children can use it easily (part of the tooling)
- fix stories
- remove avoidPortal from dropdown as this was not used