Commit Graph
8651 Commits
Author SHA1 Message Date
7f3af243c7 [Hacktoberfest] AI-Powered Meeting Transcript Analysis Extension for Twenty CRM (#15507)
# 🧠 AI-Powered Meeting Transcript to CRM Data Integration

## **Overview**
This feature automatically transforms meeting transcripts into
structured CRM data using AI.
When unstructured meeting notes are received via a **webhook**, the
system processes them and creates organized **notes, tasks, and
assignments** directly in **Twenty CRM**.

---

## **Key Features**

- **🤖 AI-Powered Analysis:**  
Extracts **summaries, action items, assignees, and due dates** from
natural language transcripts.

- **📋 Smart Task Consolidation:**  
  Merges related sub-tasks into unified deliverables  
  *(e.g., `"draft" + "review" + "present"` → one consolidated task).*

- **👥 Intelligent Assignment:**  
Uses **GraphQL member lookup** to match extracted assignee names to
workspace member IDs with flexible string matching.

- **🔗 Automatic Linking:**  
Links generated **notes and tasks** to relevant contacts using
`noteTargets` and `taskTargets`.

- **🗓️ Date Parsing:**  
Converts **relative date expressions** (e.g., “next Monday”, “end of
week”) into **ISO-formatted dates** for accurate scheduling.

---

## **Technical Stack**

| Component | Description |
|------------|-------------|
| **AI Provider** | Groq (via OpenAI SDK) using the `GPT-OSS-20B` model
|
| **APIs** | Twenty CRM REST API + GraphQL (for member resolution) |
| **Runtime** | Webhook-triggered **serverless function** written in
**TypeScript** |

---

## **Example Input**

```json
{
  "transcript": "During the Project Phoenix Kick-off on November 1st, 2025, we discussed securing the Series B funding. ACTION: Dylan Field is designated to finalize the investor deck layout and needs to present it next Monday, November 4th. Irfan Hussain will review the deck before the presentation by Monday morning. COMMITMENT: Dario Amodei confirmed he would personally review the security protocols for the AI model before the end of this week, by Friday November 7th. Iqra Khan will coordinate the security review process and ensure completion by the Friday deadline.",
  "meetingTitle": "Project Phoenix Kick-off",
  "meetingDate": "2025-11-01",
  "participants": [
    "Brian Chesky",
    "Dario Amodei",
    "Iqra Khan",
    "Irfan Hussain",
    "Dylan Field"
  ],
  "token": "e6d9d54e51953fd5a451cca933c63e7f8783b001f0c45be95be9d09ee06c6cda",
  "relatedPersonId": "6c4b0e98-b69e-42a4-ba0c-fd2eeafca642"
}
```
---

## **Example Output**

```json
{
  "success": true,
  "noteId": "9cc3b4fc-ae37-4b3e-a343-a4c69cf6b1e8",
  "taskIds": [
    "0f408062-0dcc-49f0-9866-1ea05392661d",
    "2b3739bf-0653-4101-9419-6a44ea5135cd"
  ],
  "summary": {
    "noteCreated": true,
    "tasksCreated": 2,
    "actionItemsProcessed": 2,
    "commitmentsProcessed": 0
  },
  "executionLogs": [
    " Validation passed",
    "📝 RelatedPersonId: 6c4b0e98-b69e-42a4-ba0c-fd2eeafca642",
    "🤖 Starting transcript analysis...",
    " Analysis complete: 2 action items, 0 commitments",
    "📄 Creating note in Twenty CRM...",
    " Note created: 9cc3b4fc-ae37-4b3e-a343-a4c69cf6b1e8",
    "📋 Creating tasks from action items...",
    " Action item tasks created: 2",
    "📋 Creating tasks from commitments...",
    " Commitment tasks created: 0"
  ]
}
```
---

Variable Name | Description
-- | --
GROQ_API_KEY | API key for authenticating requests to the Groq AI
service.
TWENTY_API_KEY | Authentication token used to access the Twenty CRM API.
TWENTY_API_URL | Base URL for the Twenty CRM REST API.
WEBHOOK_SECRET | Secret key used to validate incoming webhook requests
for security.
NODE_ENV | Defines the runtime environment (development, production,
etc.).
LOG_LEVEL | Controls verbosity of logs (info, debug, error).

---

## **Attachments**
<img width="649" height="863" alt="swappy-20251103-035128"
src="https://github.com/user-attachments/assets/2f0390af-9538-4fe2-bba8-f38e558935ad"
/>

---




https://github.com/user-attachments/assets/88620035-67ed-4150-b0be-46131083e2c5



---

Co-authored-by: iqra77818 <iqra77818@gmail.com>
2025-11-04 12:04:00 +01:00
Harshit VashishtandGitHub 0cdc61c211 [HACKTOBERFEST] feat: Add AI meeting transcript integration with Twenty CRM (#15498)
This PR introduces an end-to-end workflow to automatically process
meeting transcripts and create structured notes and tasks in Twenty CRM.
It leverages OpenAI to extract summaries, key points, action items, and
participant commitments from transcripts.

Key features include:
1. AI-powered transcript analysis: Uses OpenAI GPT‑4o-mini to extract a
concise summary, key discussion points, action items, and commitments.
2. Automated note creation: Generates a rich Markdown note in Twenty CRM
with summary and key points.
3. Task automation: Automatically creates tasks in Twenty CRM from
extracted action items and commitments, linking them to the meeting
note.
4. Custom field support: Supports optional metadata from transcript
payloads, which can be included in note/task content or mapped to custom
fields in Twenty CRM if supported.
5. Webhook-ready: Designed to process Granola-style (or similar) webhook
payloads, making it easy to integrate with any AI meeting tool.





Sumbission for Hacktoberfest 
Team Name : One for All
2025-11-04 12:03:32 +01:00
James BryantandGitHub 7ec56433c7 [HACKTOBERFEST] Add rollup engine app with UI-driven configuration (#15482)
## Summary

- add packages/twenty-apps/rollup-engine: a parameterised rollup engine
that ships a default Opportunity → Company
    aggregation.
- declare runtime config in package.json (TWENTY_API_KEY, optional
TWENTY_API_BASE_URL, and ROLLUP_ENGINE_CONFIG) so
app configuration lives entirely in Settings → Apps → [App] →
Configuration.
- document the workflow in README.md: deploy via twenty app sync,
populate env vars in the UI, use the Test panel with
    a ready JSON payload, and reference troubleshooting tips.
- adjust the function entry point to a named async export and add
fallback logic for blank base URLs, matching the
    UI’s env behaviour.
- prune legacy templates and examples so
config/templates/opportunity-to-company.json is the single copy/paste
    starting point.

  ## UI / UX impact

  After syncing the app:

- the Configuration screen shows the three env keys with helpful
descriptions (API key, optional base URL, JSON
    override),
  - the built-in Test your function panel works immediately
- and the default JSON config is available from
config/templates/opportunity-to-company.json for users who need to
    customise rollups.

  ## Testing

  - Out-of-the-box deploy to the hosted workspace (Opportunity update) ✓
  - “Test your function” with the default config ✓
- Override example: point debugOpportunityCount at a scratch field via
ROLLUP_ENGINE_CONFIG ✓
  - Optional: local smoke test (yarn install && yarn smoke) still passes
2025-11-04 12:03:00 +01:00
WeikoandGitHub fd5c39b4da Remove default feature flag feature (#15587)
## Context
Having rollout feature for new workspaces creates bad experience for new
users and doesn't bring as much value as existing ones anyway. Removing
migration v2 from default feature flag and the whole concept of default
feature flag
2025-11-04 11:40:49 +01:00
Paul RastoinandGitHub a3dc6e5c59 View field create many mutation (#15576)
# Introduction
When creating a view with v2 flag activated in production result in race
condition due to request being slow and //.
That's why we're introducing a batch create on view field here
closing https://github.com/twentyhq/core-team-issues/issues/1836

## In v2
- batch create view field endpoint is available
- frontend will target the new endpoint

## In v1
- batch create view field endpoint is not available
- frontend will stick to old fake batch view field creation loop

## Polish
- use persist view field is quite verbose as contains two data model we
could aim to create an update many view fields in order to standardize
new pattern
2025-11-04 11:36:08 +01:00
nitinandGitHub 106c33abec fix inconsistent widget placeholder placement (#15580)
fixes -
https://github.com/twentyhq/twenty/pull/15496#issuecomment-3481636780

and https://discord.com/channels/1130383047699738754/1434971632539009055

- make the pending placeholder to be non-static -- so that it behaves
similarly to compact behavior like other widgets do
- make sure users can't set dashboards to be opened in the side panel --
similar to workflows

before- 


https://github.com/user-attachments/assets/d991db56-1388-4e4b-b743-a63f56b6187a

after- 


https://github.com/user-attachments/assets/9031f2c6-1e3b-4604-a5a0-3124d14484ce
2025-11-04 11:07:57 +01:00
Abdullah.andGitHub 77f04e7306 fix: brace-expansion regular expression denial of service vulnerability (#15558)
Resolves [Dependabot Alert
236](https://github.com/twentyhq/twenty/security/dependabot/236) and
[Dependabot Alert
237](https://github.com/twentyhq/twenty/security/dependabot/237).

Used `yarn up brace-expansion --recursive` to move versions from 1.1.11
and 2.0.1 to 1.1.12 and 2.0.2.
2025-11-04 08:53:57 +01:00
Abdullah.andGitHub 14c66c942f fix: update tmp to a safer version. (#15554)
@charlesBochet had a conversation with Felix and he said we don't need
to spend time upgrading `zapier-platform-core` and `zapier-platform-cli`
since `twenty-zapier` will be deprecated anyway, making those packages
irrelevant.

I have updated tmp to a safer version elsewhere using `yarn up tmp
--recursive`. Also added `yarn.lock` to both server and front ci.

The massive changes in `yarn.lock` were introduced by
`zapier-platform-cli` version 17x - not sure if they caused those
breaking changes, but if they did and we still want update
zapier-related packages, I will take that up in another PR.
2025-11-04 08:53:33 +01:00
Charles BochetandGitHub 7ff91a61c6 Add dashboard rollout commands (#15567)
## Tests

### makeSureDashboardNamingAvailableCommand

Case 1: no dashboard custom object
Case 2: with dashboard custom object

### SeedDashboardViewCommand

Case 1: no existing view
Case 2: with existing view
2025-11-03 18:35:57 +01:00
nitinandGitHub 5bc876e4b4 IFrame widget improvements (#15483)
changes - 
- make iframe side panel to match others -- ie use SidePanelHeader for
title
- make url optional in configuration to match that of the other widgets
(allow partial saves) - render No data status when error or no url
- split widget sizes into two -- graph widget sizes and widget sizes
(graph widgets are a subset of chart widgets)
2025-11-03 17:30:06 +00:00
902eb2c5d2 Instant widget placeholder placement on drag end (#15496)
closes -
https://discord.com/channels/1130383047699738754/1430596561137700957

fixed a bug - where on chart type change in side panel -- wont update
the widgets minimum size

---------

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-11-03 17:08:13 +00:00
Thomas TrompetteandGitHub 9c695cfed5 Remove relations from manual trigger output schema (#15578)
Fixes https://github.com/twentyhq/twenty/issues/15319
Fixes https://github.com/twentyhq/twenty/issues/15255

On index page, we load record with only a few relation fields. So record
selected on manual trigger do not contain all the fields. To match that
behavior, let's remove relations from output schema. The user will do a
search on relation id if needed.
2025-11-03 16:54:13 +00:00
Raphaël BosiandGitHub 711cf819e9 Fix date granularity saving issue (#15579)
The date granularities were missing in the fragment
2025-11-03 16:53:44 +00:00
Lucas BordeauandGitHub b33b38cb02 Follow-up high fixes on date refactor (#15553)
This PR fixes important bugs on date filter handling following-up date
refactor.

Fixes : https://github.com/twentyhq/core-team-issues/issues/1814
2025-11-03 17:51:42 +01:00
nitinandGitHub bafc0496b1 add inner padding on bar chart (#15577)
before:

<img width="602" height="407" alt="CleanShot 2025-11-03 at 22 06 45"
src="https://github.com/user-attachments/assets/3553d3a0-2626-4f88-9adc-7d472fde2b26"
/>

after:

<img width="628" height="392" alt="CleanShot 2025-11-03 at 22 06 08"
src="https://github.com/user-attachments/assets/d5cf2b68-c5eb-4ce7-be27-c685ccd81109"
/>
2025-11-03 17:48:57 +01:00
a64d4dbe71 Various font styling fixes on chart (#15572)
- Fix axis legends styling
- Fix legends styling
- Fix tooltip legends

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
2025-11-03 16:43:28 +00:00
6675931100 i18n - translations (#15574)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-03 17:36:59 +01:00
Raphaël BosiandGitHub 4a87706d0b Fix chart filter settings back button behavior (#15573)
Before:


https://github.com/user-attachments/assets/23881978-3234-4a0b-95ec-15470d26026d


After:


https://github.com/user-attachments/assets/80d222f6-585a-4fff-8476-dae327e34699
2025-11-03 16:25:59 +00:00
Thomas TrompetteandGitHub d1330a4a8c Improve workflow performances (#15528)
- on draft creation, do not fetch the full version. Avoid the fetch of
steps and trigger
- on activation, we were performing 8 queries/mutations synchronously +
2 additional for automated triggers. I refacto the call it it gets
reduced to 4 queries/mutations + 2 additional for automated triggers
2025-11-03 17:23:01 +01:00
nitinGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
c2d8fd495a fix: trash button centering + gap between forbidden display and trashbutton (#15571)
<img width="247" height="73" alt="CleanShot 2025-11-03 at 21 24 18"
src="https://github.com/user-attachments/assets/c0287919-be18-4dfa-96b8-5bbdeaab6693"
/>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-11-03 16:21:17 +00:00
d72aae86cb Graph margins vary depending on presence of labels (#15559)
https://github.com/user-attachments/assets/3c27fa7b-78a3-49eb-9795-53571662cf12

---------

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2025-11-03 17:06:41 +01:00
06feee91f0 i18n - translations (#15570)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-03 16:47:03 +01:00
Weiko 464f80ed9b fix front lint 2025-11-03 16:38:55 +01:00
454114932a fix: ai settings page crash (#15455)
Fixes - https://github.com/twentyhq/twenty/issues/14995

---------

Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-11-03 16:36:27 +01:00
WeikoandGitHub a5e53d74dc fix calendar view when switched from other layout type bis (#15569) 2025-11-03 16:35:30 +01:00
WeikoandGitHub 0b21ac7dd8 Add permission check on calendar view drag and add new (#15556)
## Context
If selected date field of the calendar view has readonly, we should not
allow users to drag/drop cards nor allow them to add a new record
(resulting in a permission error on the BE due to the FE updating the
said field)
2025-11-03 16:33:59 +01:00
Paul RastoinandGitHub 17acfe1d2a [REQUIRED_FOR_1_10] Fix kanban foreign key migration (#15557)
# Introduction
We introduced a foreign key addition that will fail in production due to
orphan views targetting non existing fields

## Migration
The migration will be run for any new workspace successfully or any
twenty instance without corrupted data

## Upgrade command
The upgrade command will at some point allow the migration to be run
manually after removing any corrupted data

## Release note
We should remove the migration we've manually set as being run in
production
2025-11-03 15:08:35 +00:00
Charles BochetandGitHub 20576f2ee7 Add Dashboard in Lab, make Calendar available out of lab (#15560)
As per title!
2025-11-03 15:40:26 +01:00
b32ab35db9 i18n - translations (#15563)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-03 15:32:38 +01:00
WeikoandGitHub 82ac642d73 Fix calendar view when switched from other layout type not updating state (#15562)
## Context
Switching from kanban layout to calendar was not displaying records
properly. Adding the missing state update (similar to kanban case a few
lines above)
2025-11-03 15:32:23 +01:00
Raphaël BosiandGitHub 1739ee0595 Add date granularity and timezone and first day of the week to graphs (#15543)
- Allow users to choose the date granularity of the x axis and the group
by of the y axis on a bar chart
- Display those options conditionally
- Store timezones in graphs: each graph has its own timezone, defaults
to the user timezone. There will be a picker in the v2 to choose the
timezone. For now the timezone is not used by the backend, but it will
be used in filters and in group by queries.
- Store first day of the week



https://github.com/user-attachments/assets/66a5d156-dd93-4ebe-8c8f-d172f93e25be
2025-11-03 15:27:47 +01:00
MarieandGitHub 0975b06a9b [demo] Allow workspace to work with a non-system workspaceMember object (#15547)
For demo purposes, we want to work with workspaceMember has a non-system
object, allowing it to be displayed on the product, to be added custom
fields etc.
It is something we may want to do later anyway. 
This PR adds a bypassPermissionChecks on workspaceMember repository
calls. This does not provide more permissions than before for other
workspaces: workspaceMember being a system object, permissions are
already bypassed for this one. (Note that actions such inviting a new
workspaceMember, updating a workspaceMember are protected by a system
permission checked in the relevant places).

This work does **not** allow any workspace to switch their
workspaceMember object to non-system, the switch is still manual.
2025-11-03 14:57:37 +01:00
9e1c854b27 fix: updated dryrun to return correct populated record (#15484)
## Description

- This PR approaches to solve
https://github.com/twentyhq/twenty/issues/15201
- updated `createDryRunResponse` to return fully populated merged record
- this way the frontend can render the populated data it as-is without
recomputing relations
- Dry-run now uses the same nested-relations population path as the
non-dryRun flow

---------

Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2025-11-03 13:50:14 +01:00
GuillimandGitHub 120cec9885 fix migration command - workflow runs (#15540)
fix migration command to enable the id addition in the fieldmetadata
options of workflow runs

Isues was on the workfluw rin (xurrently in produciton) if we filter by
status: clicking "Stopped" also selects "Stoppping" automatically.

<img width="735" height="436" alt="Screenshot 2025-11-03 at 12 26 40"
src="https://github.com/user-attachments/assets/20fd8b71-f7be-4115-acae-9b36f53e6d5f"
/>
2025-11-03 12:38:14 +01:00
Charles BochetandGitHub 50eb8c5558 Add command to make sure v1.8 workspaces are not using FULL or PARTIAL sync stages (that should be already deprecated) (#15545)
In v1.8, we have already run a command to deprecate FULL or PARTIAL sync
stages.

However the code was fully deprecated in v1.10 and some workspaces might
still have this status used. This is to double check
2025-11-03 12:36:46 +01:00
0480ea048c i18n - translations (#15542)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-03 12:01:05 +01:00
GuillimandGitHub 4f4a13edf5 switch migrationActionsWithParent to migrationActionsWithParentTmp temporary (#15478)
The removal of views from the workspaces schema implies the deletion of 
```
view
viewFilter
viewFilterGroup
viewGroup
viewSort
```
Before it is created again in the core schema.

However, the syncmetadata that executes the pending migration fails
because it will try to
`query failed: DROP TABLE "workspace_xxxx"."view"`
before the removal of the other view related tables that contains a view
foreign key with this kind of message
> constraint FK_c5ab40cd4debb51d588752a4857 on table "viewField" depends
on table view
2025-11-03 11:50:22 +01:00
3dac899741 i18n - translations (#15541)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-03 11:48:30 +01:00
Charles BochetandGitHub 08d475b229 Revert "fix: tmp allows arbitrary temporary file / directory write via symbolic link dir parameter" (#15539)
Reverts twentyhq/twenty#15452
2025-11-03 11:42:28 +01:00
5b2950c43a Introduce SSO bypass permission. (#15417)
Closes [Core Issue
#1772](https://github.com/twentyhq/core-team-issues/issues/1772).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces SSO bypass with a new permission flag and workspace-level
provider toggles, enabling permitted users to log in via
Google/Microsoft/Password when SSO-only, with backend enforcement and
frontend UI/hooks/queries.
> 
> - **Backend**:
> - **Permission & Enforcement**: Add `PermissionFlagType.SSO_BYPASS`;
update `AuthService` to allow login via non-SSO providers when workspace
bypass is enabled and user has `SSO_BYPASS`.
> - **Workspace Model**: Add `isGoogleAuthBypassEnabled`,
`isMicrosoftAuthBypassEnabled`, `isPasswordAuthBypassEnabled`
(migration, entity, update input, service validation).
> - **Public API**: Extend `PublicWorkspaceDataOutput` with
`authBypassProviders`; resolver computes it; permissions defaults
include `SSO_BYPASS`.
> - **Frontend**:
> - **GraphQL/State**: Generate new types/fields; add
`authBypassProviders` to `GetPublicWorkspaceDataByDomain`; new states
`workspaceAuthBypassProvidersState`, `workspaceBypassModeState`.
> - **Auth UI/Logic**: Add `useWorkspaceBypass`; update sign-in form and
footer to offer "Bypass SSO" and use merged providers when enabled;
remove auto-redirect when single SSO.
> - **Settings**: Add Security section to toggle bypass methods per
provider; conditionally show Change Password via `useCanChangePassword`.
> - **Tests/Mocks**: Update mocks and tests to include bypass
flags/providers.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8c393b2bad. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-11-03 11:40:09 +01:00
604b3e50de i18n - translations (#15531)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-03 10:36:14 +01:00
e462f191d0 fix: tmp allows arbitrary temporary file / directory write via symbolic link dir parameter (#15452)
Resolves [Dependabot Alert
255](https://github.com/twentyhq/twenty/security/dependabot/255) - fix:
tmp allows arbitrary temporary file / directory write via symbolic link
`dir` parameter.

Updated the dev-dependency `zapier-platform-cli` for it to depend on tmp
0.2.4 and also ran `yarn up tmp --recursive` to update the version of
tmp elsewhere.

Not expecting any breaking changes to twenty-zapier since
`zapier-platform-cli` is marked as a development dependency.

Co-authored-by: martmull <martmull@hotmail.fr>
2025-11-03 10:17:08 +01:00
Félix MalfaitandGitHub 7c854ba816 Remove Hacktoberfest from README (#15530)
As per title
2025-11-03 10:03:03 +01:00
neo773andGitHub f9eb06f015 fix IMAP onboarding (#15444) 2025-11-02 13:33:03 +01:00
GuillimandGitHub 53bb534af8 MORPH for relationDecorator (#15420)
# Adding MORPH support  to the relationDecorator

Extends the @WorkspaceRelation decorator to support morph relations by
adding isMorphRelation and morphId options

The implementation enforces type safety through TypeScript discriminated
unions, ensuring morphId is required when isMorphRelation: true, and
includes runtime validation that throws a descriptive error if morphId
is missing.

The morph relation metadata is properly stored in metadataArgsStorage
and integrates seamlessly with the existing StandardFieldRelationFactory
to convert decorated fields into FieldMetadataType.MORPH_RELATION
entities with appropriate database constraints.



To test :
in person entity, add
```ts
  @WorkspaceRelation({
    standardId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordCompany,
    type: RelationType.MANY_TO_ONE,
    label: msg`Test Related Record of Company`,
    description: msg`Test relation to company`,
    icon: 'IconLink',
    inverseSideTarget: () => CompanyWorkspaceEntity,
    inverseSideFieldKey: 'testRelatedPeople',
    onDelete: RelationOnDeleteAction.CASCADE,
    isMorphRelation: true,
    morphId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordMorphId,
  })
  @WorkspaceIsNullable()
  @WorkspaceIsSystem()
  testRelatedRecordCompany: Relation<CompanyWorkspaceEntity> | null;

  @WorkspaceJoinColumn('testRelatedRecordCompany')
  testRelatedRecordCompanyId: string | null;

  @WorkspaceRelation({
    standardId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordOpportunity,
    type: RelationType.MANY_TO_ONE,
    label: msg`Test Related Record of Opportunity`,
    description: msg`Test relation to opportunity`,
    icon: 'IconLink',
    inverseSideTarget: () => OpportunityWorkspaceEntity,
    inverseSideFieldKey: 'testRelatedPeople',
    onDelete: RelationOnDeleteAction.CASCADE,
    isMorphRelation: true,
    morphId: PERSON_STANDARD_FIELD_IDS.testRelatedRecordMorphId,
  })
  @WorkspaceIsNullable()
  @WorkspaceIsSystem()
  testRelatedRecordOpportunity: Relation<OpportunityWorkspaceEntity> | null;

  @WorkspaceJoinColumn('testRelatedRecordOpportunity')
  testRelatedRecordOpportunityId: string | null;
```

in opportunity,
```ts
@WorkspaceRelation({
    standardId: OPPORTUNITY_STANDARD_FIELD_IDS.testRelatedPeople,
    type: RelationType.ONE_TO_MANY,
    label: msg`Test Related People`,
    description: msg`People linked to the opportunity via Test Related Record`,
    icon: 'IconUsers',
    inverseSideTarget: () => PersonWorkspaceEntity,
    inverseSideFieldKey: 'testRelatedRecordOpportunity',
    onDelete: RelationOnDeleteAction.SET_NULL,
  })
  @WorkspaceIsNullable()
  @WorkspaceIsSystem()
  testRelatedPeople: Relation<PersonWorkspaceEntity[]>;
  ```

in company,
```ts
  @WorkspaceRelation({
    standardId: COMPANY_STANDARD_FIELD_IDS.testRelatedPeople,
    type: RelationType.ONE_TO_MANY,
    label: msg`Test Related People`,
description: msg`People linked to the company via Test Related Record`,
    icon: 'IconUsers',
    inverseSideTarget: () => PersonWorkspaceEntity,
    inverseSideFieldKey: 'testRelatedRecordCompany',
    onDelete: RelationOnDeleteAction.SET_NULL,
  })
  @WorkspaceIsNullable()
  @WorkspaceIsSystem()
  testRelatedPeople: Relation<PersonWorkspaceEntity[]>;
```

In constants/standard-field-ids.ts, add the standard fields ids
```ts
// COMPANY_STANDARD_FIELD_IDS
testRelatedPeople: '20202020-7a90-47e9-b31f-916a716fd212',

// OPPORTUNITY_STANDARD_FIELD_IDS
testRelatedPeople: '20202020-7a90-47e9-b31f-916a716fd213',

// PERSON_STANDARD_FIELD_IDS
testRelatedRecordOpportunity: '20202020-5a90-47e9-b31f-916a716fd211',
testRelatedRecordCompany: '20202020-1eb2-4298-910b-66b015b36d72',
testRelatedRecordMorphId: '20202020-6ccf-48e3-bb92-bc9961bc011e',
```
2025-11-02 13:31:48 +01:00
Charles BochetandGitHub 498d89633c Fix view favorite making the app crash (#15517)
On new workspaces, as they come without the "view" object which not part
of engine metadata, favorites pointing to views make the app crashes.

This fixes it
2025-11-01 20:05:17 +01:00
neo773andGitHub 44c0b9f94b refactor messaging error handling (#15416) 2025-11-01 20:03:41 +01:00
c42c2590de i18n - translations (#15516)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-11-01 18:45:55 +01:00
afeb505eed [Breaking Change] Implement reliable date picker utils to handle all timezone combinations (#15377)
This PR implements the necessary tools to have `react-datepicker`
calendar and our date picker components work reliably no matter the
timezone difference between the user execution environment and the user
application timezone.

Fixes https://github.com/twentyhq/core-team-issues/issues/1781

This PR won't cover everything needed to have Twenty handle timezone
properly, here is the follow-up issue :
https://github.com/twentyhq/core-team-issues/issues/1807

# Features in this PR

This PR brings a lot of features that have to be merged together.

- DATE field type is now handled as string only, because it shouldn't
involve timezone nor the JS Date object at all, since it is a day like a
birthday date, and not an absolute point in time.
- DATE_TIME field wasn't properly handled when the user settings
timezone was different from the system one
- A timezone abbreviation suffix has been added to most DATE_TIME
display component, only when the timezone is different from the system
one in the settings.
- A lot of bugs, small features and improvements have been made here :
https://github.com/twentyhq/core-team-issues/issues/1781

# Handling of timezones

## Essential concepts

This topic is so complex and easy to misunderstand that it is necessary
to define the precise terms and concepts first. It resembles character
encoding and should be treated with the same care.

- Wall-clock time : the time expressed in the timezone of a user, it is
distinct from the absolute point in time it points to, much like a
pointer being a different value than the value that it points to.
- Absolute time : a point in time, regardless of the timezone, it is an
objective point in time, of course it has to be expressed in a given
timezone, because we have to talk about when it is located in time
between humans, but it is in fact distinct from any wall clock time, it
exists in itself without any clock running on earth. However, by
convention the low-level way to store an absolute point in time is in
UTC, which is a timezone, because there is no way to store an absolute
point in time without a referential, much like a point in space cannot
be stored without a referential.
- DST : Daylight Save Time, makes the timezone shift in a specific
period every year in a given timezone, to make better use of longer days
for various reasons, not all timezones have DST. DST can be 1 hour or 30
min, 45 min, which makes computation difficult.
- UTC : It is NOT an “absolute timezone”, it is the wall-clock time at
0° longitude without DST, which is an arbitrary and shared human
convention. UTC is often used as the standard reference wall-clock time
for talking about absolute point in time without having to do timezone
and DST arithmetic. PostgreSQL stores everything in UTC by convention,
but outputs everything in the server’s SESSION TIMEZONE.

## How should an absolute point in time be stored ?

Since an absolute point in time is essentially distinct from its
timezone it could be stored in an absolute way, but in practice it is
impossible to store an absolute point in time without a referential. We
have to say that a rocket launched at X given time, in UTC, EST, CET,
etc. And of course, someone in China will say that it launched at 10:30,
while in San Francisco it will have launched at 19:30, but it is THE
SAME absolute point in time.

Let’s take a related example in computer science with character
encoding. If a text is stored without the associated encoding table, the
correct meaning associated to the bits stored in memory can be lost
forever. It can become impossible for a program to guess what encoding
table should be used for a given text stored as bits, thus the glitches
that appeared a lot back in the early days of internet and document
processing.

The same can happen with date time storing, if we don’t have the
timezone associated with the absolute point in time, the information of
when it absolutely happened is lost.

It is NOT necessary to store an absolute point in time in UTC, it is
more of a standard and practical wall-clock time to be associated with
an absolute point in time. But an absolute point in time MUST be store
with a timezone, with its time referential, otherwise the information of
when it absolutely happened is lost.

For example, it is easier to pass around a date as a string in UTC, like
`2024-01-02T00:00:00Z` because it allows front-end and back-end code to
“talk” in the same standard and DST-free wall-clock time, BUT it is not
necessary. Because we have date libraries that operate on the standard
ISO timezone tables, we can talk in different timezone and let the
libraries handle the conversion internally.

It is false to say that UTC is an absolute timezone or an absolute point
in time, it is just the standard, conventional time referential, because
one can perfectly store every absolute points in time in UTC+10 with a
complex DST table and have the exactly correct absolute points in time,
without any loss of information, without having any UTC+0 dates
involved.

Thus storing an absolute point in time without a timezone associated,
for example with `timestamp` PostgreSQL data type, is equivalent to
storing a wall-clock time and then throwing away voluntarily the
information that allows to know when it absolutely happened, which is a
voluntary data-loss if the code that stores and retrieves those
wall-clock points in time don’t store the associated timezone somewhere.

This is why we use `timestamptz` type in PostgreSQL, so that we make
sure that the correct absolute point in time is stored at the exact time
we send it to PostgreSQL server, no matter the front-end, back-end and
SQL server's timezone differences.

## The JavaScript Date object

The native JavaScript Date object is now officially considered legacy
([source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)),
the Date object stores an absolute point in time BUT it forces the
storage to use its execution environment timezone, and one CANNOT modify
this timezone, this is a legacy behavior.

To obtain the desired result and store an absolute point in time with an
arbitrary timezone there are several options :

- The new Temporal API that is the successor of the legacy Date object.
- Moment / Luxon / @date-fns/tz that expose objects that allow to use
any timezone to store an absolute point in time.

## How PostgreSQL stores absolute point in times

PostgreSQL stores absolute points in time internally in UTC
([source](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-INPUT-TIME-STAMPS)),
but the output date is expressed in the server’s session timezone
([source](https://www.postgresql.org/docs/current/sql-set.html)) which
can be different from UTC.

Example with the object companies in Twenty seed database, on a local
instance, with a new “datetime” custom column :

<img width="374" height="554" alt="image"
src="https://github.com/user-attachments/assets/4394cb43-d97e-4479-801d-ca068f800e39"
/>

<img width="516" height="524" alt="image"
src="https://github.com/user-attachments/assets/b652f36a-d2e2-47a4-8950-647ca688cbbd"
/>

## Why can’t I just use the JavaScript native Date object with some
manual logic ?

Because the JavaScript Date object does not allow to change its internal
timezone, the libraries that are based on it will behave on the
execution environment timezone, thus leading to bugs that appear only on
the computers of users in a timezone but not for other in another
timezone.

In our case the `react-datepicker` library forces to use the `Date`
object, thus forcing the calendar to behave in the execution environment
system timezone, which causes a lot of problems when we decide to
display the Twenty application DATE_TIME values in another timezone than
the user system one, the bugs that appear will be of the off-by-one date
class, for example clicking on 23 will select 24, thus creating an
unreliable feature for some system / application timezone combinations.

A solution could be to manually compute the difference of minutes
between the application user and the system timezones, but that’s not
reliable because of DST which makes this computation unreliable when DST
are applied at different period of the year for the two timezones.

## Why can’t I compute the timezone difference manually ?

Because of DST, the work to compute the timezone difference reliably,
not just for the usual happy path, is equivalent to developing the
internal mechanism of a date timezone library, which is equivalent to
use a library that handles timezones.

## Using `@date-fns/tz` to solve this problem

We could have used `luxon` but it has a heavy bundle size, so instead we
rely here on `@date-fns/tz` (~1kB) which gives us a `TZDate` object that
allows to use any given timezone to store an absolute point-in-time.

The solution here is to trick `react-datepicker` by shifting a Date
object by the difference of timezone between the user application
timezone and the system timezone.

Let’s take a concerte example.

System timezone : Midway,  ⇒ UTC-11:00, has no DST.

User application timezone : Auckland, NZ ⇒ UTC+13:00, has a DST.

We’ll take the NZ daylight time, so that will make a timezone difference
of 24 hours !

Let’s take an error-prone date : `2025-01-01T00:00:00` . This date is
usually a good test-case because it can generate three classes of bugs :
off-by-one day bugs, off-by-one month bugs and off-by-one year bugs, at
the same time.

Here is the absolute point in time we take expressed in the different
wall-clock time points we manipulate


Case | In system timezone ⇒ UTC-11 | In UTC | In user application
timezone ⇒ UTC+13
-- | -- | -- | --
Original date | `2024-12-31T00:00:00-11:00` | `2024-12-31T11:00:00Z` |
`2025-01-01T00:00:00+13:00`
Date shifted for react-datepicker | `2025-01-01T00:00:00-11:00` |
`2025-01-01T11:00:00Z` | `2025-01-02T00:00:00+13:00`

We can see with this table that we have the number part of the date that
is the same (`2025-01-01T00:00:00`) but with a different timezone to
“trick” `react-datepicker` and have it display the correct day in its
calendar.

You can find the code in the hooks
`useTurnPointInTimeIntoReactDatePickerShiftedDate` and
`useTurnReactDatePickerShiftedDateBackIntoPointInTime` that contain the
logic that produces the above table internally.

## Miscellaneous

Removed FormDateFieldInput and FormDateTimeFieldInput stories as they do
not behave the same depending of the execution environment and it would
be easier to put them back after having refactored FormDateFieldInput
and FormDateTimeFieldInput

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-11-01 18:31:24 +01:00
EtienneandGitHub 3a203040f7 Fix - Multiple banners issue (#15506)
Done : 
- Fix multiple banners issue
- Lift impersonation banner


After 
<img width="800" height="400" alt="Screenshot 2025-10-31 at 18 05 02"
src="https://github.com/user-attachments/assets/7d5949d4-1c8b-4a4f-91c1-c115e53436fb"
/>
2025-10-31 19:17:31 +01:00