Commit Graph

15 Commits

Author SHA1 Message Date
Raphaël Bosi 41d5d80a65 Migrate Company and Person standard fields in preparation for the enrichment app (#21171)
# Migrate Company and Person standard fields in preparation for the
enrichment app

## Why

Our standard `Person`/`Company` objects accumulated fields that aren't
generic to every
business, while missing a more universal revenue field that essentially
every CRM ships.
This PR makes the **Standard application** hold a tighter, more
universal set of fields,
and sets the stage for a follow-up PR that introduces a **People Data
Labs enrichment app**
to populate them.

## What changes

### Standard fields

**Demoted (Standard → Workspace Custom application)** — not generic
enough to ship as standard:

| Object  | Field                          | Type     |
| ------- | ------------------------------ | -------- |
| Company | annualRecurringRevenue (ARR)   | CURRENCY |
| Company | employees                      | NUMBER   |
| Company | idealCustomerProfile (ICP)     | BOOLEAN  |
| Company | xLink (X/Twitter)              | LINKS    |
| Person  | xLink (X/Twitter)              | LINKS    |
| Person  | city                           | TEXT     |

**Added (new generic Standard field)** — present in
Salesforce/HubSpot/Zoho, PDL-populatable:

| Object | Field | Type |
| ------- | ------------- |
-------------------------------------------------------- |
| Company | annualRevenue | CURRENCY (generic total revenue; replaces
the niche ARR) |

### Behavior by workspace

* **New workspaces:** demoted fields are gone; `annualRevenue` is
**active**.
* **Existing workspaces:** demoted fields are **preserved as active
custom fields, data intact**;
`annualRevenue` is created **inactive (opt-in)** with its column ready,
so a later activation
  is a metadata-only toggle.

### Upgrade commands (v2.9)

Three idempotent, per-workspace commands, run in timestamp order:

1. **`upgrade:2-9:move-demoted-standard-fields-to-custom-application`**
(1799000040000) —
re-owns the 6 demoted fields to the workspace custom application
(`isCustom = true`,
new `applicationId` + fresh `universalIdentifier`), keeping their data
and active state.
2. **`upgrade:2-9:rename-conflicting-custom-fields`** (1799000045000) —
if a workspace already
has a *custom* field named `annualRevenue`, renames it to
`annualRevenueCustom`
(data preserved via column rename) so the standard field can be added.
Skips non-custom matches.
3. **`upgrade:2-9:add-inactive-generic-standard-fields`**
(1799000050000) — creates
`Company.annualRevenue` on existing workspaces as inactive, guarded to
skip workspaces
   missing the target object or where the name is still taken.

**Failure model:** the workspace iterator isolates failures per
workspace (one workspace failing
never affects others); within a workspace the runner records per-command
status and resumes on the
next run, and every command is idempotent, so partial runs self-heal.

### Supporting changes

* **Field-option color palette:** widened the `TagColor` union
(`twenty-shared` `FieldMetadataOptions`
+ the field-metadata `options.input` DTO) from 10 colors to the full
theme palette, benefiting any
  future SELECT/MULTI_SELECT field.
* **Dev seeder:**
* The default "Annual Recurring Revenue" dashboard widget now points at
the generic
    `annualRevenue` field (renamed to "Annual Revenue").
* Removed the "Companies by Size (Stacked by City)" widget (relied on
the demoted `employees`).
* `employees` is dropped from company data seeds and re-added as a
**custom** field seed, so dev
workspaces still get an `employees` column matching the demoted
behavior.

### Cleanup

Front-end record types (`Company.ts`/`Person.ts`), the
`getDisplayNameFromParticipant` test mock,
metadata integration specs, the Zapier `crud_record` test, and the
regenerated
`get-standard-object-metadata-related-entity-ids` snapshot.

## ⚠️ Breaking change (intentional)

Removes standard fields `Company.annualRecurringRevenue`,
`Company.employees`,
`Company.idealCustomerProfile`, `Company.xLink`, `Person.xLink`, and
`Person.city` from the core
GraphQL schema (replaced by `Company.annualRevenue`).

This is why the breaking-changes check reports a large number of
removals — `graphql-inspector`
flags any removed object field plus its derived
aggregate/order-by/filter/update types.

**Mitigation:** the
`upgrade:2-9:move-demoted-standard-fields-to-custom-application` command
re-owns these fields as custom fields per workspace, preserving their
name and data, so existing
tenants keep working. New workspaces won't have them.
2026-06-04 15:54:04 +00:00
Félix Malfait 67e7f05a68 feat: email attachments and open-in-app click action (#19485)
## Changes

### Email Attachments
- Added `EmailAttachmentsField` component for uploading and managing
email attachments
- New `useUploadEmailAttachment` hook for handling file uploads with
size validation
- New `UPLOAD_EMAIL_ATTACHMENT_FILE` mutation for backend file
persistence
- Integrated attachments into email composer with file validation
- Added `EmailRecipientLimits` constant to enforce max recipients (100)
on frontend

### Open in App Click Action
- New `useOpenEmailInAppOrFallback` hook to open emails in the in-app
composer
- Email fields now default to "Open in app" action instead of "Open as
link"
- New `SettingsDataModelFieldOnClickActionForm` support for
`OPEN_IN_APP` action
- Email secondary table cell button now offers in-app composer as
alternative action
- `AttachmentChip` component moved from advanced-text-editor to file
module for reuse

### Refactoring & New Utilities
- Extracted `useComposeEmailForTargetRecord` hook for consistent email
composer opening
- New `useResolveDefaultEmailRecipient` hook to resolve recipient based
on record type
- New `getPrimaryEmailFromRecord` utility for safe email field access
- New `EmptyInboxPlaceholder` component with CTA button
- Simplified `ComposeEmailButton` using new hooks
- Enhanced `ComposeEmailCommand` to support bulk Person selections
- Updated `useSendEmail` to accept and forward attachments
- Recipient count validation with warning in composer footer

### Backend
- New `FileEmailAttachmentModule` with resolver and service
- New `file-email-attachment.command` for record selection menu items
- Updated `SendEmailInput` GraphQL type to include `files` field
- Email tool constants and exceptions updated

### Type Updates
- Added `SendEmailAttachmentInput` GraphQL type
- Added `FileFolder.EMAIL_ATTACHMENT` to file folder interface

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-04-10 10:01:50 +02:00
Félix Malfait b27a97f2c5 feat: enforce @/ alias for imports and fix all relative parent imports (#16787)
## Summary
This PR enforces the use of `@/` alias for imports instead of relative
parent imports (`../`).

## Changes

### ESLint Configuration
- Added `no-restricted-imports` pattern in `eslint.config.react.mjs` to
block `../*` imports with the message "Relative parent imports are not
allowed. Use @/ alias instead."
- Removed the non-working `import/no-relative-parent-imports` rule
(doesn't work properly in ESLint flat config)

### VS Code Settings
- Added `javascript.preferences.importModuleSpecifier: non-relative` to
`.vscode/settings.json` (TypeScript setting was already there)

### Code Fixes
- Fixed **941 relative parent imports** across **706 files** in
`packages/twenty-front`
- All `../` imports converted to use `@/` alias

## Why
- Consistent import style across the codebase
- Easier to move files without breaking imports
- Better IDE support for auto-imports
- Clearer understanding of where imports come from
2025-12-23 22:57:51 +01:00
Paul Rastoin 4996f3dd28 Finalize twenty standard app as workspace migration object and fields (#16353)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1995
In this PR we're fixing the remaining object/fields validation errors
resulting from standard objects and fields now passing a validation that
wasn't when using the sync metadata

## Key Changes
- **Field naming**: Renamed `iCalUID` to `iCalUid` for consistent
camelCase convention across calendar events
- **Enum standardization**: Uppercased enum values for message channels
(email→EMAIL), message participants (from→FROM, to→TO, cc→CC, bcc→BCC),
and message direction (incoming→INCOMING, outgoing→OUTGOING)
- **Label simplification**: Removed example values from workspace member
number format labels for cleaner UI
- **Migration infrastructure**: Added `isSystemBuild` flag throughout
field metadata service pipeline to allow system-level updates of
standard fields that bypass normal restrictions

## Migrating the existing data
We've created an upgrade command that will identify using the existing
object and field standard id field that needs to be updated, even though
the sync metadata still in usage could have fix them ( and the goal is
to deprecate it by the end of the sprint )
We will call the updateOneField for each of them, we're passing by the
field service in order to battle test what are going to be the temporary
way to handle standard migrations when we will start deprecating the
sync metadata but haven't still refactored the v2 workspace migration to
be workspace agnostic

## Twenty eng migration
Tested the whole migration + upgrade on twenty eng
Here are generated workspace migration
Records are handled natively gracefully too

### ICalUid
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "iCalUID",
            "to": "iCalUid",
            "property": "name"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```

### Incoming Outgoing
None as already caps in database somehow
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [],
    "workspaceId": ""
  }
}
```

### EMAIL
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "'email'",
            "to": "'EMAIL'",
            "property": "defaultValue"
          },
          {
            "from": [
              {
                "color": "green",
                "id": "",
                "label": "Email",
                "position": 0,
                "value": "email"
              },
              {
                "color": "blue",
                "id": "",
                "label": "SMS",
                "position": 1,
                "value": "sms"
              }
            ],
            "to": [
              {
                "color": "green",
                "id": "",
                "label": "Email",
                "position": 0,
                "value": "EMAIL"
              },
              {
                "color": "blue",
                "id": "",
                "label": "SMS",
                "position": 1,
                "value": "SMS"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": "e"
  }
}
```

### MessageParticipantRole
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": "'from'",
            "to": "'FROM'",
            "property": "defaultValue"
          },
          {
            "from": [
              {
                "color": "green",
                "id": "",
                "label": "From",
                "position": 0,
                "value": "from"
              },
              {
                "color": "blue",
                "id": "",
                "label": "To",
                "position": 1,
                "value": "to"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Cc",
                "position": 2,
                "value": "cc"
              },
              {
                "color": "red",
                "id": "",
                "label": "Bcc",
                "position": 3,
                "value": "bcc"
              }
            ],
            "to": [
              {
                "color": "green",
                "id": "",
                "label": "From",
                "position": 0,
                "value": "FROM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "To",
                "position": 1,
                "value": "TO"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Cc",
                "position": 2,
                "value": "CC"
              },
              {
                "color": "red",
                "id": "",
                "label": "Bcc",
                "position": 3,
                "value": "BCC"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```

### Workspace member number format labels
```json
{
  "status": "success",
  "workspaceMigration": {
    "relatedFlatEntityMapsKeys": [
      "flatFieldMetadataMaps",
      "flatIndexMaps",
      "flatViewFilterMaps",
      "flatViewGroupMaps",
      "flatViewMaps",
      "flatViewFieldMaps",
      "flatObjectMetadataMaps"
    ],
    "actions": [
      {
        "type": "update_field",
        "fieldMetadataId": "",
        "objectMetadataId": "",
        "updates": [
          {
            "from": [
              {
                "color": "turquoise",
                "id": "",
                "label": "System",
                "position": 0,
                "value": "SYSTEM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "Commas and dot (1,234.56)",
                "position": 1,
                "value": "COMMAS_AND_DOT"
              },
              {
                "color": "green",
                "id": "",
                "label": "Spaces and comma (1 234,56)",
                "position": 2,
                "value": "SPACES_AND_COMMA"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Dots and comma (1.234,56)",
                "position": 3,
                "value": "DOTS_AND_COMMA"
              },
              {
                "color": "purple",
                "id": "",
                "label": "Apostrophe and dot (1'234.56)",
                "position": 4,
                "value": "APOSTROPHE_AND_DOT"
              }
            ],
            "to": [
              {
                "color": "turquoise",
                "id": "",
                "label": "System",
                "position": 0,
                "value": "SYSTEM"
              },
              {
                "color": "blue",
                "id": "",
                "label": "Commas and dot",
                "position": 1,
                "value": "COMMAS_AND_DOT"
              },
              {
                "color": "green",
                "id": "",
                "label": "Spaces and comma",
                "position": 2,
                "value": "SPACES_AND_COMMA"
              },
              {
                "color": "orange",
                "id": "",
                "label": "Dots and comma",
                "position": 3,
                "value": "DOTS_AND_COMMA"
              },
              {
                "color": "purple",
                "id": "",
                "label": "Apostrophe and dot",
                "position": 4,
                "value": "APOSTROPHE_AND_DOT"
              }
            ],
            "property": "options"
          }
        ]
      }
    ],
    "workspaceId": ""
  }
}
```
2025-12-09 14:09:48 +01:00
Félix Malfait 464a480043 Continue ESLINT9 Migration (#13795)
Might already fix #13793
2025-08-10 23:25:58 +02:00
Paul Rastoin 9ad8287dbc [REFACTOR] twenty-shared multi barrel and CJS/ESM build with preconstruct (#11083)
# Introduction

In this PR we've migrated `twenty-shared` from a `vite` app
[libary-mode](https://vite.dev/guide/build#library-mode) to a
[preconstruct](https://preconstruct.tools/) "atomic" application ( in
the future would like to introduce preconstruct to handle of all our
atomic dependencies such as `twenty-emails` `twenty-ui` etc it will be
integrated at the monorepo's root directly, would be to invasive in the
first, starting incremental via `twenty-shared`)

For more information regarding the motivations please refer to nor:
- https://github.com/twentyhq/core-team-issues/issues/587
-
https://github.com/twentyhq/core-team-issues/issues/281#issuecomment-2630949682

close https://github.com/twentyhq/core-team-issues/issues/589
close https://github.com/twentyhq/core-team-issues/issues/590

## How to test
In order to ease the review this PR will ship all the codegen at the
very end, the actual meaning full diff is `+2,411 −114`
In order to migrate existing dependent packages to `twenty-shared` multi
barrel new arch you need to run in local:
```sh
yarn tsx packages/twenty-shared/scripts/migrateFromSingleToMultiBarrelImport.ts && \
npx nx run-many -t lint --fix -p twenty-front twenty-ui twenty-server twenty-emails twenty-shared twenty-zapier
```
Note that `migrateFromSingleToMultiBarrelImport` is idempotent, it's atm
included in the PR but should not be merged. ( such as codegen will be
added before merging this script will be removed )

## Misc
- related opened issue preconstruct
https://github.com/preconstruct/preconstruct/issues/617

## Closed related PR
- https://github.com/twentyhq/twenty/pull/11028
- https://github.com/twentyhq/twenty/pull/10993
- https://github.com/twentyhq/twenty/pull/10960

## Upcoming enhancement: ( in others dedicated PRs )
- 1/ refactor generate barrel to export atomic module instead of `*`
- 2/ generate barrel own package with several files and tests
- 3/ Migration twenty-ui the same way
- 4/ Use `preconstruct` at monorepo global level

## Conclusion
As always any suggestions are welcomed !
2025-03-22 19:16:06 +01:00
Weiko 431da37cdf add stories to roles components (#10503)
## Context
Adding stories for roles components. Also moving modules components to
the proper "modules" folder, "pages" folder being only for entry points.

## Test
Run storybook

<img width="1145" alt="Screenshot 2025-02-26 at 13 40 40"
src="https://github.com/user-attachments/assets/bc184ab0-c590-4362-8c5a-1bf5ef176e6c"
/>
<img width="1149" alt="Screenshot 2025-02-26 at 13 40 32"
src="https://github.com/user-attachments/assets/699cd205-31db-45e9-b9c1-caff1832bd47"
/>
<img width="1153" alt="Screenshot 2025-02-26 at 13 40 11"
src="https://github.com/user-attachments/assets/72e45a67-ea89-4999-8b16-6f7d027d07f6"
/>
<img width="471" alt="Screenshot 2025-02-26 at 13 38 16"
src="https://github.com/user-attachments/assets/62676943-9935-42b5-b769-5544f7eed85f"
/>
<img width="472" alt="Screenshot 2025-02-26 at 13 38 12"
src="https://github.com/user-attachments/assets/946baab9-1be4-439e-bf99-0ebeab0995f7"
/>
2025-02-26 18:16:05 +01:00
Paul Rastoin 7fd89678b7 [CHORE] Avoid isDefined duplicated reference, move it to twenty-shared (#9967)
# Introduction
Avoid having multiple `isDefined` definition across our pacakges
Also avoid importing `isDefined` from `twenty-ui` which exposes a huge
barrel for a such little util function

## In a nutshell
Removed own `isDefined.ts` definition from `twenty-ui` `twenty-front`
and `twenty-server` to move it to `twenty-shared`.
Updated imports for each packages, and added explicit dependencies to
`twenty-shared` if not already in place

Related PR https://github.com/twentyhq/twenty/pull/9941
2025-02-01 12:10:10 +01:00
Raphaël Bosi 3d5ecc9c08 7242 error when displaying message threads with a large number of participants (#7251)
Closes #7242
2024-09-25 16:53:18 +02:00
Marie b0b2281fc5 Fix mocks for unlogged users (#6376) 2024-07-24 10:08:54 +02:00
Pacifique LINJANJA 627a6bda29 Update twenty-front commands (#4667)
# 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>
2024-04-17 18:06:02 +02:00
gitstart-app[bot] 872fb2bd49 TWNTY-4450 - Add tests for /modules/activities/emails (#4520)
* Add tests for `/modules/activities/emails`

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>

* Fix tests

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>

* Remove temporary changes

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
2024-03-18 17:10:07 +01:00
Lucas Bordeau 581dfafe11 Renamed nullable utils into isDefined and isUndefinedOrNull (#4402)
* Renamed nullable utils into isDefined and isUndefinedOrNull
2024-03-11 14:28:57 +01:00
gitstart-app[bot] 17511be0cf TWNTY-3794 - ESLint rule: only take explicit boolean predicates in if statements (#4354)
* ESLint rule: only take explicit boolean predicates in if statements

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

* Merge main

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

* Fix frontend linter errors

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

* Fix jest

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

* Fix lint on new code

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Toledodev <rafael.toledo@engenharia.ufjf.br>
2024-03-09 10:48:19 +01:00
Thomas Trompette 43b10cb00c Add record chip for sender and add receivers (#3629)
* Add record chip for sender and add receivers

* Build enum for roles

* Rename var and use string literal

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
2024-01-25 18:34:19 +01:00