bc28e1557c2a40a4628cd2e7ecb576d3e8913aae
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bc28e1557c |
Introduce updateWorkspaceMemberSettings and clarify product (#19441)
## Summary Introduces a dedicated **metadata** mutation to update **standard (non-custom)** workspace member settings, moves profile-related UI to use it, and aligns **workspace member** record permissions with the rest of the CRM so users cannot escalate visibility via RLS by editing their own member record. ## Product behaviour ### Profile and appearance (standard fields) - Users can still update **their own** standard workspace member fields that the product exposes in **Settings / Profile** (e.g. name, locale, color scheme, avatar flow) via the new **`updateWorkspaceMemberSettings`** mutation. - The mutation returns a **boolean**; the app **merges** the updated fields into local state so the UI stays in sync without refetching the full workspace member record. - **Locale** changes also keep **`userWorkspace`** in sync when a locale is present in the payload (including from the workspace `updateOne` path when applicable). ### Custom fields on workspace members - The dedicated metadata mutation **rejects** any **custom** workspace member field (and unknown keys). Those updates must go through the normal **object** `updateOne` pipeline, which is subject to **object- and field-level** permissions like other records. But since we don't have object- and field-level permission configuration for system objects yet, this permission is derived from Workspace member settings permission. - **Workspace member** is no longer exempt from ORM permission validation for updates merely because it is a **system** object. Users who **do not** have workspace member access (e.g. no **Workspace members** settings permission and no equivalent broad settings access on the role) **cannot** use `updateOne` on `workspaceMember` to change **custom** (or other) fields on their own row—even though that row is used for RLS predicates. - This closes a path where someone could widen what they can see by writing to fields that drive row-level rules. ### Who can change another member - Updating **another** user’s workspace member still requires **Workspace members** (or equivalent) settings permission, consistent with admin tooling. |
||
|
|
1cb4c98cb3 |
Add dataloader and read from cache for view entities (#18594)
## Context Improve view resolution using cache and dataloader ## Performance Comparison |Run|Main (no DataLoaders/cache)|Feature Branch (DataLoaders + cache)|Speedup| |---|---|---|---| |1 (cold)|418ms|95ms|~4.4x faster| |2|42ms|19ms|~2.2x faster| |3|37ms|19ms|~1.9x faster| |4|39ms|12ms|~3.2x faster| |5|33ms|13ms|~2.5x faster| The biggest improvement is to use dataloaders for the multiple relations associated with views. Cache is a bit less significant since there are other cache mechanism such as PostgreSQL buffer cache but it will probably be more meaningful with bigger workspaces |
||
|
|
8e6b267ff3 |
Allow DATE_TIME IS operand to filter on a whole day (#17529)
Fixes: https://github.com/twentyhq/core-team-issues/issues/2027 We've replaced the DATE_TIME picker with DATE picker, and changed the logic to filter for complete day period. https://github.com/user-attachments/assets/ba7e1078-bab3-4c62-a803-d6a851f14b7d --------- Co-authored-by: Arun kumar <arunkumar@Aruns-MacBook-Air.local> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> |
||
|
|
b4e924b671 |
Sync views and navigation items (#18003)
Both objects are necessary to fully enjoy objects within applications <img width="770" height="311" alt="Capture d’écran 2026-02-17 à 15 19 43" src="https://github.com/user-attachments/assets/48c51fa4-63f4-45b2-a40a-df73f3aa79be" /> |
||
|
|
476bdf764c |
Refactor flat entity maps to be universal oriented (#17665)
# Introduction
In preparation of the workspace agnostic builder, we're migrating
`FlatEntityMaps` to be universal identifier oriented and based
As in the builder context there're won't be any ids at all
Please also note that the FlatEntity is a UniversalFlatEntity superset
From
```ts
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
export type FlatEntityMaps<T extends SyncableFlatEntity> = {
byId: Partial<Record<string, T>>;
idByUniversalIdentifier: Partial<Record<string, string>>;
universalIdentifiersByApplicationId: Partial<Record<string, string[]>>;
};
```
To
```ts
export type FlatEntityMaps<
T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
> = {
byUniversalIdentifier: Partial<Record<string, T>>;
universalIdentifierById: Partial<Record<string, string>>;
universalIdentifiersByApplicationId: Partial<Record<string, string[]>>; // this might make more sense to be migrated to universalIdentifiersByApplicationUniversalIdentifier but it's the main topic of this PR
};
```
## Low level maps tools
Had to refactor find | create | delete | replace | find-many | get-sub
tools ( through mutations and or throw equivalent )
|
||
|
|
0b5be7caa3 |
Refactored Date to Temporal in critical date zones (#16544)
Fixes https://github.com/twentyhq/twenty/issues/16110 This PR implements Temporal to replace the legacy Date object, in all features that are time zone sensitive. (around 80% of the app) Here we define a few utils to handle Temporal primitives and obtain an easier DX for timezone manipulation, front end and back end. This PR deactivates the usage of timezone from the graph configuration, because for now it's always UTC and is not really relevant, let's handle that later. Workflows code and backend only code that don't take user input are using UTC time zone, the affected utils have not been refactored yet because this PR is big enough. # New way of filtering on date intervals As we'll progressively rollup Temporal everywhere in the codebase and remove `Date` JS object everywhere possible, we'll use the way to filter that is recommended by Temporal. This way of filtering on date intervals involves half-open intervals, and is the preferred way to avoid edge-cases with DST and smallest time increment edge-case. ## Filtering endOfX with DST edge-cases Some day-light save time shifts involve having no existing hour, or even day on certain days, for example Samoa Islands have no 30th of December 2011 : https://www.timeanddate.com/news/time/samoa-dateline.html, it jumps from 29th to 31st, so filtering on `< next period start` makes it easier to let the date library handle the strict inferior comparison, than filtering on `≤ end of period` and trying to compute manually the end of the period. For example for Samoa Islands, is end of day `2011-12-29T23:59:59.999` or is it `2011-12-30T23:59:59.999` ? If you say I don't need to know and compute it, because I want everything strictly before `2011-12-29T00:00:00 + start of next day (according to the library which knows those edge-cases)`, then you have a 100% deterministic way of computing date intervals in any timezone, for any day of any year. Of course the Samoa example is an extreme one, but more common ones involve DST shifts of 1 hour, which are still problematic on certain days of the year. ## Computing the exact _end of period_ Having an open interval filtering, with `[included - included]` instead of half-open `[included - excluded)`, forces to compute the open end of an interval, which often involves taking an arbitrary unit like minute, second, microsecond or nanosecond, which will lead to edge-case of unhandled values. For example, let's say my code computes endOfDay by setting the time to `23:59:59.999`, if another library, API, or anything else, ends up giving me a date-time with another time precision `23:59:59.999999999` (down to the nanosecond), then this date-time will be filtered out, while it should not. The good deterministic way to avoid 100% of those complex bugs is to create a half-open filter : `≥ start of period` to `< start of next period` For example : `≥ 2025-01-01T00:00:00` to `< 2025-01-02T00:00:00` instead of `≥ 2025-01-01T00:00:00` to `≤ 2025-01-01T23:59:59.999` Because, `2025-01-01T00:00:00` = `2025-01-01T00:00:00.000` = `2025-01-01T00:00:00.000000` = `2025-01-01T00:00:00.000000000` => no risk of error in computing start of period But `2025-01-01T23:59:59` ≠ `2025-01-01T23:59:59.999` ≠ `2025-01-01T23:59:59.999999` ≠ `2025-01-01T23:59:59.999999999` => existing risk of error in computing end of period This is why an half-open interval has no risk of error in computing a date-time interval filter. Here is a link to this debate : https://github.com/tc39/proposal-temporal/issues/2568 > For this reason, we recommend not calculating the exact nanosecond at the end of the day if it's not absolutely necessary. For example, if it's needed for <= comparisons, we recommend just changing the comparison code. So instead of <= zdtEndOfDay your code could be < zdtStartOfNextDay which is easier to calculate and not subject to the issue of not knowing which unit is the right one. > > [Justin Grant](https://github.com/justingrant), top contributor of Temporal ## Application to our codebase Applying this half-open filtering paradigm to our codebase means we would have to rename `IS_AFTER` to `IS_AFTER_OR_EQUAL` and to keep `IS_BEFORE` (or even `IS_STRICTLY_BEFORE`) to make this half-open interval self-explanatory everywhere in the codebase, this will avoid any confusion. See the relevant issue : https://github.com/twentyhq/core-team-issues/issues/2010 In the mean time, we'll keep this operand and add this semantic in the naming everywhere possible. ## Example with a different user timezone Example on a graph grouped by week in timezone Pacific/Samoa, on a computer running on Europe/Paris : <img width="342" height="511" alt="image" src="https://github.com/user-attachments/assets/9e7d5121-ecc4-4233-835b-f59293fbd8c8" /> Then the associated data in the table view, with our **half-open date-time filter** : <img width="804" height="262" alt="image" src="https://github.com/user-attachments/assets/28efe1d7-d2fc-4aec-b521-bada7f980447" /> And the associated SQL query result to see how DATE_TRUNC in Postgres applies its internal start of week logic : <img width="709" height="220" alt="image" src="https://github.com/user-attachments/assets/4d0542e1-eaae-4b4b-afa9-5005f48ffdca" /> The associated SQL query without parameters to test in your SQL client : ```SQL SELECT "opportunity"."closeDate" as "close_date", TO_CHAR(DATE_TRUNC('week', "opportunity"."closeDate", 'Pacific/Samoa') AT TIME ZONE 'Pacific/Samoa', 'YYYY-MM-DD') AS "DATE_TRUNC by week start in timezone Pacific/Samoa", "opportunity"."name" FROM "workspace_1wgvd1injqtife6y4rvfbu3h5"."opportunity" "opportunity" ORDER BY "opportunity"."closeDate" ASC NULLS LAST ``` # Date picker simplification (not in this PR) Our DatePicker component, which is wrapping `react-datepicker` library component, is now exposing plain dates as string instead of Date object. The Date object is still used internally to manage the library component, but since the date picker calendar is only manipulating plain dates, there is no need to add timezone management to it, and no need to expose a handleChange with Date object. The timezone management relies on date time inputs now. The modification has been made in a previous PR : https://github.com/twentyhq/twenty/issues/15377 but it's good to reference it here. # Calendar feature refactor Calendar feature has been refactored to rely on Temporal.PlainDate as much as possible, while leaving some date-fns utils to avoid re-coding them. Since the trick is to use utils to convert back and from Date object in exec env reliably, we can do it everywhere we need to interface legacy Date object utils and Temporal related code. ## TimeZone is now shown on Calendar : <img width="894" height="958" alt="image" src="https://github.com/user-attachments/assets/231f8107-fad6-4786-b532-456692c20f1d" /> ## Month picker has been refactored <img width="503" height="266" alt="image" src="https://github.com/user-attachments/assets/cb90bc34-6c4d-436d-93bc-4b6fb00de7f5" /> Since the days weren't useful, the picker has been refactored to remove the days. # Miscellaneous - Fixed a bug with drag and drop edge-case with 2 items in a list. # Improvements ## Lots of chained operations It would be nice to create small utils to avoid repeated chained operations, but that is how Temporal is designed, a very small set of primitive operations that allow to compose everything needed. Maybe we'll have wrappers on top of Temporal in the coming years. ## Creation of Temporal objects is throwing errors If the input is badly formatted Temporal will throw, we might want to adopt a global strategy to avoid that. Example : ```ts const newPlainDate = Temporal.PlainDate.from('bad-string'); // Will throw ``` |
||
|
|
3cea19baf4 |
feat(ai): add view management tools for AI chat (#16495)
## Summary Adds a new **VIEW** tool category for the AI chat, enabling it to work with views: - **get-views**: List views in the workspace, optionally filtered by object metadata ID - **get-view-query-parameters**: Convert a view's filters and sorts into GraphQL query parameters that can be passed to existing `find_*` data tools - **create-view**, **update-view**, **delete-view**: CRUD operations for view management ### Key design decisions 1. **No pagination duplication**: Instead of creating a `find-records-from-view` tool that would duplicate pagination logic, `get-view-query-parameters` returns filter/sort parameters that the AI can pass to existing record-fetching tools. 2. **Permission model**: - Read tools (get-views, get-view-query-parameters) are available to all users - Write tools require the `VIEW` permission - UNLISTED views can only be modified by their creator 3. **Leverages existing utilities**: Uses `computeRecordGqlOperationFilter` from `twenty-shared` for filter conversion. ### Files changed - Added `ViewToolProvider`, `ViewToolsFactory`, and `ViewQueryParamsService` - Added `VIEW` to `ToolCategory` enum and tool registry - Updated `chat-execution.service.ts` to include view tools in the catalog and pass viewId in browsing context - Extracted shared `formatValidationErrors` utility to reduce duplication ## Test plan - [x] Unit tests for `ViewToolsFactory` - [x] Unit tests for `ViewQueryParamsService` - [x] Lint and typecheck pass |