Adding support for all Composite Fields while using the "import"
functionality. This includes:
- Currency
- Address
Edit :
- Refactored a lot of types in the spreadsheet import module
- Renamed a lot of functions, hooks and types that were not
self-explanatory enough
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This PR refactors the ORM-Manager to simplify and unify the datasource
creation. I'm deprecating all usages if InjectWorkspaceDatasource and
InjectWorkspaceRepository as we can't be sure they are up-to-date
## Bug Description
We are facing a bug in case recaptcha is enabled.
To reproduce:
- Create your recaptcha: https://www.google.com/recaptcha/about/
- update your server .env with the following variables:
```
CAPTCHA_SECRET_KEY=REPLACE_ME
CAPTCHA_SITE_KEY=REPLACE_ME
CAPTCHA_DRIVER=google-recaptcha
```
- Go to the login page, enter an existing user email and hit 'Reset your
password'.
- Add a console.log in emailPasswordResetLink in auth.resolver.ts to get
the token that would be sent by email if you don't have the mailer setup
- Browse: /reset-password/{passwordToken}
- Update the password:
<img width="1446" alt="image"
src="https://github.com/user-attachments/assets/dd5b077f-293e-451a-8630-22d24ac66c42">
- See that the token is invalid
You should see two calls in your developer network tab. A successful one
to update the password and another to log you in. This 2nd call
(Challenge) does not have the captcha token provided. It should be
## Fix
- Refreshing the token on page load
- providing it to the Challenge graphql call
This PR fixes a few bugs on TwentyORM:
- fix many to one relations that were not properly queries
- fix many to one relations that were not properly parsed
- compute datasource (or use from cache) at run-time and do not use
injected one that could be outdated
We still need to refactor it to simplify, I feel the API are too complex
and we have too many cache layers. Also the relation computation part is
very complex and bug prone
This pull request addresses an issue where the delete button, placed
closely to the export button, could lead to accidental deletions without
confirmation. The problem was that the confirmation modal for the delete
action, being the second action, was not displayed. The code was updated
to map all action bar entries and display their respective confirmation
modals correctly.
### Changes Made:
- Updated the rendering of action bar entries to ensure all confirmation
modals are shown.
Changed from displaying only the first action bar entry's confirmation
modal to mapping through all entries and displaying each modal if it
exists.
- Removed the delete from the onClick because it is present in the
onConfirmClick.
### Description
Resolves Issue: [https://github.com/twentyhq/twenty/issues/6224](#6224)
### Additional Consideration
I have changed the default start rating value from 1 star to no star
since clicking on the selected start was reverting the filed to 1 star
which didn't seem like the appropriate behaviour. Let me know if this
change is fine
---------
Co-authored-by: JosephAshwin23 <joseph.sanjivi@anywhere.co>
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
- Removed enabled props from useScopedHotkeys becayse it doesn't work
- Moved enter useScopedHotkeys in a handler that we drill down to the
text inputs on the settings form
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
### Description
This PR introduces a custom ESLint rule named
`inject-workspace-repository`. The purpose of this rule is to enforce
naming conventions for files and classes that use the
`@InjectWorkspaceRepository` decorator or include services ending with
`WorkspaceService` in their constructors.
### Rule Overview
The new ESLint rule checks for the following conditions:
1. **File Naming**:
- Only file ending with `.service.ts` or `.workspace-service.ts` are
checked.
- If a file contains a class using the `@InjectWorkspaceRepository`
decorator or a service ending with `WorkspaceService` in the
constructor, the file name must end with `.workspace-service.ts`.
2. **Class Naming**:
- Classes that use the `@InjectWorkspaceRepository` decorator or include
services ending with `WorkspaceService` in their constructors must have
names that end with `WorkspaceService`.
### How It Works
The rule inspects each TypeScript file to ensure that the naming
conventions are adhered to. It specifically looks for:
- Constructor parameters with the `@InjectWorkspaceRepository`
decorator.
- Constructor parameters with a type annotation ending with
`WorkspaceService`.
When such parameters are found, it checks the class name and the file
name to ensure they conform to the expected patterns.
### Example Code
#### Valid Cases
1. **Correct File and Class Name with Decorator**:
```typescript
// Filename: my.workspace-service.ts
class MyWorkspaceService {
constructor(@InjectWorkspaceRepository() private repository) {}
}
```
2. **Service Dependency**:
```typescript
// Filename: another.workspace-service.ts
class AnotherWorkspaceService {
constructor(private myWorkspaceService: MyWorkspaceService) {}
}
```
#### Invalid Cases
1. **Incorrect Class Name**:
```typescript
// Filename: my.workspace-service.ts
class MyService {
constructor(@InjectWorkspaceRepository() private repository) {}
}
// Error: Class name should end with 'WorkspaceService'.
```
2. **Incorrect File Name**:
```typescript
// Filename: my.service.ts
class MyWorkspaceService {
constructor(@InjectWorkspaceRepository() private repository) {}
}
// Error: File name should end with '.workspace-service.ts'.
```
3. **Incorrect File and Class Name**:
```typescript
// Filename: my.service.ts
class MyService {
constructor(@InjectWorkspaceRepository() private repository) {}
}
// Error: Class name should end with 'WorkspaceService'.
// Error: File name should end with '.workspace-service.ts'.
```
4. **Incorrect File Type**:
```typescript
// Filename: another.service.ts
class AnotherService {
constructor(private myWorkspaceService: MyWorkspaceService) {}
}
// Error: Class name should end with 'WorkspaceService'.
// Error: File name should end with '.workspace-service.ts'.
```
5. **Incorrect Class Name with Dependency**:
```typescript
// Filename: another.workspace-service.ts
class AnotherService {
constructor(private myWorkspaceService: MyWorkspaceService) {}
}
// Error: Class name should end with 'WorkspaceService'.
```
### First step
This rule is only a warning for now, and then we'll migrate all the code
that need to be migrated and move from `warn` to `error`.
Fix#6309
Co-authored-by: Charles Bochet <charles@twenty.com>
### Overview
This PR builds upon #5153, adding the ability to get a repository for
custom objects. The `entitySchema` is now generated for both standard
and custom objects based on metadata stored in the database instead of
the decorated `WorkspaceEntity` in the code. This change ensures that
standard objects with custom fields and relations can also support
custom objects.
### Implementation Details
#### Key Changes:
- **Dynamic Schema Generation:** The `entitySchema` for standard and
custom objects is now dynamically generated from the metadata stored in
the database. This shift allows for greater flexibility and
adaptability, particularly for standard objects with custom fields and
relations.
- **Custom Object Repository Retrieval:** A repository for a custom
object can be retrieved using `TwentyORMManager` based on the object's
name. Here's an example of how this can be achieved:
```typescript
const repository = await this.twentyORMManager.getRepository('custom');
/*
* `repository` variable will be typed as follows, ensuring that standard
fields and relations are properly typed:
* const repository: WorkspaceRepository<CustomWorkspaceEntity & {
* [key: string]: any;
* }>
*/
const res = await repository.find({});
```
Fix#6179
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Weiko <corentin@twenty.com>
## Context
An object should always have a labelIdentifier (would be its primary key
at least). If the associated field is deleted by a user, it will break
the app. Ideally we should handle that on the DB level but we don't have
a FK for this column yet.
In the meantime I'm adding the validation check in the backend, note
that this is already handle on the FE side since the "archive/delete"
buttons don't appear for such fields so you need to reassign it to
another field first which is the desired behaviour.
Hello team,
- Modified File: `packages/twenty-docker/twenty/entrypoint.sh`
- Reason: The current `entrypoint.sh` works fine for server with RAM
more than 8GB. But the server with less RAM will give error while
running `scripts/setup-db.ts`.
![setup-db-failed](https://github.com/twentyhq/twenty/assets/52379037/5e27240a-23ef-4d52-93bb-337e1a004feb)
- My commit: It will set `NODE_OPTIONS="--max-old-space-size=8192"` in
docker entrypoint which guarantee to run the script successfully.
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This PR aims to fix#6102
I think we should dicuss a bit about how we should manage the lenght and
set the variables globally
Edit :
@RobertoSimonini1 I used this PR to fix various problems that were left
unsolved :
- Refactor TextDisplay component, EllipsisDisplay was redundant with
OverflowingTextWithTooltip
- Removed maxWidth on TextDisplay for all other components, as it wasn't
the right way to do it, the parent container should be responsible for
width not the TextDisplay (code smell)
- Drilled-down isCentered to respect its intent in the RecordInlineCell
display of the record show page title
- Fixed RecordInlineCellEditMode so that the portal is well centered
above the record show page title
- Fixed DoubleTextInput width so that it expands normally and takes all
its parent available space.
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
## Context
LabelIdentifier and ImageIdentifier are metadata info attached to
objectMetadata that are used to display a record in a more readable way.
Those columns point to existing fields that are part of the object.
For example, for a relation picker of a person, we will show a record
using the "name" labelIdentifier and the "avatarUrl" imageIdentifier.
<img width="215" alt="Screenshot 2024-07-11 at 18 45 51"
src="https://github.com/twentyhq/twenty/assets/1834158/488f8294-0d7c-4209-b763-2499716ef29d">
Currently, the FE has a specific logic for company and people objects
and we have a way to update this value via the API for custom objects,
but the code is not flexible enough to change other standard objects.
This PR updates the WorkspaceEntity API so we can now provide the
labelIdentifier and imageIdentifier in the WorkspaceEntity decorator.
Example:
```typescript
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.activity,
namePlural: 'activities',
labelSingular: 'Activity',
labelPlural: 'Activities',
description: 'An activity',
icon: 'IconCheckbox',
labelIdentifierStandardId: ACTIVITY_STANDARD_FIELD_IDS.title,
})
@WorkspaceIsSystem()
export class ActivityWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
standardId: ACTIVITY_STANDARD_FIELD_IDS.title,
type: FieldMetadataType.TEXT,
label: 'Title',
description: 'Activity title',
icon: 'IconNotes',
})
title: string;
...
```
## Context
We've created a yoga (gql server) hook that catches requests and cache
them when needed. In practice we use it on the "objects" query because
this is often queried on the FE and it should never return something
different unless the schema has been intentionally changed by the user
when editing their data model (updating objects, fields, etc).
The issue here is we always cache the response regardless of its result,
even when it fails. This PR fixes that behaviour by only caching the
query response if it is successful.
I'm also fixing the cache key because the signature let users put
multiple operations and the cache key was not taking this into account
(we always use it on only one operation but we might have issues in the
future because another operation response could have erased the cached
response of another). Now the cache key contains the name of the
operation as well.
## Test
tested locally by manually throwing an error in the JWT auth guard
## Context
- Adding RATING sort and filter capabilities.
- Fixing isEmpty/isNotEmpty filters
- Fixing combined view filters so it combines filters per field metadata
and not per filter id. This is more a product question but to me it does
not make sense to apply multiples filters on the same field IF the
operations is wrapped in a AND. If at some point we want to put a OR
instead then that would make more sense
# Fix URL handling for LinkedIn and Twitter links
Fixes#6287
## Solution
Updated `checkUrlType` function to prepend "https://" to URLs if
missing, ensuring proper handling of social media links.
## Changes
- Modified `/packages/twenty-front/src/utils/checkUrlType.ts`
- Added a check to prepend "https://" if URL doesn't start with a
protocol
---------
Co-authored-by: Prince Yadav <prince1.yadav@tataaig.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Services exceptions are not catch when the endpoint comes from an
auto-resolver.
We want to remove auto-resolver but it requires to implement pagination
by ourselves.
As a quick fix, here are interceptors that will trigger the exception
handler.
I had a hard time making it generic so I finally added one interceptor
for each since this is not supposed to stay
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/9fcdd5ca-4329-467c-ada8-4dd5d45be259">
Open questions:
- the Tag component does not match Figma in term of style and API for
"transparent" | "outline". We need to discuss with @Bonapara what is the
desired behavior here
- right now opportunity.stage is not nullable. We need to discuss with
@FelixMalfait and @Bonapara what we want here. I would advocate to make
a it nullable for now until we introduce settings on select fields.
custom select are nullable and it could be confusing for the user
Follow up:
- enhance tests on Tags
- add story to cover the No Value column on record board
### Description
- Refacto "Select All/Unselect all" on indexes
- Add sequential mass deletion from front end (limited to 10k records)
- Fixed coverage with new unit tests on new useFetchAllRecordIds hook
and other utils
### Refs
Closes#4397Closes#5169
### Demo
https://github.com/twentyhq/twenty/assets/26528466/2658ad2c-827e-4670-b42b-3092e268ff32
---------
Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
fixes#6106
---------
Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com>
Co-authored-by: Adithya Thejas S <adithyathejass.1997@gmail.com>
Co-authored-by: Rob Luke <code@robertluke.net>
Co-authored-by: rostaklein <r.klein@make.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
- Refactor connected account module
- Move blocklist into it's own module
- Move contact-creation-manager into it's own module
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
We call convertExceptionToGraphQLError in the exception handler for http
exceptions but we don't take into account those that already are
graphqlErrors and because of that the logic of convertExceptionToGraphql
is to fallback to a 500.
Now if the exception is a BaseGraphqlError (custom graphql error we
throw in the code), we throw them directly.
BEFORE
<img width="957" alt="Screenshot 2024-07-12 at 15 33 03"
src="https://github.com/user-attachments/assets/22ddae13-4996-4ad3-8f86-dd17c2922ca8">
AFTER
<img width="923" alt="Screenshot 2024-07-12 at 15 32 01"
src="https://github.com/user-attachments/assets/d3d6db93-6d28-495c-a4b4-ba4e47d45abd">
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This is the first step of Link field type deprecation
(https://github.com/twentyhq/twenty/issues/5909).
Forbid creation of link field type in product and api. Update to this
type is not a concern as we do not allow the update of field type.
In the longer term, we want to improve the efficiency and reliability of
the sync-metadata command, by choosing an error handling strategy and
paying greater attention to health checks.
In the meantime, this PR adds an option to run the sync-metadata command
on all active workspaces at once.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Closes#5735.
The field probability on opportunity will -
- stop being created for new workspaces (after this PR is merged)
- have "isCustom" value set to true and be displayed as such in the
settings (after this PR is merged + sync-metadata is run on workspace)
- still show in the views (all the time)
This field is deprecated as a standard field but not replaced by another
one, so we are not adding the `(deprecated)` suffix in the label.
- Fixed activity creation in cache
- Fixed activity creation in DB, where the relation target was
disappearing after creation
- Added an option to match root query filter in creation optimistic
effect to avoid adding the newly created record in every mounted query
in Apollo cache on the same object (which was causing notes to be
duplicated on every object in the cache)
- Fixed tab list scope id
- Fixed various browser console warnings
# Support for Kubernetes via Terraform and Manifests
Adding basic support for the following Kubernetes resources
- persistent volume
- server
- database
- persistent volume claim
- server
- database
- deployment
- server
- database
- ingress
- server
- service
- server
- database
- secret
- server
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Using a portal in dropdown systematically can be an issue in case we are
having dropdown within dropdown. The useClickOutside listener will be
triggered. It's easier to usePortal only in the case we really need
them, which is quite rare
Add a new command to delete objectMetadataId fieldMetadata that have a
wrong standard-id. This is because we have fixed the missing
objectMetadataId column but one already exists in the fieldMetadataId
with the wrong table. We should run this command before run the
sync-metadata.
Introduced a new module and command to run all the command associated
with the upgrade to 0.22. Not exactly sure with this structure but
ideally we would like to have only 1 command for version upgrades so
this is a first step.
## Context
We want to add an index on our foreign keys since PG does not do it for
us. An index can sometimes be expensive and not always meaningful
depending on different usages but in our case we decided to apply an
index for every foreign keys.
```typescript
@WorkspaceIndex()
@WorkspaceJoinColumn('author')
authorId: string;
```
This syntax is valid but since we want to apply it to every join column
I've decided to update the code of WorkspaceJoinColumn so it properly
registers a new index at the same time which is less error-prone.
Note: We had a bug on index name generation since postgres index names
are unique per schema and not table, the object metadata id (hashed) has
been added to the formula that generates the name of the index
## Test
Sync metadata. We have 45 join columns as of today per workspace, we
should see 45 rows inside IndexMetadata table
Insert inside AuditLog table are all failing due to objectMetadataId
column missing.
The FieldMetadata was sharing the same standard-id with another one
(objectName) so it was skipped during the comparison step of the
sync-metadata.
Running a sync-metadata again should fix this issue. Note that this
column is non-nullable so if the table contains existing records, it
will fail. However, since the insert was failing I'm assuming the table
is empty anyway.
- Refactor calendar modules and some messaging modules to better
organize them by business rules and decouple them
- Work toward a common architecture for the different calendar providers
by introducing interfaces for the drivers
- Modify cron job to use the new sync statuses and stages
Recently, we've forced all dropdown menu to be displayed in portal. This
loses the z-index hieararchy structure and forces us to specify a higher
z-index in order for dropdown menus to be displayed on top
- Refactored components in table
- Added a isTableRecordScrolledLeftState and
isTableRecordScrolledTopState to subscribe to table scroll
- Added a zIndex logic that subscribes to those new states in new tinier
components
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
We have recently decided that boolean fields should only accept truthy
or falsy value, with users deciding of a default value at creation.
This command helps cleaning the existing data, by
1. updating all boolean fields default values from null to false
2. updating all boolean fields values for records from null to false
---------
Co-authored-by: Weiko <corentin@twenty.com>
In this PR:
- Renaming SettingsAccountsEmailBlocklist to
SettingsAccountsEmailBlocklist as the blocklist is not tied to
emails/messaging but is user level
- Changing the UI settings UI by removing /emails/{id} page and adding
tabs on /emails page
<img width="1512" alt="image"
src="https://github.com/twentyhq/twenty/assets/12035771/d215a891-fff9-477d-915d-0d7a697742e8">
NEXT_PUBLIC environnement variable values are set at build time and not
run time.
Build is currently performed in Github actions so setting those vars at
runtime has no effect.
We can use a package to automatically pass those variables at runtime
Fixes#6032.
Pg has a char limit on identifiers (= table, columns, enum names) of 63
bytes.
Let's limit the metadata names that will be converted to identifiers
(objects names, fields names, relation names, enum values) to 63 chars.
For the sake of simplicity in the FE we will limit the input length of
labels.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Added:
- An "Ask AI" command to the command menu.
- A simple GraphQL resolver that converts the user's question into a
relevant SQL query using an LLM, runs the query, and returns the result.
<img width="428" alt="Screenshot 2024-06-09 at 20 53 09"
src="https://github.com/twentyhq/twenty/assets/171685816/57127f37-d4a6-498d-b253-733ffa0d209f">
No security concerns have been addressed, this is only a
proof-of-concept and not intended to be enabled in production.
All changes are behind a feature flag called `IS_ASK_AI_ENABLED`.
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## Context
Rating fields were not editable on the show page and kanban view when
they were null, this is because we don't have a way to leave the empty
state for fields that are editModeContentOnly.
~~This is actually an issue for bool fields (which is the other field
type that has editModeContentOnly) as well but they have default values
can't go be edited to NULL so it's not visible.~~
Actually let's fix bool, this could happen too
Hovering over "Empty" will now show the RatingField edit mode.
I'm not 100% sure about this solution though, we could also make this
behaviour on click? I preferred over since this is the behaviour on the
table view 🤔
## Test
https://github.com/twentyhq/twenty/assets/1834158/6825b5c3-2c62-41f2-8e03-343bc0e895e2
This PR was first here to fix the issue related to ticket #5004, after
some testing it seems that changing the name of a relation is actually
properly working, if we rename `ONE-TO-MANY` side, the only things that
is going to be updated is the FieldMetadata as the `joinColumn` is
stored on the opposite object.
For `MANY-TO-ONE` relations, the `joinColumn` migration is properly
generated. We need to take care that if we rename a side of a relation,
sometimes the opposite side doesn't have `inverseSideFieldKey`
implemented and used by default the name of the opposite object, so this
is going to throw an error as the field can't be found in the object.
---------
Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com>
An error was introduced in the calendar cron job because we tried to
inject the workspace context inside the calendarChannelRepository where
we didn't have access to that context.
We have recently deprecated our subscriptionStatus on workspace to
replace it by a check on existing subscription (+ freeAccess
featureFlag) but the logic was not properly implemented
Closes#5748
- Create feature flag
- Add scope `https://www.googleapis.com/auth/profile.emails.read` when
connecting an account
- Get email aliases with google people API, store them in
connectedAccount and refresh them before each message-import
- Update the contact creation logic accordingly
- Refactor
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Class exception for each metadata module + handler to map on graphql
error
TODO left :
- find a way to call handler on auto-resolvers nestjs query (probably
interceptors)
- discuss what should be done for pre-hooks errors
- discuss what should be done for Unauthorized exception
This PR fix an issue with the `IsNull()` find operator applied on
one-to-many relation, this one is not supported by TypeORM.
We can instead filter by an empty array to retrieve object with empty
relations.
CSS modules were used as a first test for performance optimization.
We later found out that Linaria was a better tradeoff.
This PR removes what was implemented in CSS modules and also the CSS
theme file that was created that was overlapping with the TS theme
files.
querying workspaceMembers may be slow leads to wrong
setNextOnboardingStatus value. So we added a resolved field in workspace
to get workspaceMemberCount directly
In this PR, I'm simplifying storybook setup:
1) Remove build --test configuration that prevent autodocs. We are not
using autodocs at all (the dev experience is not good enough), so I have
completely disabled it.
2) Clarify `serve` vs `test` vs `serve-and-test` configurations
After this PR:
- you can serve storybook in two modes: `npx nx run
twenty-front:storybook:serve:dev` and `npx nx run
twenty-front:storybook:serve:static`
- you can run tests agains an already served storybook (this is useful
in dev so you don't have to rebuild everytime to run tests): `npx nx run
twenty-front:storybook:test`
- you can conbine both: `npx nx run
twenty-front:storybook:serve-and-test:static`
- move front `onboardingStatus` computing to server side
- add logic to `useSetNextOnboardingStatus`
- update some missing redirections in
`usePageChangeEffectNavigateLocation`
- separate subscriptionStatus from onboardingStatus
- Put error handling outside of `refreshAndSaveAccessToken`
- return after failing to refresh access token in
`processMessageBatchImport`
- remove unnecessary token refresh in `processMessageListFetch`
This PR introduce a new decorator named `@WorkspaceJoinColumn`, the goal
of this one is to manually declare the join columns inside the workspace
entities, so we don't have to rely on `ObjectRecord` type.
This decorator can be used that way:
```typescript
@WorkspaceRelation({
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.company,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Company',
description: 'ActivityTarget company',
icon: 'IconBuildingSkyscraper',
inverseSideTarget: () => CompanyWorkspaceEntity,
inverseSideFieldKey: 'activityTargets',
})
@WorkspaceIsNullable()
company: Relation<CompanyWorkspaceEntity> | null;
// The argument is the name of the relation above
@WorkspaceJoinColumn('company')
companyId: string | null;
```
Closes#5924.
Adding the "many" side of relations in the table view, and fixing some
issues (glitch in Multi record select, cache update after update).
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This PR introduces an `upsert` parameter (along the existing `data`
param) for `createOne` and `createMany` mutations.
When upsert is set to `true`, the function will look for records with
the same id if an id was passed. If not id was passed, it will leverage
the existing duplicate check mechanism to find a duplicate. If a record
is found, then the function will perform an update instead of a create.
Unfortunately I had to remove some nice tests that existing on the args
factory. Those tests where mostly testing the duplication rule
generation logic but through a GraphQL angle. Since I moved the
duplication rule logic to a dedicated service, if I kept the tests but
mocked the service we wouldn't really be testing anything useful. The
right path would be to create new tests for this service that compare
the JSON output and not the GraphQL output but I chose not to work on
this as it's equivalent to rewriting the tests from scratch and I have
other competing priorities.
Fixes https://github.com/twentyhq/twenty/issues/6016
This was another side effect of the optimization made on
RecordTableCellContainer to avoid using recoil states, but which causes
too many unpredictable side effects.
I just put back the previous system which works well. We'll see how to
optimize it again later.
#### Overview
This PR introduces a new API for dynamically registering and executing
pre and post query hooks in the Workspace Query Hook system using the
`@WorkspaceQueryHook` decorator. This approach eliminates the need for
manual provider registration, and fix the issue of `undefined` or `null`
repository using `@InjectWorkspaceRepository`.
#### New API
**Define a Hook**
Use the `@WorkspaceQueryHook` decorator to define pre or post hooks:
```typescript
@WorkspaceQueryHook({
key: `calendarEvent.findMany`,
scope: Scope.REQUEST,
})
export class CalendarEventFindManyPreQueryHook implements WorkspaceQueryHookInstance {
async execute(userId: string, workspaceId: string, payload: FindManyResolverArgs): Promise<void> {
if (!payload?.filter?.id?.eq) {
throw new BadRequestException('id filter is required');
}
// Implement hook logic here
}
}
```
This API simplifies the registration and execution of query hooks,
providing a more flexible and maintainable approach.
---------
Co-authored-by: Weiko <corentin@twenty.com>
To enable Google Calendar integration, you need to run `yarn
command:prod cron📆google-calendar-sync` in the worker
container. However, currently, the self-hosting guide does not tell you
how to do it. If you just follow the guide, only Gmail integration will
be enabled. So I added the command for calendar sync cron on
self-hosting-var.mdx.
As per title!
Also, I'm removing an incorrect logic in the enum migration runner that
takes care of the case where we have no defaultValue but non nullable
which is not a valid business case.
# This PR
- Revise my previous work (PR #5969)
Because it would break the current logic and cause unexpected behavior.
(Issue #5979)
- Solve (Issue #5915) with another way
@lucasbordeau What do you think about my current approach?
@JarWarren Please check it out—I'd love to get your feedback too!
---------
Co-authored-by: Achsan <achsanh@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Hi Twenty team,
I'd love to have Australian dollar as an option in Twenty! Please let me
me know if I have missed anything I need to change to enable this.
Thanks for a a great product
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
We had an issue on infinite scroll on table view.
The fetch more logic was modifying isTableLastRowVisible state (which is
wrong, how could it know)? This was done to prevent loading too much
data at once. This was causing some race condition on
isTableLastRowVisible (as the table itself was also changing it
depending on the real visibility of the line)
I have remove this hacky usage of isTableLastRowVisible and replaced it
by a setTimeout to let the user some time to scroll and introduce a
throttle logic.
Our tests on FE are red, which is a threat to code quality. I'm adding a
few unit tests to improve the coverage and lowering a bit the lines
coverage threshold
## Context
Our Flexible Schema engine dynamically generates entities/tables/APIs
for us but was not flexible enough to build indexes in the DB. With more
and more features involving heavy queries such as Messaging, we are now
adding a new WorkspaceIndex() decorator for our standard objects (will
come later for custom objects). This decorator will give enough
information to the workspace sync metadata manager to generate the
proper migrations that will create or drop indexes on demand.
To be aligned with the rest of the engine, we are adding 2 new tables:
IndexMetadata and IndexFieldMetadata, that will store the info of our
indexes.
## Implementation
```typescript
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.person,
namePlural: 'people',
labelSingular: 'Person',
labelPlural: 'People',
description: 'A person',
icon: 'IconUser',
})
export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceField({
standardId: PERSON_STANDARD_FIELD_IDS.email,
type: FieldMetadataType.EMAIL,
label: 'Email',
description: 'Contact’s Email',
icon: 'IconMail',
})
@WorkspaceIndex()
email: string;
```
By simply adding the WorkspaceIndex decorator, sync-metadata command
will create a new index for that column.
We can also add composite indexes, note that the order is important for
PSQL.
```typescript
@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.person,
namePlural: 'people',
labelSingular: 'Person',
labelPlural: 'People',
description: 'A person',
icon: 'IconUser',
})
@WorkspaceIndex(['phone', 'email'])
export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
```
Currently composite fields and relation fields are not handled by
@WorkspaceIndex() and you will need to use this notation instead
```typescript
@WorkspaceIndex(['companyId', 'nameFirstName'])
export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
```
<img width="700" alt="Screenshot 2024-06-21 at 15 15 45"
src="https://github.com/twentyhq/twenty/assets/1834158/ac6da1d9-d315-40a4-9ba6-6ab9ae4709d4">
Next step: We might need to implement more complex index expressions,
this is why we have an expression column in IndexMetadata.
What I had in mind for the decorator, still open to discussion
```typescript
@WorkspaceIndex(['nameFirstName', 'nameLastName'], { expression: "$1 || ' ' || $2"})
export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
```
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR is replacing and removing all the raw queries and repositories
with the new `TwentyORM` and injection system using
`@InjectWorkspaceRepository`.
Some logic that was contained inside repositories has been moved to the
services.
In this PR we're only replacing repositories for calendar feature.
---------
Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
## Fixes#5902 :
- [x] Navigation items' height should be risen to 28px.
> For clarity:
- [x] Also increased the height of NavigationDrawerSectionTitle to 28px
to match navigation item.
- [x] The gap between sections should be reduced to 12px
> Was already completed it seems.
- [x] The workspace switcher should be aligned with the navigation items
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Update the docs to accurately reflect `LoggerDriverType`. Using `sentry`
throws an error on startup.
```
export enum LoggerDriverType {
Console = 'console',
}
```
Happy to change the wording of course.
Closes#5915
This issue occurs only when there is no select field.
The user then creates a new one in settings and returns back to the view
picker.
And the bug arises, it because `viewPickerKanbanFieldMetadataId` is not
being set correctly.
When a user navigate to settings, the dirty state should be set to
false. As a result, after re-rendering the view picker component, it
triggers the effect to set `viewPickerKanbanFieldMetadataId`
---------
Co-authored-by: Achsan <achsanh@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Timezone with a negative offset weren't working good with date pickers.
I split the logic for display and parsing between date only and
datetime.
Date time is sending and displaying using timezone, and date only is
sending and displaying by forcing the date to take its UTC day and month
and 00:00:00 time.
This way its consistent across all timezones.
Previously the error boundary component was re-rendering with the same
state as long as we stayed in the same router, so for page change inside
an index container, it would stay on error state.
The fix is to memorize the location the error page is on during its
first render, and then to reset the error boundary if it gets
re-rendered with a different location even in the same index container.
Fixes : #3592
- Remove filters from metadata rest api
- add limite before and after parameters for metadata
- remove update from metadata relations
- fix typing issue
- fix naming
- fix before parameter
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Greetings from Seoul! I found this amazing project a few days ago, and
trying to introduce it to my team. However there is a tiny but
significant problem, that South Korean won is not available in twenty.
So I added `KRW` to the enum `CurrencyCode` and the constant
`SETTINGS_FIELD_CURRENCY_CODES`. I tested it locally and apparently
works fine.
The display for Rating field type was missing, I just added it based on
RatingInput in readonly mode and optimized a bit for performance also.
Fixes https://github.com/twentyhq/twenty/issues/5900
Filtering relations is not allowed
(see`packages/twenty-server/src/engine/metadata-modules/relation-metadata/dtos/relation-metadata.dto.ts`)
so we remove filtering for find many relation
we also fixed some bug in result structure and metadata open-api schema
### Overview
This PR introduces significant enhancements to the MessageQueue module
by integrating `@Processor`, `@Process`, and `@InjectMessageQueue`
decorators. These changes streamline the process of defining and
managing queue processors and job handlers, and also allow for
request-scoped handlers, improving compatibility with services that rely
on scoped providers like TwentyORM repositories.
### Key Features
1. **Decorator-based Job Handling**: Use `@Processor` and `@Process`
decorators to define job handlers declaratively.
2. **Request Scope Support**: Job handlers can be scoped per request,
enhancing integration with request-scoped services.
### Usage
#### Defining Processors and Job Handlers
The `@Processor` decorator is used to define a class that processes jobs
for a specific queue. The `@Process` decorator is applied to methods
within this class to define specific job handlers.
##### Example 1: Specific Job Handlers
```typescript
import { Processor, Process, InjectMessageQueue } from 'src/engine/integrations/message-queue';
@Processor('taskQueue')
export class TaskProcessor {
@Process('taskA')
async handleTaskA(job: { id: string, data: any }) {
console.log(`Handling task A with data:`, job.data);
// Logic for task A
}
@Process('taskB')
async handleTaskB(job: { id: string, data: any }) {
console.log(`Handling task B with data:`, job.data);
// Logic for task B
}
}
```
In the example above, `TaskProcessor` is responsible for processing jobs
in the `taskQueue`. The `handleTaskA` method will only be called for
jobs with the name `taskA`, while `handleTaskB` will be called for
`taskB` jobs.
##### Example 2: General Job Handler
```typescript
import { Processor, Process, InjectMessageQueue } from 'src/engine/integrations/message-queue';
@Processor('generalQueue')
export class GeneralProcessor {
@Process()
async handleAnyJob(job: { id: string, name: string, data: any }) {
console.log(`Handling job ${job.name} with data:`, job.data);
// Logic for any job
}
}
```
In this example, `GeneralProcessor` handles all jobs in the
`generalQueue`, regardless of the job name. The `handleAnyJob` method
will be invoked for every job added to the `generalQueue`.
#### Adding Jobs to a Queue
You can use the `@InjectMessageQueue` decorator to inject a queue into a
service and add jobs to it.
##### Example:
```typescript
import { Injectable } from '@nestjs/common';
import { InjectMessageQueue, MessageQueue } from 'src/engine/integrations/message-queue';
@Injectable()
export class TaskService {
constructor(
@InjectMessageQueue('taskQueue') private readonly taskQueue: MessageQueue,
) {}
async addTaskA(data: any) {
await this.taskQueue.add('taskA', data);
}
async addTaskB(data: any) {
await this.taskQueue.add('taskB', data);
}
}
```
In this example, `TaskService` adds jobs to the `taskQueue`. The
`addTaskA` and `addTaskB` methods add jobs named `taskA` and `taskB`,
respectively, to the queue.
#### Using Scoped Job Handlers
To utilize request-scoped job handlers, specify the scope in the
`@Processor` decorator. This is particularly useful for services that
use scoped repositories like those in TwentyORM.
##### Example:
```typescript
import { Processor, Process, InjectMessageQueue, Scope } from 'src/engine/integrations/message-queue';
@Processor({ name: 'scopedQueue', scope: Scope.REQUEST })
export class ScopedTaskProcessor {
@Process('scopedTask')
async handleScopedTask(job: { id: string, data: any }) {
console.log(`Handling scoped task with data:`, job.data);
// Logic for scoped task, which might use request-scoped services
}
}
```
Here, the `ScopedTaskProcessor` is associated with `scopedQueue` and
operates with request scope. This setup is essential when the job
handler relies on services that need to be instantiated per request,
such as scoped repositories.
### Migration Notes
- **Decorators**: Refactor job handlers to use `@Processor` and
`@Process` decorators.
- **Request Scope**: Utilize the scope option in `@Processor` if your
job handlers depend on request-scoped services.
Fix#5628
---------
Co-authored-by: Weiko <corentin@twenty.com>
I have fixed the scrolling the record container page on mobile making it
hidden.
This PR aims to fix#5745
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
A mini PR to discuss with @Bonapara tomorrow
Separating remote objects from others and making the menu collapsible
(style to be changed)
<img width="225" alt="Screenshot 2024-06-12 at 23 25 59"
src="https://github.com/twentyhq/twenty/assets/6399865/b4b69d36-6770-43a2-a5e8-bfcdf0a629ea">
Biggest issue is we don't use local storage today so the collapsed state
gets lost.
I see we have localStorageEffect with recoil. Maybe store it there?
Seems easy but don't want to introduce a bad pattern.
Todo:
- style update
- collapsible favorites
- persistent storage
The record chip generator context was missing a edge were a new field of
type relation is created and not yet in the metadata so no chip
generator function can be precomputed.
For now I added a fallback default chip generator, to prevent any bug,
but we might want to add a new chip generator function while creating
the new field ?
# Context
Currently, the Twenty platform incorporates "positions" for rows on the
backend, which are functional within the Kanban view. However, this
advantageous feature has yet to be leveraged within list views.
# Feature Proposal
## Implement Row-Reordering via Drag-and-Drop on Frontend (#4846)
- This PR addresses the implementation of row reordering via
Drag-and-Drop on frontend. The objective is to enrich the list view
functionality by introducing a grip that dynamically appears upon
hovering over the left space preceding the checkbox container. This grip
empowers users to effortlessly reposition rows within the list.
#### Proposal Highlights:
- **Enhanced User Interaction**: Introduce a draggable grip to
facilitate intuitive row reordering, enhancing user experience and
productivity.
- **Preservation of Design Aesthetics**: By excluding the grip from the
first row and maintaining the left gap, we uphold design integrity while
providing enhanced functionality.
- **Consistency with Existing Features**: Align with existing
drag-and-drop functionalities within the platform, such as Favorites
re-ordering or Fields re-ordering in table options, ensuring a seamless
user experience.
## Implementation Strategy
### Grip Implementation:
- Add an extra column to the table (header + body) to accommodate the
grip cell, which displays the IconListViewGrip when its container is
hovered over.
- Ensure the preceding left-space is maintained by setting the
corresponding width for this column and removing padding from the table
container (while maintaining padding in other page elements and the
Kanban view for coherence).
### Row Drag and Drop:
- Implement row drag-and-drop functionality using draggableList and
draggableItem, based on the existing logic in the KanbanView for row
repositioning.
- Create a draggableTableBody and apply it to the current
RecordTableBody (including modal open triggering - if dragging while
sorting exists).
- Apply the draggableItem logic to RecordTableRow.
### Sorting Modal Implementation:
- Reuse the ConfirmationModel for the removeSortingModal.
- Create a new state to address the modal.
- Implement sorting removal logic in the corresponding modal file.
## Outcome
- The left-side margin is preserved.
- The grip appears upon hovering.
- Dragging a row gives it and maintains an aesthetic appearance.
- Dropping a row updates its position, and the table gets a new
configuration.
- If sorting is present, dropping a row activates a modal. Clicking on
the "Remove Sorting" button will deactivate any sorting (clicking on
"Cancel" will close the modal), and the table will revert to its default
configuration by position, allowing manual row reordering. Row
repositioning will not occur if sorting is not removed.
- The record table maintains its overall consistency.
- There are no conflicts with DragSelect functionality.
https://github.com/twentyhq/twenty/assets/92337535/73de96cc-4aac-41a9-b4ec-2b8d1c928d04
---------
Co-authored-by: Vasco Paisana <vasco.paisana@tecnico.ulisboa.pt>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>