In this PR, I'm refactoring the way we associate messageParticipant post
person/company creation. Instead of looking a all person without
participant, we are passing the one that were just created.
Also, I'm making sure the message and messageParticipant creation
transaction is commited before creating person/company creation (and
then messageParticipant association)
This PR fixes several issues:
- enum naming should be: {tableName}_{fieldName}_enum and respecting the
case
- defaultValue format handled in the FE should respect the one in the BE
In my opinion we should refactor the defaultValue:
- we should respect backend format: "'myDefault'" for constant default
and "0" for float, "now" for expressions, "true" for booleans. we can
rename it to defaultValueExpression if it is more clear but we should
not maintain a parallel system
- we should deprecate option: isDefaultValue which is confusing
- we should re-work backend to have a more unified approach between
fields and avoid having if everywhere about select, multiselect, and
currency cases. one unified "computeDefaultValue" function should do the
job
What is still broken:
- currency default Value on creation. I think we should do the refactor
first
- select default value edition.
These cases do not break the schema but are ignored currently
# This PR
- Fix#5021
- Migrates `passwordResetToken` and `passwordResetTokenExpiresAt` fields
from `core.users` to `core.appToken`
- Marks those fields as `deprecated` so we can remove them later if we
are happy with the transition -- I took this decision on my own,
@FellipeMTX let me know what you think about it, we can also remove them
straight away if you think it's better
- Fixed the `database:migration` script from the `twenty-server` to:
```json
"database:migrate": {
"executor": "nx:run-commands",
"dependsOn": ["build"], // added this line
"options": {
"cwd": "packages/twenty-server",
"commands": [
"nx typeorm -- migration:run -d src/database/typeorm/metadata/metadata.datasource",
"nx typeorm -- migration:run -d src/database/typeorm/core/core.datasource"
],
"parallel": false
}
},
```
The migration script wasn't running because the builds were not executed
- [x] Added unit tests for the token.service file's changes
Looking forward to hearing feedback from you
cc: @charlesBochet
---------
Co-authored-by: Weiko <corentin@twenty.com>
This PR is a follow up of PR #5153.
This one introduce some changes on how we're querying composite fields.
We can do:
```typescript
export class CompanyService {
constructor(
@InjectWorkspaceRepository(CompanyObjectMetadata)
private readonly companyObjectMetadataRepository: WorkspaceRepository<CompanyObjectMetadata>,
) {}
async companies(): Promise<CompanyObjectMetadata[]> {
// Old way
// const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
// where: { xLinkLabel: 'MyLabel' },
// });
// Result will return xLinkLabel property
// New way
const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
where: { xLink: { label: 'MyLabel' } },
});
// Result will return { xLink: { label: 'MyLabel' } } property instead of { xLinkLabel: 'MyLabel' }
return companiesFilteredByLinkLabel;
}
}
```
Also we can now inject `TwentyORMManage` class to manually create a
repository based on a given `workspaceId` using
`getRepositoryForWorkspace` function that way:
```typescript
export class CompanyService {
constructor(
// TwentyORMModule should be initialized
private readonly twentyORMManager,
) {}
async companies(): Promise<CompanyObjectMetadata[]> {
const repository = await this.twentyORMManager.getRepositoryForWorkspace(
'8bb6e872-a71f-4341-82b5-6b56fa81cd77',
CompanyObjectMetadata,
);
const companies = await repository.find();
return companies;
}
}
```
## Context
Because creating an object in metadata also generates a graphql type and
because graphql does not allow 2 types with the same name, we have to
manage a list of reserved keywords that can't be used as object names.
Currently we were maintaining a list of the core objects but we also
have to introduce composite fields that are also generated as gql types.
The whole viewBar component was re-rendered on view changes which was
introducing performance issue. The need was to compute page title, this
should be done in a lower level component
## Context
Positions are used within a view to display and sort the different
records of standard/custom object.
When we add a new record and want to put it before the existing first
record, we have to use float values to insert them in the DB and respect
the desired order. We are adding a new command that can be executed to
flatten those positions.
---------
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
- Added dynamic OG Image to share and download in contributors page
<img width="1176" alt="Screenshot 2024-05-02 at 16 24 00"
src="https://github.com/twentyhq/twenty/assets/102751374/0579454b-ccc7-46ba-9875-52458f06ee82">
- Added dynamic metadata
- Added design to contributor page
- Added a NEXT_PUBLIC_HOST_URL in the .env file
Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
While using middleware (executed pre-graphql) for graphql endpoint, we
need to swallow exception and return errors with a 200. Otherwise it's
not a valid graphql response
## Context
There is no calendarChannel syncStatus column compared to the
messageChannel table. In the meantime, we are trying to infer its status
based on the fact that the connection hasn't failed and the sync is
enabled
- Fixes storybook coverage command: the coverage directory path was
incorrect, but instead of failing `storybook:test --configuration=ci`,
it was hanging indefinitely.
- Switches back to `concurrently` to launch `storybook:static` and
`storybook:test` in parallel, which allows to use options to explicitly
kill `storybook:static` when `storybook:test` fails.
- Moves `storybook:test --configuration=ci` to its own command
`storybook:static:test`: used in the CI, and can be used locally to run
storybook tests without having to launch `storybook:dev` first.
- Creates command `storybook:coverage` and enables cache for this
command.
- Fixes Jest tests that were failing.
- Improves caching conditions for some tasks (for instance, no need to
invalidate Jest test cache if only Storybook story files were modified).
Various fixes
- Remote objects are read-only for now, we already hide and block most
of the write actions but the button that allows you to add a new record
in an empty collection was still visible.
- CreatedAt is not mandatory on remote objects (at least for now) so it
was breaking the show page, it now checks if createdAt exists and is not
null before trying to display the human readable format `Added x days
ago`
- The filters are overwritten in query-runner-args.factory.ts to handle
NUMBER field type, this was only working with filters like
```
{
"id": {
"in": [
1
]
}
```
but not with more depth such as
```
"and": [
{},
{
"id": {
"in": [
1
]
}
}
]
```
- Fixes CREATE FOREIGN TABLE raw query which was missing ",".
## Context
We recently introduced a change that now throws a 401 if the token is
invalid or expired.
The first implementation is using an allow list and 'IntrospectionQuery'
was missing so the playground was broken.
The check has been updated and we now only check the excludedOperations
list if a token is not present. This is because some operations can be
both used as loggedIn and loggedOut so we want to validate the token for
those sometimes (and set the workspace, user, cache version, etc). Still
not a very clean solution imho.
## Context
We have a non-nullable constraint on authorId in attachments and
documents, until we have soft-deletion we need to handle deletion of
workspace-members and their attachments/documents.
This PR introduces pre-hooks to deleteOne/deleteMany
This is called when a user deletes a workspace-member from the members
page
Next: needs to be done on user level as well. This is called when users
try to delete their own accounts. I've seen other issues such as
re-creating a user with a previously used email failing.
Adding stripe integration by making the server logic independent of the
input fields:
- query factories (remote server, foreign data wrapper, foreign table)
to loop on fields and values without hardcoding the names of the fields
- adding stripe input and type
- add the logic to handle static schema. Simply creating a big object to
store into the server
Additional work:
- rename username field to user. This is the input intended for postgres
user mapping and we now need a matching by name
---------
Co-authored-by: Thomas Trompette <thomast@twenty.com>
## Context
Those settings are not implemented yet, we would like to move them to a
different page as well.
In the meantime, we are hiding them since we plan to launch calendar in
the next release and this won't be implemented before.
We will implement it in this
https://github.com/twentyhq/twenty/issues/5140
## Context
Currently, this middleware validates the token and stores the user,
workspace and cacheversion in the request object.
It only does so when a token is provided and ignores the middleware
logic if not. If the token is invalid or expired, the exception is
swallowed.
This PR removes the try/catch and adds an allowlist to skip the token
validation for operations executed while not signed-in.
I don't know a better way to do that with Nestjs. We can't easily add
the middleware per resolver without refactoring the flexible schema
engine so I'm doing it the other way around.
Fixes https://github.com/twentyhq/twenty/issues/5224
Added a smart Changelog :
- Publish the Changelog before the app release. If the release has not
yet been pushed to production, do not display it.
- When the app release is done, make the Changelog available with the
correct date.
- If the Changelog writing is delayed because the release has already
been made, publish it immediately.
- Display everything locally to be able to iterate on the changelog and
have a preview
Added an endpoint for the Changelog
---------
Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## Context
#4774
## How was it tested
Locally
## In further PRs
- Update connection status upon page change
- Adapt Info banner to dark mode
- placeholders for form
## Context
Currently we have an unicity constraint in the DB but we don't return a
clear error to the frontend before reaching the DB (which then throws a
500). This PR adds a validation check similar to what we have with field
creation
Closes#5097
- Uses "nx affected" to detect what projects need to be checked in the
current PR (for now, `ci-front` and `ci-server` workflows only).
- Caches results of certain tasks (`lint`, `typecheck`, `test`,
`storybook:build`) when a PR pipeline runs. The next runs of the same
PR's pipeline will then be able to reuse the PR's task cache to execute
tasks faster.
- Caches Yarn's cache folder to install dependencies faster in CI jobs.
- Rewrites the node modules cache/install steps as a custom, reusable
Github action.
- Distributes `ci-front` jobs with a "matrix" strategy.
- Sets common tasks config at the root `nx.json`. For instance, to
activate the `typecheck` task in a project, add `typecheck: {}` to its
`project.json` and it'll use the default config set in `nx.json` for the
`typecheck` task. Options can be overridden in each individual
`project.json` if needed.
- Adds "scope" tags to some projects: `scope:frontend`, `scope:backend`,
`scope:shared`. An eslint rule ensures that `scope:frontend` only
depends on `scope:frontent` or `scope:shared` projects, same for
`scope:backend`. These tags are used by `nx affected` to filter projects
by scope and generates different task cache keys according to the
requested scope.
- Enables checks for twenty-emails in the `ci-server` workflow.
Now that we have persistent cache for schemas, we want to be able to
reset its state when users run the database:reset db otherwise schemas
won't be synced with the new DB state.
Note: In an upcoming PR, we want to be able to invalidate the cache on a
workspace level when we change the metadata schema through twenty
version upgrade
## Context
Messaging and calendar cron jobs are only working for workspace that
have sub status different than incomplete, this is because currently
this is the simplest way to know if a user is onboarded. This should not
be the source of truth and this will be updated in a later version. In
the meantime, to make self-hosting easier, we are adding an extra check
on IS_BILLING_ENABLED env var since sub status is not relevant for
people not using billing.
We should not depend on the foreign data wrapper type to manage distant
table. The remote server should be enough to handle the table creation.
Here is the new flow to fetch available tables:
- check if the remote server have available tables already stored
- if not, import full schema in a temporary schema
- copy the tables into the available tables field
- delete the schema
Left todo:
- update remote server input for postgres so we receive the schema
---------
Co-authored-by: Thomas Trompette <thomast@twenty.com>
A search bar has been introduced in the filter dropdown menu however we
don't want to apply debounce since the search has no side-effect and is
purely FE (it's not querying the DB compared to other search bars). Also
adding autofocus on the search bar when the dropdown is open.
## Query depth deprecation
I'm deprecating depth parameter in our graphql query / cache tooling.
They were obsolete since we introduce the possibility to provide
RecordGqlFields
## Refactor combinedFindManyRecordHook
The hook can now take an array of operationSignatures
## Fix tasks issues
Fix optimistic rendering issue. Note that we still haven't handle
optimisticEffect on creation properly
## Introduction
This PR introduces "TwentyORM," a custom ORM module designed to
streamline database interactions within our workspace schema, reducing
the need for raw SQL queries. The API mirrors TypeORM's to provide a
familiar interface while integrating enhancements specific to our
project's needs.
To facilitate this integration, new decorators prefixed with `Workspace`
have been implemented. These decorators are used to define entity
metadata more explicitly and are critical in constructing our schema
dynamically.
## New Features
- **Custom ORM System**: Named "TwentyORM," which aligns closely with
TypeORM for ease of use but is tailored to our application's specific
requirements.
- **Decorator-Driven Configuration**: Entities are now configured with
`Workspace`-prefixed decorators that clearly define schema mappings and
relationships directly within the entity classes.
- **Injectable Repositories**: Repositories can be injected similarly to
TypeORM, allowing for flexible and straightforward data management.
## Example Implementations
### Decorated Entity Definitions
Entities are defined with new decorators that outline table and field
metadata, relationships, and constraints. Here are examples of these
implementations:
#### Company Metadata Object
```typescript
@WorkspaceObject({
standardId: STANDARD_OBJECT_IDS.company,
namePlural: 'companies',
labelSingular: 'Company',
labelPlural: 'Companies',
description: 'A company',
icon: 'IconBuildingSkyscraper',
})
export class CompanyObjectMetadata extends BaseObjectMetadata {
@WorkspaceField({
standardId: COMPANY_STANDARD_FIELD_IDS.name,
type: FieldMetadataType.TEXT,
label: 'Name',
description: 'The company name',
icon: 'IconBuildingSkyscraper',
})
name: string;
@WorkspaceField({
standardId: COMPANY_STANDARD_FIELD_IDS.xLink,
type: FieldMetadataType.LINK,
label: 'X',
description: 'The company Twitter/X account',
icon: 'IconBrandX',
})
@WorkspaceIsNullable()
xLink: LinkMetadata;
@WorkspaceField({
standardId: COMPANY_STANDARD_FIELD_IDS.position,
type: FieldMetadataType.POSITION,
label: 'Position',
description: 'Company record position',
icon: 'IconHierarchy2',
})
@WorkspaceIsSystem()
@WorkspaceIsNullable()
position: number;
@WorkspaceRelation({
standardId: COMPANY_STANDARD_FIELD_IDS.accountOwner,
label: 'Account Owner',
description: 'Your team member responsible for managing the company account',
type: RelationMetadataType.MANY_TO_ONE,
inverseSideTarget: () => WorkspaceMemberObjectMetadata,
inverseSideFieldKey: 'accountOwnerForCompanies',
onDelete: RelationOnDeleteAction.SET_NULL,
})
@WorkspaceIsNullable()
accountOwner: WorkspaceMemberObjectMetadata;
}
```
#### Workspace Member Metadata Object
```typescript
@WorkspaceObject({
standardId: STANDARD_OBJECT_IDS.workspaceMember,
namePlural: 'workspaceMembers',
labelSingular: 'Workspace Member',
labelPlural: 'Workspace Members',
description: 'A workspace member',
icon: 'IconUserCircle',
})
@WorkspaceIsSystem()
@WorkspaceIsNotAuditLogged()
export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
@WorkspaceField({
standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name,
type: FieldMetadataType.FULL_NAME,
label: 'Name',
description: 'Workspace member name',
icon: 'IconCircleUser',
})
name: FullNameMetadata;
@WorkspaceRelation({
standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.accountOwnerForCompanies,
label: 'Account Owner For Companies',
description: 'Account owner for companies',
icon: 'IconBriefcase',
type: RelationMetadataType.ONE_TO_MANY,
inverseSideTarget: () => CompanyObjectMetadata,
inverseSideFieldKey: 'accountOwner',
onDelete: RelationOnDeleteAction.SET_NULL,
})
accountOwnerForCompanies: Relation
<CompanyObjectMetadata[]>;
}
```
### Injectable Repository Usage
Repositories can be directly injected into services, allowing for
streamlined query operations:
```typescript
export class CompanyService {
constructor(
@InjectWorkspaceRepository(CompanyObjectMetadata)
private readonly companyObjectMetadataRepository: WorkspaceRepository<CompanyObjectMetadata>,
) {}
async companies(): Promise<CompanyObjectMetadata[]> {
// Example queries demonstrating simple and relation-loaded operations
const simpleCompanies = await this.companyObjectMetadataRepository.find({});
const companiesWithOwners = await this.companyObjectMetadataRepository.find({
relations: ['accountOwner'],
});
const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
where: { xLinkLabel: 'MyLabel' },
});
return companiesFilteredByLinkLabel;
}
}
```
## Conclusions
This PR sets the foundation for a decorator-driven ORM layer that
simplifies data interactions and supports complex entity relationships
while maintaining clean and manageable code architecture. This is not
finished yet, and should be extended.
All the standard objects needs to be migrated and all the module using
the old decorators too.
---------
Co-authored-by: Weiko <corentin@twenty.com>
Fixes#5168
- Added primaryInverted and primaryInvertedHover to design system.
- Changed primary button background with a gradient to inverted-flat for
both light and dark themes.
- Hover added to go lighter (consistent with tertiary color of +5 step
on GRAY_SCALE).
- Font color changed from primary to inverted.
- Modified button border from light to strong.
Two components are still utilizing the button with gradient background -
email and chrome extension.
Figma design guidelines show them to be inverted and flat (not
gradient).
- Should I change those as well?
- Should the gradient style be removed altogether after this has been
completed?
Co-authored-by: Henry Kim <henrykim@Henrys-iMac.local>
I have added error logging to Sentry using Sentry.captureException. The
_info object which includes the componentStack will be sent in extra
data along with the exception.
## Context
We recently enabled the option to bypass SSL certificate authority
validation when establishing a connection to PostgreSQL. Previously, if
this validation failed, the server would revert to unencrypted traffic.
Now, it maintains encryption even if the SSL certificate check fails. In
the process, we overlooked a few DataSource setups, prompting a review
of DataSource creation within our code.
## Current State
Our DataSource initialization is distributed as follows:
- **Database folder**: Contains 'core', 'metadata', and 'raw'
DataSources. The 'core' and 'metadata' DataSources manage migrations and
static resolver calls to the database. The 'raw' DataSource is utilized
in scripts and commands that require handling both aspects.
- **typeorm.service.ts script**: These DataSources facilitate
multi-schema connections.
## Vision for Discussion
- **SystemSchema (formerly core) DataSource**: Manages system schema
migrations and system resolvers/repos. The 'core' schema will be renamed
to 'system' as the Core API will include parts of the system and
workspace schemas.
- **MetadataSchema DataSource**: Handles metadata schema migrations and
metadata API resolvers/repos.
- **(Dynamic) WorkspaceSchema DataSource**: Will be used in the Twenty
ORM to access a specific workspace schema.
We currently do not support cross-schema joins, so maintaining these
DataSources separately should be feasible. Core API resolvers will
select the appropriate DataSource based on the field context.
- **To be discussed**: The potential need for an AdminDataSource (akin
to 'Raw'), which would be used in commands, setup scripts, and the admin
panel to connect to any database schema without loading any model. This
DataSource should be reserved for cases where utilizing metadata,
system, or workspace entities is impractical.
## In This PR
- Ensuring all existing DataSources are compliant with the SSL update.
- Introducing RawDataSource to eliminate the need for declaring new
DataSource() instances in commands.
## Context
@lucasbordeau introduced a new Yoga plugin that allows us to cache our
requests (👏), see https://github.com/twentyhq/twenty/pull/5189
I'm simply updating the implementation to allow us to use different
cache storage types such as redis
Also adding a check so it does not use cache for other operations than
ObjectMetadataItems
## Test
locally, first call takes 340ms, 2nd takes 30ms with 'redis' and 13ms
with 'memory'
In this PR I'm introducing a simple custom graphql-yoga plugin to create
a caching mechanism specific to our metadata.
The cache key is made of : workspace id + workspace cache version, with
this the cache is automatically invalidated each time a change is made
on the workspace metadata.
New strategy:
- add settings field on FieldMetadata. Contains a boolean isIdField and
for numbers, a precision
- if idField, the graphql scalar returned will be a GraphQL id. This
will allow the app to work even for ids that are not uuid
- remove globals dateScalar and numberScalar modes. These were not used
- set limit as Integer
- check manually in query runner mutations that we send a valid id
Todo left:
- remove WorkspaceBuildSchemaOptions since this is not used anymore.
Will do in another PR
---------
Co-authored-by: Thomas Trompette <thomast@twenty.com>
Co-authored-by: Weiko <corentin@twenty.com>
## Description
This PR adds recaptcha on login form. One can add any one of three
recaptcha vendor -
1. Google Recaptcha -
https://developers.google.com/recaptcha/docs/v3#programmatically_invoke_the_challenge
2. HCaptcha -
https://docs.hcaptcha.com/invisible#programmatically-invoke-the-challenge
3. Turnstile -
https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#execution-modes
### Issue
- #3546
### Environment variables -
1. `CAPTCHA_DRIVER` - `google-recaptcha` | `hcaptcha` | `turnstile`
2. `CAPTCHA_SITE_KEY` - site key
3. `CAPTCHA_SECRET_KEY` - secret key
### Engineering choices
1. If some of the above env variable provided, then, backend generates
an error -
<img width="990" alt="image"
src="https://github.com/twentyhq/twenty/assets/60139930/9fb00fab-9261-4ff3-b23e-2c2e06f1bf89">
Please note that login/signup form will keep working as expected.
2. I'm using a Captcha guard that intercepts the request. If
"captchaToken" is present in the body and all env is set, then, the
captcha token is verified by backend through the service.
3. One can use this guard on any resolver to protect it by the captcha.
4. On frontend, two hooks `useGenerateCaptchaToken` and
`useInsertCaptchaScript` is created. `useInsertCaptchaScript` adds the
respective captcha JS script on frontend. `useGenerateCaptchaToken`
returns a function that one can use to trigger captcha token generation
programatically. This allows one to generate token keeping recaptcha
invisible.
### Note
This PR contains some changes in unrelated files like indentation,
spacing, inverted comma etc. I ran "yarn nx fmt:fix twenty-front" and
"yarn nx lint twenty-front -- --fix".
### Screenshots
<img width="869" alt="image"
src="https://github.com/twentyhq/twenty/assets/60139930/a75f5677-9b66-47f7-9730-4ec916073f8c">
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
In this PR:
- Follow up on #5170 as we did not take into account not logged in users
- only apply throttler on root fields to avoid performance overhead
In this PR I'm introducing a new patch on @graphql-yoga/nestjs package.
This patch overrides a previous patch that was made to compute the
conditionnal schema on each request,
Here we use a cache map to compute only once per schema workspace cache
version.
This allows us to have sub 100ms query time.
Fixes#5161
<img width="179" alt="image"
src="https://github.com/twentyhq/twenty/assets/68029599/5a7a5b11-c6ec-4f1f-b94d-37be470a7f79">
detail: before this PR, the icon would display as normal for the default
data:// icons, but not for uploaded files
(`workspace-logo/original/{}.png` for example). This PR fixes this by
calling the getImageAbsoluteURIOrBase64 function to resolve the missing
path.
An error has been recently introduced in the sync of fieldMetadata. This
PR fixes it
Additionnally, we are enabling email for trialing and past_due
workspaces. There is an ongoing work to introduce a more robust
activationStatus on workspace.
While trying to migrate a workspace from 0.3.3 to 0.10.0, we've faced an
issue with the script to migrate default-values format.
This PR fixes it.
We really need to add tests on this part ;)
Previously we had to create a separate API key to give access to chrome
extension so we can make calls to the DB. This PR includes logic to
initiate a oauth flow with PKCE method which redirects to the
`Authorise` screen to give access to server tokens.
Implemented in this PR-
1. make `redirectUrl` a non-nullable parameter
2. Add `NODE_ENV` to environment variable service
3. new env variable `CHROME_EXTENSION_REDIRECT_URL` on server side
4. strict checks for redirectUrl
5. try catch blocks on utils db query methods
6. refactor Apollo Client to handle `unauthorized` condition
7. input field to enter server url (for self-hosting)
8. state to show user if its already connected
9. show error if oauth flow is cancelled by user
Follow up PR -
Renew token logic
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
I extracted the init database logic into its own file.
You can now run it with yarn database:init.
Added database entry for GitHub stars.
Do you want me to remove the init route or is it used for something else
?
---------
Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Added SEO to Contributors, Contributor, User Guide (+ each of it's
pages), Changelog, OSS friends: titles, descriptions
Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
Date picker UI was off because of the recent refactor with new field
types Date and DateTime. We had to allow the date picker to edit both.
In this PR we come back to the previous design and we only use the input
to modify time.
Also we use our Select component instead of the ones from the library
`react-datepicker`
---------
Co-authored-by: Weiko <corentin@twenty.com>
- Modified CSS: font-size, colors, margins...
- Added an ArticleContent Component that will be used for the changelog
and user guide articles, which contains the styles for each titles,
paragraphs, images etc.
- Added link to User Guide in footer
- Added a UserGuideWarning Component:
<img width="332" alt="Screenshot 2024-04-17 at 18 14 48"
src="https://github.com/twentyhq/twenty/assets/102751374/0f5c601b-a2c0-4c63-baeb-1990d65fc332">
- Added a UserGuideEditContent Component:
<img width="394" alt="Screenshot 2024-04-17 at 18 16 24"
src="https://github.com/twentyhq/twenty/assets/102751374/6c7c3bd4-c5bb-4d02-8f93-bd99bc30ce7b">
- Added a UserGuideLink Component (for usage in MDX to allow links to
open in new tab)
- Fixed table of content
- Made responsive
---------
Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## Context
As discussed with @lucasbordeau and @charlesBochet we are looking at
making low level UI components stateless when possible.
Therefore TextInput should not handle a hotkey state. Instead hotkeys
should be defined in the parent component (as done here in
CreateProfile).
Introducing here TextInputV2 that is stateless and that can already
replace TextInput without any behaviour change everywhere it is used
with `disableHotkey` prop.
## How was it tested?
Locally + Storybook
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
## Context
For users with many records, only the first n*60 records were loaded on
board views (n being the number of visible columns). This was because of
the following behavior:
- watch for end of column visibility changes. If an end of column is
visible, try to fetch more. However, watching for visbility changes is
not reliable enough.
## What we want
If an end of column is visible, try to fetch more. If no more records is
availble in pagination, do not fetch more
Refactored the code to introduce two different concepts:
- AuditLogs (immutable, raw data)
- TimelineActivities (user-friendly, transformed data)
Still some work needed:
- Add message, files, calendar events to timeline (~2 hours if done
naively)
- Refactor repository to try to abstract concept when we can (tbd, wait
for Twenty ORM)
- Introduce ability to display child timelines on parent timeline with
filtering (~2 days)
- Improve UI: add links to open note/task, improve diff display, etc
(half a day)
- Decide the path forward for Task vs Notes: either introduce a new
field type "Record Type" and start going into that direction ; or split
in two objects?
- Trigger updates when a field is changed (will be solved by real-time /
websockets: 2 weeks)
- Integrate behavioral events (1 day for POC, 1 week for
clean/documented)
<img width="1248" alt="Screenshot 2024-04-12 at 09 24 49"
src="https://github.com/twentyhq/twenty/assets/6399865/9428db1a-ab2b-492c-8b0b-d4d9a36e81fa">
This PR fixes#5024.
Local recoil state was not updated when workspace name is changed. This
PR updates local recoil state so that dropdown (and other parts of the
app) correctly shows the workspace name without reload.
When distant table does not have an id column, syncing does not work.
Today the check is only made after creating the foreign table locally.
We should do it first, so we avoid having a foreign table created and
failing right after.
Co-authored-by: Thomas Trompette <thomast@twenty.com>
Default currency logic was not handling a specific case where the
default currency is empty in the field metadata.
I fixed the ternary cascade and made it more explicit, thus also
avoiding falling into having an empty currency code being persisted.
Improved table cell performances by putting all hooks from
RecordTableCell in RecordTableContext and RecordTable component, so that
each cell now only subscribes to a reference to those hooks' returned
function.
We couldn't do memoization here since the problem is not to memoize
between re-renders but to share the same function reference between
hundreds of different components, so a context it the fastest way for
this.
I had to refactor the hooks a little bit so that they take as arguments
what was previously taken from the cell's context.
[In a previous PR](https://github.com/twentyhq/twenty/pull/5008) I was
fixing dark mode by calling useTheme in AppErrorBoundary while there was
actually no parent ThemeProvider. This was causing a bug when an error
was actually intercepted by AppErrorBoundary because theme was empty.
Now I am providing the error theme in GenericErrorFallback, fallbacking
to THEME_LIGHT as it can be called from outside a ThemeProvider (as it
is the case today), but also reading into ThemeProvider in case we end
up using this component in a part of the application where it is
available, not to necessarily use THEME_LIGHT.
## How was it tested?
with @thaisguigon
Locally (dark mode works + error mode works (throwing an error in
RecoilDebugObserver))
# This PR
- Moves dev and ci scripts to the `project.json` file in the
twenty-front package
- Adds a project.json file in the root of the project with the main
start command that start both twenty-server and twenty-front
applications concurrently
- Updates the script command of the root project with the start:prod
command (replacing the start command which will be used in dev with the
help of nx)
- Add a start:prod command in the twenty-front app, replacing the start
command (now used for dev purpose)
Issue ref #4645
@charlesBochet @FelixMalfait please let me know how can I improve it
---------
Co-authored-by: Thaïs Guigon <guigon.thais@gmail.com>
## Context
We have recently added an event listener to create audit logs on objects
update. However, we have only created the structure (relations on event
standard objects) for Company, Person, Opportunity and custom objects.
There is a larger effort in #4936 to refactor this.
For now, we are disabling log auditing on all other objects
## How
Add @IsNotAuditLogged() annotation on all standard objects except
Company, Person, Opportunity
## Context
We recently introduced this verification but we didn't take into account
self-hosting that might not use billing.
## Test
tested locally with
- new workspace and new account
- existing workspace with new account and billing not enabled and status
incomplete => OK
- existing workspace with new account and billing enabled and status
incomplete => NOK
- existing workspace with new account and billing enabled and status
active => OK
Unfortunately, it is not possible in CSS to have an overflow:visible
over x-axis while having an overflow:hidden over y-axis, leading to the
following issue:
<img width="1512" alt="image"
src="https://github.com/twentyhq/twenty/assets/12035771/9b84cbbb-c6c4-4fd6-a630-a24f01eccf73">
I'm refactoring the RecordInlineCell and RecordTableCell to use
useFloating + createPortal to open the cell.
We will require remote table entity to map distant table name and local
foreign table name.
Introducing the entity:
- new source of truth to know if a table is sync or not
- created synchronously at the same time as metadata and foreign table
Adding a few more changes:
- exception rather than errors so the user can see these
- `pluralize` library that will allow to stop adding `Remote` suffix on
names
---------
Co-authored-by: Thomas Trompette <thomast@twenty.com>
## Context
Fixes#4808
TL;DR
Introducing pure stateless modal component ("UI modal") for our auth
modal not to have default hotkeyScope overriding our create-profile
hotkeyScope
+ we dont want the shortcut to be available for all the modal content, only for the input that should not be using a hotkeyscope, so we are using onKeyDown for the specific issue on create profile.
Explanation
create-profile hotkey scope is set by PageChangeEffect; CreateProfile
component adds enter key shortcut; but this scope is overwritten by the
default scope by the Modal component that expects a hotkeyScope to reset
to (and defaults to the default hotkeyScope if none indicated).
In the auth flow we were using that Modal component to give a modal look
to the flow but it is not a modal per say, it's a set of pages contained
within a modal look.
By creating this UI component we are escaping that hotkeyScope
overriding that does not make sense in our context.
## How was it tested
Locally
Storybook
## Context
- Rename remaining V2 services.
- Delete messages in DB when gmail history tells us they've been
deleted. I removed the logic where we store those in a cache since it's
a bit overkill because we don't need to query gmail and can use those
ids directly. The strategy is to delete the message channel message
association of the current channel, not the message or the thread since
they can still be linked to other channels. However, we will need to
call the threadCleaner service on the workspace to remove orphan
threads/non-associated messages.
Note: deletion for full-sync is a bit tricky because we need the full
list of message ids to compare with the DB and make sure we don't
over-delete. Currently, to keep memory, we don't have a variable that
holds all ids as we flush it after each page. Easier solution would be
to wipe everything before each full sync but it's probably not great for
the user experience if they are currently manipulating messages since
full-sync can happen without a user intervention (if a partial sync
fails due to historyId being invalidated by google for some reason)
- Implemented correct mask for Date and DateTime field in
InternalDatePicker
- Use only keyDown event and click outside in InternalDatePicker and
DateInput
- Refactored InternalDatePicker UI to have month and year displayed
- Fixed bug and synchronized date value between the different inputs
that can change it
---------
Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>
## Context
The full-sync job was enqueued within a transaction, which means it
could be executed before the transaction was commit and
connected-account was not created yet.
This PR re-arrange the code a bit to avoid this
cc @bosiraphael thx for flagging this!
## Context
Recent PR introduced a verifyTransientToken inside the
GoogleAPIsProviderEnabledGuard guard. This is used to extract the
workspaceId from the token. This is working fine for the first call sent
to google however the callback is calling the same guard which is
causing an issue because the transientToken is missing from the
callback.
Imho, the same guard shouldn't be used by the callback but for the time
being I'm adding a check to prevent using feature flag when
transientToken is absent. In fact, it is present in the request but not
in the same key. Because the scope is only relevant for the first call,
I'm simply adding a check there.
## Context
Currently the calendar scope is bound to an env variable. We want to
rollout this feature to some users so this PR adds a check on the
existing IS_CALENDAR_ENABLED flag
closes#4714
We cannot set null expiration dates for api keys. So we will set to 100
years instead of null. If apiKey expires in more that 10 years, it is
displayed as "Never expires"
## Context
With the addition of cronjobs, the app is building a lot of jobs and
stores them indefinitely. There is no real point to keep all of them in
the queue once they have been processed (completed or failed) so we are
adding a new default option to the bull-mq driver
## Implementation
See bull-mq JobsOption doc
```typescript
/**
* If true, removes the job when it successfully completes
* When given a number, it specifies the maximum amount of
* jobs to keep, or you can provide an object specifying max
* age and/or count to keep. It overrides whatever setting is used in the worker.
* Default behavior is to keep the job in the completed set.
*/
removeOnComplete?: boolean | number | KeepJobs;
/**
* If true, removes the job when it fails after all attempts.
* When given a number, it specifies the maximum amount of
* jobs to keep, or you can provide an object specifying max
* age and/or count to keep. It overrides whatever setting is used in the worker.
* Default behavior is to keep the job in the failed set.
*/
removeOnFail?: boolean | number | KeepJobs;
```
removeOnFail should be a bit higher since they are the ones we are most
likely looking at when needed.
1/ When the user inputs wrong connection informations, we do not inform
him. He will only see that no tables are available.
We will display a connection failed status if an error is raised testing
the connection
2/ If the connection fails, it should still be possible to delete the
server. Today, since we try first to delete the tables, the connection
failure throws an error that will prevent server deletion. Using the
foreign tables instead of calling the distant DB.
3/ Redirect to connection show page instead of connection list after
creation
4/ Today, foreign tables are fetched without the server name. This is a
mistake because we need to know which foreign table is linked with which
server. Updating the associated query.
<img width="632" alt="Capture d’écran 2024-04-12 à 10 52 49"
src="https://github.com/twentyhq/twenty/assets/22936103/9e8406b8-75d0-494c-ac1f-5e9fa7100f5c">
---------
Co-authored-by: Thomas Trompette <thomast@twenty.com>
The authorization token has an expiry of 5 minutes, we already have
checks in place to verify this and throw a Forbidden exception. We need
to revoke the token once it's used otherwise it could be used multiple
times to gain access to tokens till it expires.
This PR changes the vite config to enable the generation of sourcemaps
with the help of an Env.
It also adds a new script to run the build with the said env as well as
more memory (the dafault 2go leads to an OOM)
Experiment using swc instead of tsc (as we did the switch on
twenty-front)
It's **much** faster (at least 5x) but has stricter requirements.
I fixed the build but there's still an error while starting the server,
opening this PR for discussion.
Checkout the branch and try `nx build:swc twenty-server`
Read: https://docs.nestjs.com/recipes/swc#common-pitfalls
close#4925
Before, for google-auth, if the user exists, we would simply returns a
login token, without checking the InvitationLink
Now, we just call the `authService.signUp` function that handle all
use-cases for us (user exists or not, invitationLink exists or not)
Added isAuditLogged column to object-metadata-entity.ts
This is my first open source pull request. Please do let me know if made
any mistake. I will be greatfull. Thank u
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This PR introduces a new folder structure for business modules.
Cron commands and jobs are now stored within the same module/folder at
the root of the business module
e.g: /modules/messaging/crons/commands instead of
/modules/messaging/commands/crons
Patterns are now inside their own cron-command files since they don't
need to be exported
Ideally cronJobs and cronCommands should have their logic within the
same class but it's a bit harder than expected due to how commanderjs
and our worker need both some class heritage check, hence the first
approach is to move them in the same folder
Also Messaging fullsync/partialsync V2 has been dropped since this is
the only used version => Breaking change for ongoing jobs and crons.
Jobs can be dropped but we will need to re-run our crons (only
cron:messaging:gmail-fetch-messages-from-cache)
In the previous PR #4912 it seems that I forgot to pass the environment
on the backend.
Here is a quick fix!
I also added some "doc" in the the .env.example
- Adjusted the height of the background image to fit every possible
screen
- Added the proper rotation degree to the background
- Refactored gradient colour stop into CONST to make sure each
background image starts at the same level
**To be noted :**
I had to use Javascript and useEffect to make the div take the entire
height of the page (turn it into a "use client" component). This was
necessary because absolute positioning by default makes the element's
sizing relative to its nearest positioned ancestor, not the entire
document.
---------
Co-authored-by: Ady Beraud <a.beraud96@gmail.com>