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>
The new date time formatting util made for performance optimization
missed two things :
- Padding 0 for hours and minutes with 1 digit only.
- Correctly parsing the day of the month (now uses JS Date native
getDate() instead of slicing the ISO String)
In the settings part of the app, where display fields are used as in
table cell and board cards, we didn't have the new context selector
logic implemented, due to the recent performance optimization.
- make invitation and reset password available on every page
- add a sleep after setKeyPair as tokens are sometimes not updated when
redirecting to Index
- refactor sleep
Issue: #5761
Changes:
- Use `useFindManyRecords` in `RecordTableWithWrappers.tsx` to determine
if any records exist for that object
- Add `hasUnfilteredRecords` prop to `RecordTableEmptyState.tsx`.
This changes to empty state title, but I'm guessing that we'll need to
change the button text and subheading as well you guys can let me know
what you think. If this works I can go on to do those next, thanks!
---------
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
In RecordTableCellContainer, I just removed onMouseEnter event handler
that was being triggered when we used keyboard soft focus move.
It's not necessary to have it because we already listen on mouse move
which is matching our use case where we only want soft focus to move
when mouse move and not when the cursor stays on top of a cell.
- Improve the rest api by introducing startingAfter/endingBefore (we
previously had lastCursor), and moving pageInfo/totalCount outside of
the data object.
- Fix broken GraphQL playground on website
- Improve analytics by sending server url
Add a new util called `resolveAbsolutePath` to allow providing absolute
path for environment variable like `STORAGE_LOCAL_PATH`.
If the path in the env start with `/` we'll not prefix it with
`process.cwd()`.
Also we're using a static path for the old `db_initialized` file now
named `db_status` and stop using the env variable for this file as this
one shouldn't ne stored in the `STORAGE_LOCAL_PATH`.
Fix#4794
---------
Co-authored-by: Quentin Galliano <qgalliano@gmail.com>
- Added Linaria to have compiled CSS on our optimized field displays
- Refactored mocks for performance stories on fields
- Refactored generateRecordChipData into a global context, computed only
when we fetch object metadata items.
- Refactored ChipFieldDisplay
- Refactored PhoneFieldDisplay
In `messaging-gmail-messages-import.service`, we were refreshing the
access token before each query but we were passing the old access token
to `fetchAllMessages`.
I modified the function to query the updated connectedAccount with the
new access token.
This will solve the 401 errors we were getting in production.
I increased the inline relation of the relations fields, now the edit
pen is visible when hovering the icon and not only the label.
this aims to fix: #5662
Made the alignment consistent with the field panel. This uses 90px as
the key label width.
**Issue:** #5730
**Changes:**
- Add a label width of 90 to FieldContext Provider in useFieldContext
function
- Add a label width of 90 to ActivityTargetsInlineCell component
**Screen recording form local testing:**
https://github.com/twentyhq/twenty/assets/120792086/e150530b-4163-4a69-9bd5-119a2f202d4f
---------
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
In this PR, we implement the display and update of fields from
fromManyObjects (e.g update Employees for a Company).
Product requirement
- update should be triggered at each box check/uncheck, not at lose of
focus
Left to do in upcoming PRs
- add the column in the table views (e.g. column "Employees" on
"Companies" table view)
- add "Add new" possibility when there is no records (as is currently
exists for "one" side of relations:)
<img width="374" alt="Capture d’écran 2024-06-10 à 17 38 02"
src="https://github.com/twentyhq/twenty/assets/51697796/6f0cc494-e44f-4620-a762-d7b438951eec">
- update cache after an update affecting other records (e.g "Listings"
have one "Person"; if listing A belonged to Person A but then we
attribute listing A to Person B, Person A is no longer owner of Listing
A. For the moment that would not be reflected immediatly leading, to
potential false information if information is accessed from cache)
- try to get rid of the glitch - we also have it on the task page
example. (probably) due to the fact that we are using a recoil state to
read, update then re-read
https://github.com/twentyhq/twenty/assets/51697796/54f71674-237a-4946-866e-b8d96353c458
* Remove relations where they cannot be used
* Removed duplicated schema for findMany
* Reuse schema for Relation variant to reduce size of sent json object
closes#5778
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: martmull <martmull@hotmail.fr>
Now while pressing the `Enter` button, the select field selects the
relevant option.
- Added a `handleKeyDown` function to set the `persistField` with the
selected option.
- Added an `onKeyDown` event on `DropdownMenuSearchInput` component, to
trigger `handleKeyDown` when `Enter` is pressed.
- Fixes: #5556
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
- Fixes#5504
- Fixes#5503
- Return 404 when the page does not exist
- Modified the footer in order to align it properly
- Removed "noticed something to change" in each table of content
- Fixed the URLs of the edit module
- Added the edit module to Developers
- Fixed header style on the REST API page.
- Edited the README to point to Developers
- Fixed selected state when clicking on sidebar elements
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
In this PR, I'm doing 2 things:
- refresh connectedAccount token on message-list-fetch. It's currently
only refresh while doing the messages-import. However messages-import
stage are only triggered if new messages are detected (which could take
days or week depending of the messageChannel activity). We should also
refresh it while trying to fetch the list
- handle Unhandled Gmail error code 500 with reason "backendError".
These can occur on gmail side. In this case, we just retry later.
In this PR, I'm mainly doing two things:
- uniformizing messaging-messages-import and
messaging-message-list-fetch behaviors (cron.job and job)
- improving performances of these cron.jobs by not triggering the jobs
if the stage is not relevant
- making sure these jobs have same signature (workspaceId +
messageChannelId)
# This PR
- Fixes#5520
- Created a shared confirmation modal component for the `ContextMenu`
and the `ActionBar` components to avoid code repetition - with its
storybook file
Looking forward to getting feedback @charlesBochet
I changed the visibility of the search dialog to make it full screen on
mobile, this should already be ok but I couldn't try it on mobile, so I
just used devtools, if I need to do something else on this PR just tell
me :)
This PR aims to fix: #5746
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
First step for creating credentials for database proxy.
In next PRs:
- When calling endpoint, create database Postgres on proxy server
- Setup user on database using postgresCredentials
- Build remote server on DB to access workspace data
I changed the Sort button used in the Header using
StyledHeaderDropdownButton component (the same used for Filter and
Options') instead of LightButton.
This PR aims to fix the issue: #5743
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## Context
Fixing `setIsFocused is not a function` and the fact that edit buttons
were not showing up anymore.
A new FieldFocusContextProvider has been introduced and added to
RecordInlineCell but not ActivityTargetsInlineCell. This should fix the
issue.
<img width="523" alt="Screenshot 2024-06-05 at 17 42 07"
src="https://github.com/twentyhq/twenty/assets/1834158/1c1f919e-3829-4e40-b573-3b1b75b7c16f">
This is a small PR to improve the design of our CSV import.
I noticed the back button that was implemented in a recent PR #5625 was
broken and would need to be fixed (e.g. try to come back to the very
first upload step from the sheet selection step). cc @shashankvish0010
if you want to give a stab at fixing your PR that'd be amazing, thanks!
Update of select fields options was failing if we deleted an option that
was used for at least one row: former code would not update the value to
null but leave it to the no-longer-allowed value.
- Rename syncSubStatus to syncStage
- Rename ongoingSyncStartedAt to syncStageStartedAt
- Remove throttlePauseUntil from db and compute it with
syncStageStartedAt and throttleFailureCount
- Fix duplicate view field creation
- Fix redirect to proper settings data model page
- Refetch view fields after field creation (temporary solution)
Fixes https://github.com/twentyhq/twenty/issues/5598
- Removing existing listener that was backfilling created records
without position
- Switch to a job that backfill all objects within workspace
- Adapting `FIND_BY_POSITION` so it can fetch objects without position.
Currently we needed to input a number
Fix issue introduced in https://github.com/twentyhq/twenty/pull/5426
It's not a beautiful solution. Maybe one day we should have a dedicated
component for title but it also comes with downsides (lot of code to
copy paste, such as "esc" to leave field, copy button, etc.). This one
doesn't create less debt in my opinion. Once we have the layout/widget
system we might have a dedicated widget type and the right abstraction
layers
**Changes:**
- Changed -/+ to eye and eye off icons
- Changed menu width to 200px
- Created separate menu for hidden fields
- Added Edit Fields option to hidden fields menu
- Added test file MenuItemSelectTag (wasn't included in the issue)
As this is my first pr, feedback is very welcome!
**Note:**
These changes cover most of #4363 . I left out the implementation of the
RightIcon in the "Hidden Fields" menu item.
---------
Co-authored-by: kiridarivaki <k.darivaki03@gmail.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This PR introduces a new side panel to edit records and the ability to
minimize the side panel.
The goal is leverage this sidepanel to be able to create records while
being in another show page.
I'm opening the PR for feedback since it involved refactoring and
therefore already touches a lot of files, even though it was quick to
implement.
<img width="1503" alt="Screenshot 2024-05-23 at 17 41 37"
src="https://github.com/twentyhq/twenty/assets/6399865/6f17e7a8-f4e9-4eb4-b392-c756db7198ac">
- fix : #5521
When we deleted an opportunity that had been added to the favorites
list, the opportunity was removed correctly, but it still remained in
the favorites list. The issue was due to not accounting for the removal
of the opportunity from the favorites during the deletion process.
This problem has now been fixed :
https://github.com/twentyhq/twenty/assets/78202522/3d3cb689-3228-43fc-bf50-e824370582a7
Co-authored-by: Jeff Gasparini <jeff@Jeff.local>
Now all the required fields are displayed with the respective labels.
- Added a `FieldContextProvider` for the field `Reminder` in the
`ActivityEditorFields`.
- Fixed the missing label values, by adding a missed optional
`showLabel` within the `fieldDefinition` in the `useFieldContext`.
fixes: #5667
![Screenshot
(342)](https://github.com/twentyhq/twenty/assets/140178357/adf9563a-6cab-4809-8616-1c256abab717)
- refactor record position factory and record position query factory
- override position if not present during createMany
To avoid overriding the same positions for all data in createMany, the
logic is:
- if inserted last, use last position + arg index + 1
- if inserted first, use first position - arg index - 1
Now the fields don't disappear on drag and drop.
- After reviewing the codebase, I checked that when `inView` is true the
`RecordInlineCell` is rendered otherwise the
`StyledRecordInlineCellPlaceholder` will render which causes the fields
get disappear.
- So, I added the condition to check if `isDragSelectionStartEnabled` is
false then `StyledRecordInlineCellPlaceholder` will be rendered
otherwise `RecordInlineCell`.
fixes: #5651https://github.com/twentyhq/twenty/assets/140178357/022195ca-fec2-43a7-8808-f4974dbe66cf
---------
Co-authored-by: martmull <martmull@hotmail.fr>