Compare commits

...

58 Commits

Author SHA1 Message Date
Etienne edbdca614c add 2026-06-05 18:28:41 +02:00
Etienne 70ada24a1d add 2026-06-05 18:27:59 +02:00
Etienne 78ea9d8eeb add 2026-06-05 17:33:59 +02:00
Etienne ce32c76ab9 Merge branch 'main' into ej/optimize-metadata-crud 2026-06-05 15:58:10 +02:00
nitin e485b679ea [Call Recording] Add standard object (#21158)
Adds **Call Recording** as a first-class standard object (Twenty's
flat-metadata
standard-object system), with a hidden junction to calendar events and a
backfill
command for existing workspaces. Everything is gated behind the
`IS_CALL_RECORDING_ENABLED` feature flag.

### What's included
- **`CallRecording`**: audio/video files, transcript, status, recording
policy,
timing, external bot/recording ids. Label identifier is
`meetingOccurrenceKey`.
- **`CallRecordingCalendarEventAssociation`**: hidden junction linking a
recording
to a calendar event (dedupes one bot to many subscribers of the same
meeting).
- Full metadata graph via the flat-metadata builders: fields, indexes,
views,
  view fields/groups, record page layout, and navigation items.
- **Metadata-only reverse relation** on `CalendarEvent`: present in
standard
metadata, omitted from the TS entity class to avoid expanding recursive
  nested-insert types.
- **Upgrade command (2.9.0)** backfilling active/suspended workspaces:
  - Creates the full graph; idempotent (skips when it already exists).
- Moves a colliding custom `callRecording` object aside to
`callRecordingOld`
    (numeric suffix if that name is also taken).
- Navigation items (commands) are flag-gated by `universalIdentifier`,
so a custom object
    reusing the name is never gated.

### QA
Run locally against existing workspaces (with and without a name
collision) and a
freshly created workspace:
- [x] Backfill, collision: custom `callRecording` renamed to
`callRecordingOld`;
  standard graph created.
- [x] Backfill, no collision: standard graph created; unrelated custom
object untouched.
- [x] Idempotent: re-run is a no-op, with no duplicate metadata and
counts unchanged.
- [x] New workspace via `init()` produces an identical graph to the
backfill
  (`universalIdentifier` set-diff = 0).
- [x] Label identifier (`meetingOccurrenceKey`) holds position 0 in
non-widget views.
- [x] Nav items gated behind the feature flag; collision-renamed
object's nav
  expression re-pointed to its new name.
- [x] Unit tests cover collision name resolution and nav-gating logic.
2026-06-05 13:02:50 +00:00
Paul Rastoin a78b319877 sdk/v* twenty/v* namespaces (#21247)
# Introduction
Removed never used release dispatch workflow
Now assuming that anyone releasing will create both twenty and npm
family tags
Will create a workflow to ease this later

We will now start to have several github releases, one per namespace
2026-06-05 12:40:50 +00:00
Etienne 92a9c3a4d4 update 2026-06-05 14:15:26 +02:00
Félix Malfait c3dd6b25a6 fix: use canonical oxlint rule id in lint-disable directives (#21253)
## What

Many `oxlint-disable` / `eslint-disable` directives across the repo
carry a corrupted rule id — `@typescripttypescript/<rule>` — most likely
a find-and-replace accident that mangled the eslint-era
`@typescript-eslint/` prefix.

oxlint matches disable directives **loosely by rule name**, so these
still suppress in practice (not a silent no-op), but the id is malformed
and misleading.

## Change

Replace them with the **canonical oxlint id** `typescript/<rule>` —
matching the plugin name and rule keys declared in `.oxlintrc.json` —
**127 files, 262 directives**:

| rule | count |
| --- | ----- |
| `typescript/no-explicit-any` | 250 |
| `typescript/ban-ts-comment` | 6 |
| `typescript/no-misused-promises` | 4 |
| `typescript/no-empty-object-type` | 2 |

- `twenty-server`: 122 files
- `twenty-front`: 5 files

Comment-only — no code or runtime changes.

## Verification

`oxlint --type-aware -c .oxlintrc.json` reports **0 warnings / 0
errors** for both `twenty-server` and `twenty-front`. Every changed line
is exactly the id correction inside a disable directive (262 insertions
/ 262 deletions, no collateral edits).

> Addresses the cubic review, which flagged that the canonical oxlint id
is `typescript/...` (no `@`). Worth noting the original
`@typescripttypescript/` was not actually a silent no-op — oxlint
matches these directives loosely by rule name — but `typescript/` is the
correct, config-aligned id.
2026-06-05 13:52:32 +02:00
Raphaël Bosi 6f9b59b224 Scaffold twenty-new-ui (#21236)
Scaffolds `twenty-new-ui`, the next-gen replacement for `twenty-ui`, on
**SCSS** Modules + **Base UI** (no Linaria).

- **Tooling**: Vite lib build, subpaths mirror twenty-ui, typed SCSS
Modules, Storybook + axe a11y, size-limit, Nx targets.
- **Theme**: single token source → nx generateTheme emits the CSS vars +
accessor; parity test asserts token-for-token match with twenty-ui.

Migrated a first `Toggle` component with its stories to allow
@charlesBochet to wire the new pixel-diff system.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 10:42:57 +00:00
Abdullah. b60a91a075 fix(website): render releases from local notes, drop GitHub gate (#21238)
The releases page gated visible notes on fetchLatestGithubReleaseTag();
when that unauthenticated GitHub call was rate-limited at build (common
on shared Cloudflare build IPs, no GITHUB_TOKEN), it returned null and
getVisibleReleaseNotes returned [], rendering 'No releases are visible
yet for the current published version' — the exact prod symptom.
force-static made each deploy a coin flip.

Committed MDX (+images) is the single source of truth, so gate
visibility on nothing: render all local notes deterministically with no
network call. Removes the dev/prod divergence and deletes the now-dead
fetch-latest-release-tag.ts and get-visible-releases.ts.
2026-06-05 08:59:40 +00:00
Charles Bochet cd540098f1 fix: pass reference_commit to Argos to resolve orphan PR builds (#21245)
## Summary

- Fixes Argos CI builds showing as "Orphan" (no reference branch) for PR
builds
- Computes the merge-base SHA between the PR head and `main` using the
GitHub API (`compareCommitsWithBasehead`) in the dispatch workflow
- Passes `reference_commit` in the `ci-privileged` dispatch payload so
it can be forwarded to the Argos upload API

## Context

PR builds on Argos were showing as "Orphan" because `ci-privileged`
(where the actual Argos upload happens) has no git history of the
`twenty` repo — it cannot compute the merge-base locally. Without a
`referenceCommit`, Argos can't determine which `main` build to compare
against.

The local `visual-diff.sh` script already passes
`ARGOS_REFERENCE_COMMIT` via `git merge-base HEAD main`, but the CI
pipeline was missing this. This PR adds equivalent logic using the
GitHub API (no checkout needed).

## Note for ci-privileged

The `upload-to-argos.ts` script in `ci-privileged` needs a corresponding
update to read `reference_commit` from the dispatch payload and pass it
as `referenceCommit` in the Argos API call:

```typescript
referenceCommit: process.env.REFERENCE_COMMIT || undefined,
```

## Test plan

- [ ] Verify the workflow runs successfully on a PR (merge-base step
computes a SHA)
- [ ] Confirm Argos PR builds are no longer marked as "Orphan" after the
ci-privileged counterpart is updated
2026-06-05 10:51:16 +02:00
Félix Malfait 1b30983307 fix(settings): gate the AI settings page on AI_SETTINGS, not the chat flag (#21239)
## Summary

Closes #21229.

The two AI role permissions behaved **opposite to their labels**. The
trap is that the flag's code name is the inverse of its UI label:

| `PermissionFlagType` | UI label | Section | Means |
|---|---|---|---|
| `AI` | **"Ask AI"** | Actions | End-user: chat with AI |
| `AI_SETTINGS` | **"AI"** | Member / settings | Admin: configure AI
agents |

Before this PR (on `main`):
- `AI` ("Ask AI", chat) gated **both** the AI chat **and** the AI
settings page.
- `AI_SETTINGS` ("AI", configure agents) gated **nothing** the user
could see.

So a chat-only user could reach the whole AI **configuration** page, and
toggling the "AI" settings permission did nothing — exactly the
misalignment reported in #21229.

## Root cause

`PermissionFlagType.AI` *reads* like "the AI permission", so it looks
like the natural gate for the AI settings page — but it's actually the
**chat** flag. The settings page (nav item + route) had been pointed at
`AI` in #21072 to match the Overview stats query
(`findWorkspaceAiStats`), which was itself mis-gated on `AI`. Both the
stats query and the rest of the settings surface are admin/config
features, so they belong on `AI_SETTINGS`.

## Changes

All three move the **AI settings surface** from the chat flag (`AI`) to
the settings flag (`AI_SETTINGS`); chat keeps following `AI`:

- `useSettingsNavigationItems.tsx` — AI nav item → `AI_SETTINGS`
- `SettingsRoutes.tsx` — AI settings route group → `AI_SETTINGS`
- `ai-workspace-stats.resolver.ts` — `findWorkspaceAiStats`
(settings-only, drives the Overview tab) → `AI_SETTINGS`

After this: the "AI" permission controls the AI settings page + its
Overview; the "Ask AI" permission controls the chat. Both toggles now
match their labels.

## Test plan

- [ ] Role with **only "Ask AI"** (`AI`): AI chat tabs/pane visible;
**Settings → AI is hidden** and the route is not reachable.
- [ ] Role with **only "AI"** (`AI_SETTINGS`): Settings → AI is visible,
Overview stats load; chat nav is hidden.
- [ ] Admin (both flags): everything works as before.

## Known follow-ups (out of scope — pre-existing, shared endpoints)

These remain on `AI` because they're shared with non-settings surfaces
and need either OR-gating or a resolver split, so a role with
`AI_SETTINGS` but **not** `AI` still can't use them yet:

- `getAiSystemPromptPreview` (Models/Prompts tabs) lives in the chat
resolver, class-gated `AI`; NestJS guards are additive so it can't be
cleanly method-overridden — it should be pulled into a settings
resolver.
- Agent reads `findManyAgents` / `findOneAgent` (agent create/edit
forms) are class-gated `AI` and shared with the **Workflow** editor and
**Roles** pages; these want a guard that accepts `AI ∨ AI_SETTINGS ∨
WORKFLOWS`.
2026-06-05 08:18:20 +00:00
martmull 128d2d394d feat: allow apps to add view fields to existing views (defineViewField) (#21160)
## Summary

Lets a Twenty application add **view fields (columns) to an existing
view it does not own** — including standard views like the People index
view — without redeclaring/owning that view. This mirrors the existing,
working pattern by which an app adds a custom field to a standard object
via `defineField` + `objectUniversalIdentifier`.

The asymmetry being removed was purely in the manifest schema:
`ViewFieldManifest` only existed *nested* inside
`ViewManifest.fields[]`, so adding a view field forced declaring a
`ViewManifest` — which the sync treats as a view the app creates and
owns, and rejects when the UID is a standard view's. Validation,
persistence, the FK aggregator machinery, and uninstall cleanup were
already generic and cross-app-safe, so no engine changes were needed.

### Changes
- **twenty-shared:** new top-level `StandaloneViewFieldManifest`
(`ViewFieldManifest & { viewUniversalIdentifier }`),
`Manifest.viewFields`, and a `SyncableEntity.ViewField` member.
- **twenty-sdk:** `defineViewField` (validates `universalIdentifier` +
`viewUniversalIdentifier` + `fieldMetadataUniversalIdentifier`), CLI
manifest assembly of a top-level `viewFields` list, and `dev:add
viewField` scaffolding.
- **twenty-server:** one top-level loop over `manifest.viewFields` that
reuses the existing `fromViewFieldManifestToUniversalFlatViewField`
converter (already parameterized by `viewUniversalIdentifier`). No
validator/persistence/aggregator changes.

### Notes for maintainers
- Confirm the `Manifest.viewFields` optionality convention — implemented
as a **required** array to mirror `fields`/`views`.
- Two different apps adding a column for the same field to the same view
conflicts on the existing unique `(fieldMetadataId, viewId)` partial
index; the existing `flat-view-field-validator` duplicate check surfaces
this as a structured validation error.
- `dev:add viewField` scaffolding is included (was optional in the
plan).

## Test Plan
- [x] `twenty-shared` typecheck
- [x] `twenty-sdk` 364 unit tests + `buildManifest` assembly test
(rich-app fixture) + typecheck + prettier
- [x] `twenty-server` typecheck + `lint:diff-with-main`
- [x] **Server integration suite**
`successful-manifest-update-view-field.integration-spec.ts` (4/4):
- standalone view field attaches to the standard `allPeople` view
without recreating it (sync succeeds, no
`INVALID_VIEW_DATA`/`ENTITY_ALREADY_EXISTS`)
- uninstall removes the contributed column while the standard view + its
columns remain intact
  - duplicate `(view, field)` rejected with `METADATA_VALIDATION_FAILED`
  - unknown target view rejected
- [x] Sibling `successful-manifest-update-field.integration-spec.ts`
still green (no harness regression)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-05 08:06:30 +00:00
github-actions[bot] ff9b5a5cad chore: sync AI model catalog from models.dev (#21242)
Automated daily sync of `ai-providers.json` from
[models.dev](https://models.dev).

This PR updates pricing, context windows, and model availability based
on the latest data.
New models meeting inclusion criteria (tool calling, pricing data,
context limits) are added automatically.
Deprecated models are detected based on cost-efficiency within the same
model family.

**Please review before merging** — verify no critical models were
incorrectly deprecated.

Co-authored-by: FelixMalfait <6399865+FelixMalfait@users.noreply.github.com>
2026-06-05 09:22:38 +02:00
Ratish jain 979047d004 fix: allow email change verification on self-hosted instances (#20123)
fixes #20117 

## Technical Details
Flow after fix:
1. User submits email change request
2. user.service.ts:517-524 calls sendVerificationEmail() with
verificationTrigger: EMAIL_UPDATE
3. Guard checks: verificationTrigger === SIGN_UP → false → guard skipped
4. Verification token generated, email rendered and sent via
emailService.send()
5. User receives confirmation email at new address
6. User clicks confirmation link → email update completes
---
Impact
- Minimal change: Only 3 lines modified in a single file
- No breaking changes: Sign-up verification behavior unchanged
- Security preserved: Email changes always require verification (correct
security behavior)
- Self-hosted friendly: Instance admins can disable sign-up verification
while keeping email change verification active

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-04 18:56:15 +00:00
Etienne 8bd4cbc3fd fix(ai) - optim (#21233)
1. tool-registry.service.ts, Pass precomputed catalog to
resolveSchemas()

resolveSchemas() now accepts an optional precomputedCatalog parameter.
Both getToolsByName() and getToolInfo() pass the catalog they already
fetched, eliminating a redundant getCatalog() rebuild inside
resolveSchemas().

2. database-tool.provider.ts, Skip field lookup when schemas=false

When building the catalog index (includeSchemas=false),
getFlatFieldsFromFlatObjectMetadata() is no longer called for each of
the 25 objects. The hasGroupByToolInputSchema() check is also skipped,
group_by tools are always included in the index, with the real
eligibility check deferred to learn_tools time.

--> 100/150ms gain on learn/execute_tool execution
2026-06-04 17:27:25 +00:00
github-actions[bot] f899660a40 i18n - docs translations (#21237)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-04 19:24:06 +02:00
Paul Rastoin 0877ba2ffd Fix getApplicationSubAllFlatEntityMaps to prune unrelated appIds universal identifier aggregators (#21234)
# Introduction
Atm when computing the `fromAllFlatEntityMaps` we're retrieving all the
applicationIds related metadata entities to build a flat entity maps
scoped to them ( atm always the applicationId + twenty-standard
application id ) only inter app dependency we manage for the moment

A flat entity contains universal identifier aggregator to its related
entities

The issue was that the `getApplicationSubAllFlatEntityMaps` wasn't
pruning the aggregator by app

Now added a new process phase after the initial one that will check that
all the aggregators contains universal identifiers that has been
retrieve from the appId + appId standard intersection

## TDD test
Created a very human readable ( that's a joke ) test
2026-06-04 16:53:25 +00:00
Etienne fa408c9126 add 2026-06-04 18:46:06 +02:00
Thomas Trompette 4e1cc2d831 fix: prevent workflow from disappearing after activation (#21231)
## Summary
- Fixes a regression from #21176 where activating a workflow caused it
to disappear until page refresh
- Root cause: when a draft is activated (status DRAFT→ACTIVE),
`useEffectiveDraftVersionId` incorrectly treated it as a discard because
the cached version was no longer DRAFT, filtering it from the versions
list
- Fix: only set `lastDiscardedDraftId` when `deletedAt` is actually set
on the cached version, not when the status simply changes

## Test plan
- [x] Open a workflow with a DRAFT version
- [x] Activate the workflow → verify it does NOT disappear
- [x] Discard a draft → verify header does NOT flicker between
DRAFT/ACTIVE
2026-06-04 16:42:05 +00:00
martmull c2ca90c255 feat(sdk): add runAgent() to run app agents from logic functions (#21157)
<img width="948" height="593" alt="image"
src="https://github.com/user-attachments/assets/d990fa98-3cfd-469d-ab7f-0b2d4ccf3afc"
/>

<img width="1361" height="802" alt="image"
src="https://github.com/user-attachments/assets/1091f598-49f3-4c16-92ea-1e1c200181e2"
/>


## Add `runAgent()` to the Logic Function SDK

Lets an app's logic function run one of its own AI agents server-side
and get the result back synchronously — reusing the existing agent
executor instead of a new bespoke transport.

  ### Backend
- New **`runAgent` GraphQL mutation** (metadata schema) in
`ai-agent-execution`, wrapping the existing
`AgentAsyncExecutorService.executeAgent`. Scopes the agent lookup to the
calling
  application and runs it under an application auth context.
- New `@AuthApplication()` param decorator (mirrors `@AuthWorkspace()`)
— first GraphQL resolver authenticated by an **application access
token**.
- Guarded by `WorkspaceAuthGuard` +
`SettingsPermissionGuard(PermissionFlagType.AI)`: the app's role must
grant the `AI` permission flag.

  ### SDK
- `runAgent({ agentUniversalIdentifier, prompt })` posts the mutation to
`/metadata` with the app token via a new runtime GraphQL transport.
Returns `{ result, hasNoMoreAvailableCredits
  }`.
- Refactored the connections helpers onto a shared `postAppEndpoint`
util (removes duplicated transport logic).

  ### Frontend
- App install permission modal now shows an explicit consent line —
_"Run AI agents and bill AI credits to your workspace"_ — when the app's
role requests the `AI` flag.

  ### Docs
- Documented `runAgent` and its `AI` permission-flag requirement in
_Skills & Agents_.
- Fixed outdated role-permission examples in _Roles & Permissions_
(`permissionFlags` → `permissionFlagUniversalIdentifiers`,
`PermissionFlag` → `SystemPermissionFlag`).

  ### Test plan
- [x] SDK unit tests (`run-agent.spec.ts`) — request shape, GraphQL/HTTP
error handling, missing env vars
- [x] `twenty-server`, `twenty-front`, `twenty-shared` typecheck + lint
- [ ] Manual: install an app granting the `AI` flag, call `runAgent()`
from a logic function, confirm the agent runs and credits are billed

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-06-04 16:18:27 +00:00
Raphaël Bosi 36b654bab3 Scaffold people data labs enrichment app (#21175)
# Scaffold People Data Labs enrichment app

Defines the data model for enriching **Person** and **Company** with
People Data
Labs data. **Scaffold only** — the enrichment logic (the "mapper")
follows
separately; see the package README.

## Included
- **Fields** on Person & Company (PDL base data set).
- **Enums as SELECT / MULTI_SELECT** validated against PDL canonical
files (v34.1).
- **Standard-field mapping**: no `pdl*` shadow where a standard field
exists.
- **Location → ADDRESS**; **relation** `pdlCurrentCompany` ↔
`pdlCurrentEmployees`.
- **Metadata**: `pdlId`, `pdlLikelihood`, `pdlEnrichmentStatus`,
`pdlLastEnrichedAt`, `pdlRawPayload`.
- Shared option constants + helper, indexes, and a view per object.
2026-06-04 15:54:38 +00:00
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
martmull e0d42323af Add more control on http trigger (#21216)
add "new Response" utils to define response code or content type of http
route triggered logic function responses

follow up of https://github.com/twentyhq/twenty/pull/21214

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:34:10 +00:00
Abdullah. d3a1781a59 Use serialize-javascript for JSON-LD serialization on twenty-website (#21223)
Our scanner flags the `dangerouslySetInnerHTML` in `JsonLd.tsx` as a
potential XSS sink. Since JSON-LD must be emitted as raw `<script
type="application/ld+json">` text (rendering it as a React child
HTML-entity-escapes it and corrupts the JSON, and the site is statically
generated so it must be in the SSG HTML for crawlers),
`dangerouslySetInnerHTML` is the correct, Next.js-documented approach
(the real fix is sanitizing the payload). This PR swaps our hand-rolled
`JSON.stringify().replace(/</g, ...)` for
[`serialize-javascript`](https://www.npmjs.com/package/serialize-javascript)
in `isJSON` mode, the maintained library [Next.js explicitly
recommends](https://nextjs.org/docs/app/guides/json-ld) for this, so the
script-unsafe characters are escaped by a vetted serializer rather than
custom code.
2026-06-04 14:38:18 +00:00
Thomas des Francs 70066cdcf4 Update Google Workspace and integration logo assets (#21185)
## Summary
- Replace the Gmail and Google Calendar assets with their May 2026 icon
refreshes.
- Update the shared Google, Google Meet, Microsoft Outlook, Microsoft
Calendar-provider, Docusign, Stripe, Tally, and Zapier logo assets.
- Regenerate matching website/front raster assets at their existing
dimensions.

## Preview

GitHub's file diff can render some tiny transparent SVG/WebP logo diffs
as nearly empty. This grid uses the final raster assets from the PR
branch; Stripe and Zapier use their official favicon marks.

| Logo | Preview |
| --- | --- |
| Gmail | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/gmail.webp"
width="48" height="48" alt="Gmail logo" /> |
| Google Calendar | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/calendar.webp"
width="48" height="48" alt="Google Calendar logo" /> |
| Google | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/google.webp"
width="48" height="48" alt="Google logo" /> |
| Google Meet | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/meet.webp"
width="48" height="48" alt="Google Meet logo" /> |
| Microsoft Outlook / Calendar | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/outlook.webp"
width="48" height="48" alt="Microsoft Outlook / Calendar logo" /> |
| Docusign | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/docusign.webp"
width="48" height="48" alt="Docusign logo" /> |
| Stripe | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/stripe.webp"
width="48" height="48" alt="Stripe logo" /> |
| Tally | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/tally.webp"
width="48" height="48" alt="Tally logo" /> |
| Zapier | <img
src="https://raw.githubusercontent.com/twentyhq/twenty/bonapara/update-gmail-icon/packages/twenty-website/public/images/shared/companies/logos/zapier.webp"
width="48" height="48" alt="Zapier logo" /> |


## Notes
- Microsoft Calendar now uses the current Outlook/M365 icon, matching
the provider users connect for Microsoft calendar sync.
- Docusign keeps the compact Nexus mark for the small square logo slot,
regenerated from the current official Docusign lockup.
- Stripe now uses the official favicon mark from stripe.com.
- Zapier now uses the official square favicon mark from zapier.com.

## Sources
- Gmail 2026:
https://upload.wikimedia.org/wikipedia/commons/8/8f/Gmail_icon_%282026%29.svg
- Google Calendar 2026:
https://upload.wikimedia.org/wikipedia/commons/f/fa/Google_Calendar_icon_%282026%29.svg
- Google gradient G:
https://blog.google/company-news/inside-google/company-announcements/gradient-g-logo-design/
- Google Meet 2026:
https://commons.wikimedia.org/wiki/File:Google_Meet_icon_(2026).svg
- Microsoft 365 icon refresh:
https://techcommunity.microsoft.com/blog/microsoft365insiderblog/new-microsoft-365-icons-for-the-ai-era/4458674
- Docusign logo guidelines: https://brand.docusign.com/logo
- Stripe favicon:
https://images.stripeassets.com/fzn2n1nzq965/1hgcBNd12BfT9VLgbId7By/01d91920114b124fb4cf6d448f9f06eb/favicon.svg
- Tally press kit: https://tally.so/help/press-kit
- Zapier favicon: https://zapier.com/favicon.ico

## Validation
- Confirmed touched UI SVGs transform through SVGR successfully.
- Confirmed regenerated raster dimensions and alpha metadata with Sharp.
- Reviewed a generated contact sheet at 72px, 40px, and 16px.
- Ran `git diff --check`.
- Ran `packages/twenty-ui` Vite build with ARM Node v24.5.0:
`/Users/thomascolasdesfrancs/.nvm/versions/node/v24.5.0/bin/node
../../node_modules/vite/bin/vite.js build`.

Local note: `npx vite build` fails in `packages/twenty-ui` on this
machine because that directory resolves `/usr/local/bin/node` as x64
v23.5.0 while the Yarn install links Rollup's ARM optional package. The
direct ARM Node build above passed.
2026-06-04 14:19:49 +00:00
Charles Bochet a7fed47932 fix(twenty-ui): prevent local visual diffs from polluting CI baselines (#21225)
## Summary

- **Username-prefix branches**: Local visual-diff builds now use
`charles/main` instead of `main` as the branch name, preventing local
runs from creating auto-approved reference builds that could overwrite
CI baselines.
- **Local merge-base computation**: Computes `ARGOS_REFERENCE_COMMIT`
via `git merge-base HEAD main` locally, so the Argos SDK skips `git
fetch origin <branch>` — fixing the "fatal: couldn't find remote ref"
error when running from non-pushed branches.
- **Pass `referenceCommit` to vitest plugin**: Ensures the locally
computed merge-base is forwarded to the Argos upload.

## Test plan

- [x] Verified local visual-diff works from `main` branch (branch
becomes `charles/main`, not auto-approved)
- [x] Verified local visual-diff works from a non-pushed branch
(`test/local-only-visual-diff` → build uploaded successfully)
2026-06-04 15:42:32 +02:00
Charles Bochet f4da7767f8 chore: remove Chromatic dependencies and configuration (#21221)
## Summary

- Remove `chromatic` and `@chromatic-com/storybook` devDependencies from
twenty-front
- Remove global `chromatic` Nx target from nx.json and twenty-front
project.json override
- Remove commented Chromatic Storybook addon from twenty-front
- Remove `CHROMATIC_PROJECT_TOKEN` from .env.example
- Update README to remove Chromatic sponsor reference (image was already
missing)
- Update stale Chromatic comment in toSpliced.ts

## Context

Visual regression testing has moved from Chromatic SaaS to self-hosted
Argos at `argos.twenty-internal.com`. These are dead references that are
no longer used by any CI workflow.

**Note:** Story `parameters.chromatic: { disableSnapshot: true }`
entries are intentionally kept — the Argos plugin reads them as a
fallback.

## Test plan

- Verify `yarn install` succeeds after dependency removal
- Verify no workflow references `chromatic` or `nx chromatic`
2026-06-04 13:15:23 +00:00
Charles Bochet badeaddd37 fix(ci): ensure ui-sb-test runs on push to main (#21222)
## Summary

- Add explicit `if: always() && needs.ui-sb-build.result == 'success'`
to `ui-sb-test` job

## Context

After merging #21217, the main-branch Argos baseline pipeline doesn't
work: `ui-sb-test` is silently skipped on push to main, so no
`argos-screenshots-twenty-ui` artifact is produced.

**Root cause:** `changed-files-check` is skipped on push events
(PR-only). `ui-sb-build` handles this with `if: always() && ...`, but
`ui-sb-test` has no explicit `if` — GitHub Actions propagates the skip
through the transitive dependency chain (`changed-files-check` →
`ui-sb-build` → `ui-sb-test`).

## Test plan

- Merge this PR and verify the next push to main produces the
`argos-screenshots-twenty-ui` artifact
- Verify `dispatch-main` successfully triggers ci-privileged with the
artifact
2026-06-04 15:11:38 +02:00
Charles Bochet 9042e8a542 feat(ci): Argos main baselines + local visual diff support (#21217)
## Summary

**CI: Main-branch Argos baselines**
- Run storybook build + screenshot capture on `push` to `main` in CI UI
workflow
- Add `dispatch-main` job in visual regression dispatch to forward
main-branch screenshots to ci-privileged
- Simplify `dispatch-pr` by inlining the artifact name and removing
unused `project` output

**Local visual diff support**
- Add `scripts/visual-diff.sh` for running Argos uploads locally via
tunnel
- Add `storybook:visual-diff` Nx target wrapping the script (depends on
`storybook:build`)
- Honor `STORYBOOK_URL` env in `vitest.config.ts` to reuse pre-served
static builds (mirrors twenty-front pattern)
- Support `ARGOS_BUILD_NAME`, `ARGOS_REFERENCE_BRANCH` env overrides in
vitest plugin config

## Context

Argos builds on PRs are all "Orphan" because there's no reference build
on `main` to compare against. The CI changes add the missing piece:
every merge to main now produces screenshots and uploads them to Argos
as reference builds.

The local visual diff script enables developers to run visual regression
checks from their machine against the self-hosted Argos instance via
`kubectl port-forward` (set up by the twenty-infra `argos-tunnel`
command).

## Related

- twentyhq/twenty-argos#1 (backend config for self-hosted HTTPS
redirect)
- twentyhq/twenty-infra#709 (argos-tunnel super CLI command +
self-hosted mode)

## Test plan

- [ ] Verify CI UI runs on next push to main and produces the
`argos-screenshots-twenty-ui` artifact
- [ ] Verify `dispatch-main` triggers and uploads screenshots to Argos
- [ ] Verify subsequent PR builds show diffs against the main baseline
instead of "Orphan"
- [ ] Run `ARGOS_TOKEN=<token> npx nx storybook:visual-diff twenty-ui`
locally with tunnel active
2026-06-04 14:55:08 +02:00
github-actions[bot] 100b08e827 i18n - website translations (#21220)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-04 14:47:01 +02:00
neo773 437eed0862 fix(messaging): fix reply-quotation stripping that emptied email bodies (#21118)
some synced messages were stored with empty bodies, others with the
entire reply thread re-quoted, planer was stripping entirely quoted
forwards down to nothing and not trimming inline reply history at all

switched plaintext quote stripping to `email-reply-parser`, falling back
to the full text when it strips everything so forwards don't end up
blank. kept planer for the html path, and normalized body whitespac

---------

Co-authored-by: prastoin <paul@twenty.com>
2026-06-04 12:31:53 +00:00
Rashad Karanouh 5a55021e26 feat(website): add /partners/apply standalone page (#21219)
## Summary

- Adds a standalone `/partners/apply` page — a shareable URL that opens
the partner application wizard full-page (no modal, no nav, no footer),
on a plain black background
- Adds a `slots` prop to `PartnerApplicationWizard` so it can render
outside a `Dialog.Root` context (Base UI), keeping the existing modal on
`/partners` completely untouched
- Surfaces logic function errors to the user: the API route now checks
the webhook response body for `ok: true`, so a silent backend failure no
longer shows a false success state

## Test plan

- [ ] `yarn jest --no-coverage` — 365/365 passing
- [ ] `npx oxlint -c .oxlintrc.json .` — 0 errors (1 pre-existing
warning unrelated to this PR)
- [ ] `npx oxfmt --check .` — clean
- [ ] `npx tsc --noEmit` — clean
- [ ] Visit `/partners/apply` — wizard loads full-page on black
background, no menu, no footer
- [ ] Complete the wizard and submit — redirects to `/partners/list`
- [ ] Visit `/partners` — "Become a partner" modal still opens normally
2026-06-04 12:30:26 +00:00
Raphaël Bosi 63435a7937 Twenty new ui README (#21186)
Generated a twenty new ui implementation plan README after the
discussion with @charlesBochet
2026-06-04 12:14:43 +00:00
twenty-pr[bot] 6a2c948e84 chore: bump version to 2.10.0 (#21218)
## Summary

- Moves current version to previous versions array
- Sets TWENTY_CURRENT_VERSION to the new version
- Updates TWENTY_NEXT_VERSIONS with the next minor version
- Bumps twenty-client-sdk, twenty-sdk, and create-twenty-app to the same
version

## Checklist

- [ ] Verify version constants are correct
- [ ] Verify npm package versions match

Co-authored-by: Github Action Deploy <github-action-deploy@twenty.com>
2026-06-04 14:14:18 +02:00
Etienne fba8d15f8b metada-cud-optim 2026-06-04 13:58:12 +02:00
Charles Bochet b53f1832d8 feat(ci): simplify visual regression dispatch to twenty-ui only (#21215)
## Summary

- Only trigger visual regression on `CI UI` workflow (drop `CI Front`)
- Remove tarball re-packaging step — `ci-privileged` now downloads the
artifact directly via GitHub API
- Remove `mode`/`project` parameters from the dispatch payload
(hardcoded to twenty-ui in ci-privileged)
- Pass `run_id` of the triggering CI UI workflow so ci-privileged can
fetch the correct artifact

## Context

Part of the fast visual regression CI initiative. The `ci-privileged`
workflow has been simplified to only handle `twenty-ui` screenshots
uploaded directly to Argos.

## Test plan

- [x] Full E2E verified on production: screenshots → Argos build → diff
results → PR comment
2026-06-04 13:49:44 +02:00
Paul Rastoin 3d49642d12 [AUDIT] Run knip over twenty-server (#21159)
# Introduction
Run [knip](https://knip.dev/) over twenty-server
Used config:
```json
{
  "$schema": "https://unpkg.com/knip@5/schema.json",
  "workspaces": {
    "packages/twenty-server": {
      "entry": [
        "src/main.ts",
        "src/command/command.ts",
        "src/queue-worker/queue-worker.ts",
        "src/database/scripts/setup-db.ts",
        "src/database/scripts/truncate-db.ts",
        "src/database/clickHouse/migrations/run-migrations.ts",
        "src/database/clickHouse/seeds/run-seeds.ts",
        "src/instrument.ts",
        "lingui.config.ts",
        "test/integration/graphql/codegen/index.ts",
        "test/integration/utils/setup-test.ts",
        "test/integration/utils/teardown-test.ts",
        "scripts/**/*.ts",
        "**/*.spec.ts",
        "**/*.integration-spec.ts"
      ],
      "project": ["src/**/*.ts", "test/**/*.ts", "scripts/**/*.ts"],
      "ignore": [
        "src/database/typeorm/**/migrations/**",
        "src/database/typeorm/**/*.entity.ts",
        "**/*.workspace-entity.ts",
        "**/logic-function-resource/constants/seed-project/**"
      ],
      "ignoreDependencies": ["@types/psl", "@types/aws-lambda"],
      "ignoreBinaries": ["nest", "lingui", "typeorm"]
    }
  }
}
```
2026-06-04 10:05:22 +00:00
martmull 4ad8d8e98e Set 200 code for post requests (#21214)
as title, nestJS use to set 201 for post requests but some services
(like google) requests 200 response code

See
https://discord.com/channels/1130383047699738754/1511054250971758642/1511785364027609099
for context
2026-06-04 09:14:51 +00:00
nitin 475cd9e7fe fix(upgrade): resequence workflow step logs command (#21213) 2026-06-04 08:27:43 +00:00
dependabot[bot] f0be78e629 chore(deps-dev): bump prettier from 3.4.2 to 3.8.3 (#21205)
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.2 to
3.8.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.8.3</h2>
<ul>
<li>SCSS: Prevent trailing comma in <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/pull/18471">prettier/prettier#18471</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.8.3/CHANGELOG.md#383">Changelog</a></p>
<h2>3.8.2</h2>
<ul>
<li>Support Angular v21.2</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#382">Changelog</a></p>
<h2>3.8.1</h2>
<ul>
<li>Include available <code>printers</code> in plugin type declarations
(<a
href="https://redirect.github.com/prettier/prettier/pull/18706">#18706</a>
by <a href="https://github.com/porada"><code>@​porada</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#381">Changelog</a></p>
<h2>3.8.0</h2>
<ul>
<li>Support Angular v21.1</li>
</ul>
<p><a
href="https://github.com/prettier/prettier/compare/3.7.4...3.8.0">diff</a></p>
<p>🔗 <a href="https://prettier.io/blog/2026/01/14/3.8.0">Release note
&quot;Prettier 3.8: Support for Angular v21.1&quot;</a></p>
<h2>3.7.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix comment in union type gets duplicated by <a
href="https://github.com/fisker"><code>@​fisker</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18393">prettier/prettier#18393</a></li>
<li>Fix unstable comment print in union type by <a
href="https://github.com/fisker"><code>@​fisker</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18395">prettier/prettier#18395</a></li>
<li>Avoid quote around LWC interpolations by <a
href="https://github.com/kovsu"><code>@​kovsu</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18383">prettier/prettier#18383</a></li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#374">Changelog</a></p>
<h2>3.7.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix <code>prettier.getFileInfo()</code> change that breaks VSCode
extension by <a
href="https://github.com/fisker"><code>@​fisker</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18375">prettier/prettier#18375</a></li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#373">Changelog</a></p>
<h2>3.7.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix string print when switching quotes by <a
href="https://github.com/fisker"><code>@​fisker</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18351">prettier/prettier#18351</a></li>
<li>Preserve quote for embedded HTML attribute values by <a
href="https://github.com/kovsu"><code>@​kovsu</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18352">prettier/prettier#18352</a></li>
<li>Fix comment in empty type literal by <a
href="https://github.com/fisker"><code>@​fisker</code></a> in <a
href="https://redirect.github.com/prettier/prettier/pull/18364">prettier/prettier#18364</a></li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md#372">Changelog</a></p>
<h2>3.7.1</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.8.3</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.2...3.8.3">diff</a></p>
<h4>SCSS: Prevent trailing comma in <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/pull/18471">#18471</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="scss"><code>// Input
$value: if(sass(false): 1; else: -1);
<p>// Prettier 3.8.2
$value: if(
sass(false): 1; else: -1,
);</p>
<p>// Prettier 3.8.3
$value: if(sass(false): 1; else: -1);
</code></pre></p>
<h1>3.8.2</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.8.1...3.8.2">diff</a></p>
<h4>Angular: Support Angular v21.2 (<a
href="https://redirect.github.com/prettier/prettier/pull/18722">#18722</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19034">#19034</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<p>Exhaustive typechecking with <code>@default never;</code></p>
<!-- raw HTML omitted -->
<pre lang="html"><code>&lt;!-- Input --&gt;
@switch (foo) {
  @case (1) {}
  @default never;
}
<p>&lt;!-- Prettier 3.8.1 --&gt;
SyntaxError: Incomplete block &quot;default never&quot;. If you meant to
write the @ character, you should use the &quot;&amp;<a
href="https://redirect.github.com/prettier/prettier/issues/64">#64</a>;&quot;
HTML entity instead. (3:3)</p>
<p>&lt;!-- Prettier 3.8.2 --&gt;
<a href="https://github.com/switch"><code>@​switch</code></a> (foo) {
<a href="https://github.com/case"><code>@​case</code></a> (1) {}
<a href="https://github.com/default"><code>@​default</code></a> never;
}
</code></pre></p>
<p><code>arrow function</code> and <code>instanceof</code>
expressions.</p>
<!-- raw HTML omitted -->
<pre lang="html"><code>&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/prettier/commit/d7108a79ec745c04292aabf22c4c1adbd690b191"><code>d7108a7</code></a>
Release 3.8.3</li>
<li><a
href="https://github.com/prettier/prettier/commit/177f90898170d363ef64fde663e4d13170688bfe"><code>177f908</code></a>
Prevent trailing comma in SCSS <code>if()</code> function (<a
href="https://redirect.github.com/prettier/prettier/issues/18471">#18471</a>)</li>
<li><a
href="https://github.com/prettier/prettier/commit/1cd40668c3d6f2f4cf9d87bbc9096d92361b2606"><code>1cd4066</code></a>
Release <code>@​prettier/plugin-oxc</code><a
href="https://github.com/0"><code>@​0</code></a>.1.4</li>
<li><a
href="https://github.com/prettier/prettier/commit/a8700e245038cd8cc0cf28ef06ffedbcb3fc2dfc"><code>a8700e2</code></a>
Update oxc-parser to v0.125.0</li>
<li><a
href="https://github.com/prettier/prettier/commit/752157c78eca6f0a30e5d5cb513b682c5ecfa01e"><code>752157c</code></a>
Fix tests</li>
<li><a
href="https://github.com/prettier/prettier/commit/053fd418e180b12fa2014260212fae831f5fc5ec"><code>053fd41</code></a>
Bump Prettier dependency to 3.8.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/904c6365ec46726fd0e21021c52ae934b7e5abc6"><code>904c636</code></a>
Clean changelog_unreleased</li>
<li><a
href="https://github.com/prettier/prettier/commit/dc1f7fcc508d116cbf1644d69a1f0eb93e40d4a4"><code>dc1f7fc</code></a>
Update dependents count</li>
<li><a
href="https://github.com/prettier/prettier/commit/b31557cf331a02acf83e7e29d1001b070189a0d9"><code>b31557c</code></a>
Release 3.8.2</li>
<li><a
href="https://github.com/prettier/prettier/commit/96bbaeda0525bf758e464aed2f939d739a85c315"><code>96bbaed</code></a>
Support Angular v21.2 (<a
href="https://redirect.github.com/prettier/prettier/issues/18722">#18722</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/3.4.2...3.8.3">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for prettier since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=prettier&package-manager=npm_and_yarn&previous-version=3.4.2&new-version=3.8.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 09:45:41 +02:00
dependabot[bot] fe5337b7d0 chore(deps-dev): bump @types/passport-microsoft from 2.1.0 to 2.1.1 (#21204)
Bumps
[@types/passport-microsoft](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/passport-microsoft)
from 2.1.0 to 2.1.1.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/passport-microsoft">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/passport-microsoft&package-manager=npm_and_yarn&previous-version=2.1.0&new-version=2.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 09:45:19 +02:00
dependabot[bot] f50e471385 chore(deps): bump @mantine/hooks from 8.3.15 to 8.3.18 (#21203)
Bumps
[@mantine/hooks](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks)
from 8.3.15 to 8.3.18.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mantinedev/mantine/releases">@​mantine/hooks's
releases</a>.</em></p>
<blockquote>
<h2>8.3.18</h2>
<p>This is the last 8.x release. You are welcome to test 9.0 alpha
version and provide feedback before its release on March 31 – <a
href="https://alpha.mantine.dev/changelog/9-0-0/">https://alpha.mantine.dev/changelog/9-0-0/</a></p>
<ul>
<li><code>[@mantine/core]</code> PasswordInput: Fix styles api props not
resolving correctly in theme (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8716">#8716</a>)</li>
</ul>
<h2>8.3.17</h2>
<h2>Changes</h2>
<ul>
<li><code>[@mantine/core]</code> Stepper: Fix Google Translate
compatibility issues (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8744">#8744</a>)</li>
<li><code>[@mantine/hooks]</code> use-list-state: Add memoization to all
handlers (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8739">#8739</a>)</li>
</ul>
<h2>8.3.16</h2>
<h2>What's Changed</h2>
<ul>
<li><code>[@mantine/modals]</code> Fix <code>onClose</code> being called
multiple times (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8727">#8727</a>)</li>
<li><code>[@mantine/core]</code> Tooltip: Fix component not throwing
erro when used with string (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8694">#8694</a>)</li>
<li><code>[@mantine/core]</code> NumberInput: Fix incorrect decimal
separator parsing in <code>onPaste</code></li>
<li><code>[@mantine/core]</code> AppShell: Fix
<code>layout=&quot;alt&quot;</code> not working with
<code>mode=&quot;static&quot;</code></li>
<li><code>[@mantine/stotlight]</code> Fix actions list being rendered
when nothing found message was not set (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8592">#8592</a>)</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/mantinedev/mantine/compare/8.3.15...8.3.16">https://github.com/mantinedev/mantine/compare/8.3.15...8.3.16</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mantinedev/mantine/commit/530249feb498d74d5ea849d2984c86efd072f38c"><code>530249f</code></a>
[release] Version: 8.3.18</li>
<li><a
href="https://github.com/mantinedev/mantine/commit/bac61d6fc21be84bd80d1d9232a605c05692ae75"><code>bac61d6</code></a>
[release] Version: 8.3.17</li>
<li><a
href="https://github.com/mantinedev/mantine/commit/6b3fdee137584aece43b88b62d8c207a58c35295"><code>6b3fdee</code></a>
[refactor] Fix formatting</li>
<li><a
href="https://github.com/mantinedev/mantine/commit/c048f996cc1a8287bd9eaba1bb59bc6637316b90"><code>c048f99</code></a>
[<code>@​mantine/hooks</code>] use-list-state: Add memoization to all
handlers (<a
href="https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks/issues/8739">#8739</a>)</li>
<li><a
href="https://github.com/mantinedev/mantine/commit/dbb8732ca3350259e0cf7b2536d0bcc885b1b587"><code>dbb8732</code></a>
[release] Version: 8.3.16</li>
<li>See full diff in <a
href="https://github.com/mantinedev/mantine/commits/8.3.18/packages/@mantine/hooks">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@mantine/hooks&package-manager=npm_and_yarn&previous-version=8.3.15&new-version=8.3.18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 09:45:10 +02:00
Félix Malfait a3a44c8315 fix(front): settings skeleton, app-detail header & empty favorites (#21209)
Three small post-redesign UI fixes. Each is an independent commit, so
they can be split into separate PRs if preferred.

## 1. Settings loading skeleton — match the rounded-card layout

The redesign (#21131) moved settings chrome into a rounded card
(`SettingsPageLayout`: bordered header with breadcrumb + centered title,
optional secondary bar, 760px body), but `SettingsSkeletonLoader` still
rendered the old flat `PageHeader` + `PageBody` — so pages painted as a
full-width flat bar then snapped into the card.

- `SettingsSkeletonLoader` now reproduces the card and **reuses the real
`SettingsPageHeader` + `SettingsPageContainer`**, so the frame aligns by
construction; the card CSS is replicated (not `SettingsPageLayout`) to
avoid the layout's side effects (hotkeys, side panel, info banner).
- It's **composed with `SettingsSectionSkeletonLoader`** so the loading
body is identical whether or not chrome is present. Rule: no chrome on
screen yet → full-page skeleton; chrome already on screen → body-only
`SettingsSectionSkeletonLoader` (the admin Enterprise tab now uses it,
matching its sibling tabs). A short comment on each component documents
this.

## 2. Application detail header — pass a plain title

`SettingsApplicationDetails` / `SettingsAvailableApplicationDetails`
passed a custom `SettingsApplicationDetailTitle` (avatar + name +
multi-line description, fixed width) into `SettingsPageLayout`'s
**centered single-line title slot**, which broke the header. They now
pass the app's display name like every other page. The available-app
"unlisted" notice moves into the body as a reusable `InlineBanner`; the
now-unused `SettingsApplicationDetailTitle` is removed.

## 3. Navigation — hide Favorites when empty

Always rendering the Favorites section (#21087) left a stray "Favorites"
title above Workspace for users with no favorites. It now renders only
when at least one favorite exists (redundant per-child guards dropped).
Note: the "+ add favorite" entry point therefore appears once you have
≥1 favorite; the first favorite is created from a record/view as before.

## Verification
- `nx typecheck twenty-front`  · `oxlint` + `oxfmt --check` on changed
files 
- i18n catalogs intentionally untouched — handled by the repo's separate
i18n pipeline.
2026-06-04 09:32:36 +02:00
github-actions[bot] e99116b0a5 chore: sync AI model catalog from models.dev (#21212)
Automated daily sync of `ai-providers.json` from
[models.dev](https://models.dev).

This PR updates pricing, context windows, and model availability based
on the latest data.
New models meeting inclusion criteria (tool calling, pricing data,
context limits) are added automatically.
Deprecated models are detected based on cost-efficiency within the same
model family.

**Please review before merging** — verify no critical models were
incorrectly deprecated.

Co-authored-by: FelixMalfait <6399865+FelixMalfait@users.noreply.github.com>
2026-06-04 09:31:01 +02:00
Félix Malfait 2ac515894b feat(settings): add Logs as a dedicated tab in General settings (#21180)
## What & why

The audit-log viewer lived as a full-screen page reachable only via a
"View Logs" button buried in the **Security** tab. This surfaces it as
the **third tab in General settings** (`General | Security | Logs`),
consistent with the other tabs.

## Changes

- **Relocated** the event-logs module
`pages/settings/security/event-logs/` → `modules/settings/event-logs/`
and render it as tab content instead of a `FullScreenContainer` page.
Dropped `SettingsPath.EventLogs`, its route, and the fullscreen handling
in favor of the `general#logs` hash tab.
- **Security tab:** removed the "View Logs" entry; kept the
log-retention setting there.
- **In-tab gating** (shown to users with the Security permission):
Enterprise upgrade card when not entitled, a clear "ClickHouse not
configured" placeholder otherwise (derived from client config), and the
query is skipped when disabled. Replaces a bespoke error component that
string-matched error messages with the shared `SettingsEmptyPlaceholder`
/ `SettingsEnterpriseFeatureGateCard`.
- **Layout:** boxed content column with the table selector + filters
grouped in a `Card` and the results table below, matching settings
conventions. Kept the existing fixed filters (page/event name, member,
period) rather than recreating the record-view filter chips (those are
tightly coupled to record/view context).

Frontend + `twenty-shared` only — no changes to the log query or data.

## Test plan

- [x] `npx nx typecheck twenty-front` and `npx nx lint twenty-front`
pass
- [x] Settings → General shows three tabs; Logs is the third; breadcrumb
stays "Workspace / General"
- [x] With Enterprise + ClickHouse: table selector, filters, refresh,
and the paginated table work
- [x] Non-Enterprise: Enterprise upgrade card shown; no failing query
fires
- [ ] Enterprise without ClickHouse: shows the "ClickHouse not
configured" placeholder
- [ ] Security tab still shows the log-retention setting and the "View
Logs" button is gone
- [ ] A user without the Security permission sees neither the Security
nor Logs tab
2026-06-04 08:47:23 +02:00
Charles Bochet e6614299c6 feat(ci): integrate Argos visual regression via vitest screenshots (#21210)
## Summary

- Adds `@argos-ci/storybook` vitest plugin to `twenty-ui` for automatic
screenshot capture during vitest storybook tests
- Uploads captured screenshots (PNG, ~5MB) as a CI artifact instead of
passing the full storybook build
- Updates the visual regression dispatch workflow to pass
`mode=argos-screenshots` to ci-privileged, which then uploads
screenshots to Argos via CLI

This replaces the 10-minute Storybook screenshot capture with a ~30s
vitest browser-mode approach. The heavy screenshot work happens on free
public runners, while ci-privileged only handles the Argos API upload
(keeping secrets private).

## Architecture

```
twenty (public, free runners)          ci-privileged (private)
─────────────────────────────          ────────────────────────
1. Build storybook-static              4. Download screenshots artifact
2. Vitest captures screenshots         5. `argos upload` → Argos API
3. Upload screenshots artifact         6. Poll for results
                                       7. Post PR comment
```

## Test plan

- [x] Verified locally: vitest captures 225 screenshots in ~28s
- [x] Verified `@argos-ci/cli upload` successfully creates Argos build
from captured screenshots
- [x] Argos diffs computed and results visible via API
- [ ] CI runs end-to-end on a PR
2026-06-04 08:46:37 +02:00
Abdullah. ccffc4a1ea Fix axios related dependabot alerts generated against root yarn.lock (#21187)
Fixes the following Dependabot alerts:
https://github.com/twentyhq/twenty/security/dependabot?q=is%3Aopen+package%3Aaxios+manifest%3Ayarn.lock+has%3Apatch

Upgraded the referenced version in root yarn.lock. Creating a separate
PR for the nested ones to keep the updates isolated (e.g.
/seed-dependencies/yarn.lock).
2026-06-03 23:19:42 +00:00
github-actions[bot] 685c0ddd12 i18n - docs translations (#21195)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-03 21:43:27 +02:00
Etienne 15eaabdbc1 fix(ai) - optimize crud tools (#21133)
- **Add delete many**, `delete_many_{object}` added alongside the
existing `delete_one_{object}`.
- **Uniformize naming**, crud module, type names, and MCP helper
constants renamed for consistency.
- **Optimize tool schema (learn phase)**
  - `find_many(_companies)`: **7 158 → 2 700 tokens**
  - `find_one(_company)`: **280 → 126 tokens**
  -  ....
- Main mechanism: `reused: 'ref'` (line 7 of
`to-tool-json-schema.util.ts`). Zod walks the schema tree, tracks which
Zod schema instances appear more than once, and emits each reused
instance exactly once in `$defs`, replacing all subsequent occurrences
with a `$ref`. Works because filter and value schemas are now extracted
as shared objects.

- **Optimize system prompt (tool catalog)**, DATABASE_CRUD section
restructured to list operation patterns (`find_many_{object}`, …) once +
objects once, instead of the full N×M cross-product of tool names.
- **Optimize execute_tool**, shared record-properties schema (same
`$defs` deduplication applies at call time); introduced `upsert_many`;
added `selectedFields` to `find_*` so the agent only fetches the fields
it needs.
2026-06-03 17:57:40 +00:00
Abdullah. 50c9b68e81 Update axios in package.json of seed-dependencies (#21190)
Fixes the following Dependabot alerts:
https://github.com/twentyhq/twenty/security/dependabot?q=is%3Aopen+package%3Aaxios+manifest%3Apackages%2Ftwenty-server%2Fsrc%2Fengine%2Fcore-modules%2Fapplication%2Fapplication-package%2Fconstants%2Fseed-dependencies%2Fyarn.lock+has%3Apatch

Follow up to the alerts not addressed in
[this](https://github.com/twentyhq/twenty/pull/21187) PR.
2026-06-03 17:49:02 +00:00
github-actions[bot] 34d893f9fd i18n - translations (#21192)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-03 19:57:05 +02:00
Priyanshu Bartwal d3a7ea0790 Fix: Not able to add multiple handles to Blocklist in settings/accounts (#21049)
Fixes: #21031

### Root cause:
This feature was never fully implemented even though the placeholder
text suggested it was supported. It could only update one handle at a
time.

### Fix
Fixed the zod validation to validate each handle separately.
Used `useCreateManyRecords` to update multiple handles at the same time.

### Before:
<img width="2032" height="1162" alt="Screenshot 2026-05-29 at 3 25
12 PM"
src="https://github.com/user-attachments/assets/ae6b6ae3-ed38-4410-801e-11f514773681"
/>

### After:


https://github.com/user-attachments/assets/69354129-422a-41de-baf7-fa5a28f01f3f

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-06-03 17:34:51 +00:00
github-actions[bot] 49828d9379 i18n - translations (#21191)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-06-03 19:17:44 +02:00
Etienne dd0039ca1c feat(mcp) - optimize instruction prompt and hide get_tool_catalog (#21183)
Workspace-aware initialize.instructions

- Deleted the static mcp-server-instructions.const.ts
- Created build-mcp-server-instructions.util.ts — a comprehensive system
prompt with identity, object list, tool grammar, routing decision tree,
intent mapping, skills vs tools, safety constraints, and data efficiency
guidelines
- Created McpInstructionBuilderService — fetches workspace-specific
object names + skill names and injects them into the instructions


Hide/deprecate get_tool_catalog

Benefit : skip first MCP call (tools are included in instruction)
2026-06-03 16:58:32 +00:00
Marie 4b15b949f3 Provide additional logsobservability to workflow runs (per node) (#21142)
Surfaces per-step "Logs" tabs in the workflow run side panel so users
can see what each step actually did (model + tokens + tool calls for AI,
console output for serverless functions, request/response for HTTP,
recipients/body for Email).

<img width="546" height="501" alt="ai_agent_without_websearch"
src="https://github.com/user-attachments/assets/c6ca3518-9489-4484-a570-3d0569ff3b03"
/>

## Storage

- New `stepLogs` JSONB column on the `workflowRun` workspace entity,
typed as `Record<string, WorkflowRunStepLog>` (keyed by step id).
- Schema lives in `twenty-shared`: `workflowRunStepLogSchema` with a
discriminated `details.type` union for `AI_AGENT | CODE | HTTP_REQUEST |
EMAIL` — frontends and backends consume the same Zod-inferred type.
- Field is added to existing workspaces via a workspace upgrade command
(`2-9 add-workflow-run-step-logs-field`); the standard-object metadata
declares it for new workspaces.
- Writes happen atomically per step in
`WorkflowRunStepLogWorkspaceService.setStepLog` using `jsonb_set`. That
lets concurrent steps in the same run write their own keys without
contending with the existing lock around `workflowRun.state`.
- Per-step payload is hard-capped at 256 KB; anything larger is dropped
with a `logger.warn`, so a pathological tool call can never bloat a row.
See below for more information.

## How logs are produced

**Aalmost everything was already being collected; this PR mostly
persists and renders it.**

- **AI agent** — `AgentAsyncExecutorService` already tracked token
usage, model id, native web-search count, and the AI SDK's `steps[]`. We
map those into the log via `mapAiStepsToToolCallLogs` (`searchVector`
stripped from record outputs, per-call input/output capped at 32/64 KB,
max 200 tool calls per step). The only new measurement is a wall-clock
`durationMs` taken around `executeAgent`, and we now fold native
web-search cost into the displayed `totalCostInDollars` (it was already
billed, just not shown).
- **Code / serverless function** — reuses the `console.log` output the
function runner already returns (`logsByLevel`);
`build-code-step-log.util` only repackages it.
- **HTTP request** — built from the action's existing input/output via
`build-http-request-step-log.util`. No new signals collected.
- **Email (send / draft)** — added `sanitizedHtmlBody` + `plainTextBody`
to the existing tool outputs (a small additive change), then
`build-email-step-log.util` consumes them.

No additional AI inference or external calls are made for logging — the
cost is a small CPU overhead per step plus the JSONB write.

## Security

The log surface intentionally shows whatever the workflow touched, which
made redaction and sanitization the main design concern.

- **HTTP — secrets in headers**: existing `SENSITIVE_HEADER_NAMES` set
(Authorization, Cookie, …) replaced with `[redacted]` in both request
and response.
- **HTTP — secrets in URLs**: `SENSITIVE_URL_PARAM_NAMES` (e.g.
`api_key`, `token`, `access_token`) replaced in the query string via
`URL`-based parsing.
- **HTTP — secrets in bodies**: `SENSITIVE_BODY_KEY_REGEX` deep-walks
JSON request/response bodies (object input or stringified JSON) and
redacts matching keys. Applied to the `error` field too, since
transport-layer errors sometimes embed structured payloads.
- **Email — XSS risk in body preview**: tool outputs now expose a
server-side `sanitizedHtmlBody`; the log builder prefers it over the raw
user-authored `input.body`, with `plainTextBody` as a second fallback.
The original raw body is only used if sanitization didn't happen (e.g.
tool failed before composing).
- **AI — internal/noisy data**: `searchVector` (Postgres tsvector
strings) is stripped from record outputs returned by Twenty tools to
avoid leaking internal full-text-search payloads.
- **DB bloat / runaway agents**: 256 KB per-step cap + 32 KB / 64 KB
per-tool-call input/output cap + 200 tool calls per step.

<img width="547" height="307" alt="logic_function"
src="https://github.com/user-attachments/assets/dd4a3d16-67f2-434b-95b3-bdcaf9ed053d"
/>

## More details on Log size & truncation

Logs are stored in `workflowRun.stepLogs` (JSONB), keyed by `stepId`.

### Per-step cap

Each step's log is hard-capped at **256 KB** (`MAX_STEP_LOG_BYTES` in
`WorkflowRunStepLogWorkspaceService.setStepLog`).

For ~99% of workflows this is roomy — typical real-world sizes:
- Code / serverless function: 1–20 KB
- HTTP request: 5–70 KB
- Email: 5–30 KB
- AI agent (a handful of tool calls): 5–50 KB

### Two layers of bounding

1. **Per-field truncation** in each builder (before writing):
   - **Code**: ≤ 500 entries, ≤ 4 KB per message, ≤ 8 KB stack trace
   - **HTTP**: ≤ 32 KB per body (request + response), UTF-8 byte-aware
   - **Email**: ≤ 8 KB body preview, UTF-8 byte-aware
- **AI agent**: ≤ 32 KB tool input, ≤ 64 KB tool output, ≤ 200 tool
calls/step

2. **Global per-step safety net** at write time: if the assembled
`stepLog` still exceeds 256 KB, the write is **dropped entirely** with a
`logger.warn`. The workflow itself keeps running unaffected.

### What this means in practice

- **Safe**: workflow execution, step results, downstream steps — never
blocked by log size.
- **Safe**: iterators (each iteration overwrites the previous log for
that `stepId`, so they can't accumulate).
- **Safe**: step retries (same `stepId` is overwritten, not appended).
- **Possible**: an AI agent step with many large tool outputs (e.g., 50+
heavy `web_search` calls) can exceed 256 KB → the **entire** step's log
is dropped, side panel shows "No logs were recorded for this step". The
user has no explicit signal that the log was dropped due to size (only
server-side warn).
- **Possible** (theoretical): a workflow with hundreds of distinct steps
could push the row toward Postgres's internal ~256 MB jsonb limit.
Beyond that, individual `jsonb_set` writes would error and be swallowed
by the action's try/catch — workflow still completes.

### Possible future hardening (not in this PR)

- Replace "drop entire log" with a stub that preserves the summary card
(cost, duration, status) and marks `truncated.reason = 'size_cap'`.
- Surface size-drops in the UI (similar to the existing
`<StyledTruncatedNotice>`).
- Emit a metric so dropped logs are observable in dashboards.
2026-06-03 16:53:47 +00:00
martmull 0671ff3de5 Fix lambda error (#21179)
- move twenty-sdk from dependencies to devDependencies
- add documentation about breaking
- add warning about moving the package to dev dependencies
2026-06-03 16:45:51 +00:00
Etienne 7fcbc45017 perf(ai) - per-tool schema resolution in resolveSchemas (#21188)
learn_tools(["find_many_companies"]) goes from ~170ms → ~2ms (85x
faster, measured with 40 objects / empty fields — real workspaces would
see even bigger savings). Previously it generated schemas for all ~250
tools and discarded 249;

now it generates exactly the requested one(s).
2026-06-03 16:29:26 +00:00
1054 changed files with 45994 additions and 11803 deletions
+28 -3
View File
@@ -6,18 +6,43 @@ permissions:
on:
push:
tags:
- 'v*'
- 'twenty/v*'
- 'sdk/v*'
defaults:
run:
shell: bash --noprofile --norc -euo pipefail {0}
jobs:
deploy-tag:
dispatch-tag:
timeout-minutes: 3
runs-on: ubuntu-latest
steps:
- name: Resolve dispatch event from tag family
id: target
env:
REF_NAME: ${{ github.ref_name }}
run: |
case "$REF_NAME" in
twenty/v*)
event_type=auto-deploy-twenty
;;
sdk/v*)
event_type=auto-publish-npm
;;
*)
echo "Unsupported tag '$REF_NAME', expected 'twenty/v*' or 'sdk/v*'." >&2
exit 1
;;
esac
printf 'event_type=%s\n' "$event_type" >> "$GITHUB_OUTPUT"
- name: Repository Dispatch
env:
GH_TOKEN: ${{ secrets.TWENTY_INFRA_TOKEN }}
EVENT_TYPE: ${{ steps.target.outputs.event_type }}
REF_NAME: ${{ github.ref_name }}
run: |
gh api repos/twentyhq/twenty-infra/dispatches \
-f event_type=auto-deploy-tag \
-f "event_type=$EVENT_TYPE" \
-f "client_payload[github][ref_name]=$REF_NAME"
@@ -120,7 +120,7 @@ jobs:
echo "--- Checking package.json references correct SDK version ---"
node -e "
const pkg = require('./package.json');
const sdkVersion = pkg.dependencies['twenty-sdk'];
const sdkVersion = pkg.devDependencies['twenty-sdk'];
if (!sdkVersion.startsWith('0.0.0-ci.')) {
console.error('Expected twenty-sdk version to start with 0.0.0-ci., got:', sdkVersion);
process.exit(1);
-63
View File
@@ -1,63 +0,0 @@
name: "Release: on merge"
permissions:
contents: write
on:
pull_request:
types:
- closed
defaults:
run:
shell: bash --noprofile --norc -euo pipefail {0}
jobs:
tag_and_release:
timeout-minutes: 10
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release')
steps:
- name: Check PR Author
id: check_author
env:
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
set -euo pipefail
if [[ "$PR_AUTHOR" != "github-actions[bot]" ]]; then
echo "PR author ($PR_AUTHOR) is not trusted. Exiting."
exit 1
fi
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: main
- name: Get version from PR title
id: extract_version
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
set -euo pipefail
VERSION=$(printf '%s' "$PR_TITLE" | sed -n 's/.*Release v\([0-9][0-9.]*\).*/\1/p')
if [ -z "$VERSION" ]; then
echo "No valid version found in PR title. Exiting."
exit 1
fi
printf 'VERSION=%s\n' "$VERSION" >> "$GITHUB_ENV"
- name: Push new tag
run: |
set -euo pipefail
git config --global user.name 'Github Action Deploy'
git config --global user.email 'github-action-deploy@twenty.com'
git tag "v${{ env.VERSION }}"
git push origin "v${{ env.VERSION }}"
- uses: release-drafter/release-drafter@09c613e259eb8d4e7c81c2cb00618eb5fc4575a7 # v5
if: contains(github.event.pull_request.labels.*.name, 'create_release')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: v${{ env.VERSION }}
+12 -10
View File
@@ -27,11 +27,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Login to Docker Hub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Pull base images through Google's Docker Hub mirror — avoids Docker Hub
# rate limits and needs no credentials (this repo is public).
- name: Configure Docker Hub mirror
run: |
echo '{"registry-mirrors":["https://mirror.gcr.io"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Run compose
run: |
echo "Patching docker-compose.yml..."
@@ -102,11 +103,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Login to Docker Hub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Pull base images through Google's Docker Hub mirror — avoids Docker Hub
# rate limits and needs no credentials (this repo is public).
- name: Configure Docker Hub mirror
run: |
echo '{"registry-mirrors":["https://mirror.gcr.io"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
- name: Create frontend placeholder
run: |
mkdir -p packages/twenty-front/build
+18 -16
View File
@@ -3,6 +3,8 @@ name: CI UI
on:
pull_request:
merge_group:
push:
branches: [main]
permissions:
contents: read
@@ -13,7 +15,7 @@ concurrency:
jobs:
changed-files-check:
if: github.event_name != 'merge_group'
if: github.event_name == 'pull_request'
uses: ./.github/workflows/changed-files.yaml
with:
files: |
@@ -40,7 +42,10 @@ jobs:
run: npx nx ${{ matrix.task }} twenty-ui
ui-sb-build:
needs: changed-files-check
if: needs.changed-files-check.outputs.any_changed == 'true'
if: >-
always() &&
(github.event_name == 'push' ||
needs.changed-files-check.outputs.any_changed == 'true')
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
@@ -62,8 +67,7 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
needs: ui-sb-build
env:
STORYBOOK_URL: http://localhost:6007
if: always() && needs.ui-sb-build.result == 'success'
steps:
- name: Fetch custom Github Actions and base branch history
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
@@ -82,22 +86,20 @@ jobs:
run: |
cd packages/twenty-ui
npx playwright install
- name: Serve storybook & run tests
run: |
npx http-server packages/twenty-ui/storybook-static --port 6007 --silent &
timeout 30 bash -c 'until curl -sf http://localhost:6007 > /dev/null 2>&1; do sleep 1; done'
npx nx storybook:test twenty-ui
- name: Run storybook tests
run: npx nx storybook:test twenty-ui
- name: Upload screenshots for visual regression
if: always() && !cancelled()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: argos-screenshots-twenty-ui
path: packages/twenty-ui/screenshots
retention-days: 1
ci-ui-status-check:
if: always() && !cancelled()
timeout-minutes: 5
runs-on: ubuntu-latest
needs:
[
changed-files-check,
ui-task,
ui-sb-build,
ui-sb-test,
]
needs: [changed-files-check, ui-task, ui-sb-build, ui-sb-test]
steps:
- name: Fail job if any needs failed
if: contains(needs.*.result, 'failure')
@@ -1,52 +1,33 @@
name: Visual Regression Dispatch
# Uses workflow_run to dispatch visual regression to ci-privileged.
# This runs in the context of the base repo (not the fork), so it has
# access to secrets — making it work for external contributor PRs.
# Dispatches visual regression processing to ci-privileged after CI completes.
# Runs in the context of the base repo (not the fork) so it has access to secrets,
# making it work for external contributor PRs.
on:
workflow_run:
workflows: ['CI Front', 'CI UI']
workflows: ['CI UI']
types: [completed]
permissions:
actions: read
contents: read
pull-requests: read
jobs:
dispatch:
dispatch-pr:
if: >-
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Determine project and artifact name
id: project
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const workflowName = context.payload.workflow_run.name;
if (workflowName === 'CI Front') {
core.setOutput('project', 'twenty-front');
core.setOutput('artifact_name', 'storybook-static');
core.setOutput('tarball_name', 'storybook-twenty-front-tarball');
core.setOutput('tarball_file', 'storybook-twenty-front.tar.gz');
} else if (workflowName === 'CI UI') {
core.setOutput('project', 'twenty-ui');
core.setOutput('artifact_name', 'storybook-twenty-ui');
core.setOutput('tarball_name', 'storybook-twenty-ui-tarball');
core.setOutput('tarball_file', 'storybook-twenty-ui.tar.gz');
} else {
core.setFailed(`Unexpected workflow: ${workflowName}`);
}
- name: Check if storybook artifact exists
- name: Check if screenshots artifact exists
id: check-artifact
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const artifactName = '${{ steps.project.outputs.artifact_name }}';
const artifactName = 'argos-screenshots-twenty-ui';
const runId = context.payload.workflow_run.id;
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
@@ -59,7 +40,7 @@ jobs:
core.setOutput('exists', found ? 'true' : 'false');
if (!found) {
core.info(`Artifact "${artifactName}" not found in run ${runId} — storybook build was likely skipped`);
core.info(`Artifact "${artifactName}" not found in run ${runId} — skipping`);
}
- name: Get PR number
@@ -71,8 +52,6 @@ jobs:
const headBranch = context.payload.workflow_run.head_branch;
const headRepo = context.payload.workflow_run.head_repository;
// workflow_run.pull_requests is empty for fork PRs,
// so fall back to searching by head label (owner:branch)
let pullRequests = context.payload.workflow_run.pull_requests;
let prNumber;
@@ -80,7 +59,7 @@ jobs:
prNumber = pullRequests[0].number;
} else {
const headLabel = `${headRepo.owner.login}:${headBranch}`;
core.info(`pull_requests is empty (likely a fork PR), searching by head label: ${headLabel}`);
core.info(`Searching for PR by head label: ${headLabel}`);
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
@@ -96,7 +75,7 @@ jobs:
}
if (!prNumber) {
core.info('No pull request found for this workflow run — skipping');
core.info('No pull request found — skipping');
core.setOutput('has_pr', 'false');
return;
}
@@ -105,43 +84,100 @@ jobs:
core.setOutput('has_pr', 'true');
core.info(`PR #${prNumber}`);
- name: Download storybook artifact from triggering run
- name: Compute merge-base for Argos reference
if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true'
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
id: merge-base
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
name: ${{ steps.project.outputs.artifact_name }}
path: storybook-static
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
script: |
const headSha = context.payload.workflow_run.head_sha;
- name: Package storybook
if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true'
run: tar -czf /tmp/${{ steps.project.outputs.tarball_file }} -C storybook-static .
try {
const { data: comparison } = await github.rest.repos.compareCommitsWithBasehead({
owner: context.repo.owner,
repo: context.repo.repo,
basehead: `main...${headSha}`,
});
- name: Upload storybook tarball
if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ steps.project.outputs.tarball_name }}
path: /tmp/${{ steps.project.outputs.tarball_file }}
retention-days: 1
if (comparison.merge_base_commit?.sha) {
core.setOutput('sha', comparison.merge_base_commit.sha);
core.info(`Merge base: ${comparison.merge_base_commit.sha}`);
} else {
core.info('Could not determine merge base — will skip reference_commit');
core.setOutput('sha', '');
}
} catch (error) {
core.warning(`Failed to compute merge base: ${error instanceof Error ? error.message : String(error)}`);
core.setOutput('sha', '');
}
- name: Dispatch to ci-privileged
if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true'
env:
GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }}
PR_NUMBER: ${{ steps.pr-info.outputs.pr_number }}
RUN_ID: ${{ github.run_id }}
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
REPOSITORY: ${{ github.repository }}
BRANCH: ${{ github.event.workflow_run.head_branch }}
COMMIT: ${{ github.event.workflow_run.head_sha }}
REFERENCE_COMMIT: ${{ steps.merge-base.outputs.sha }}
run: |
ARGS=(
--method POST
-f event_type=visual-regression
-f "client_payload[pr_number]=$PR_NUMBER"
-f "client_payload[run_id]=$WORKFLOW_RUN_ID"
-f "client_payload[repo]=$REPOSITORY"
-f "client_payload[branch]=$BRANCH"
-f "client_payload[commit]=$COMMIT"
)
if [ -n "$REFERENCE_COMMIT" ]; then
ARGS+=(-f "client_payload[reference_commit]=$REFERENCE_COMMIT")
fi
gh api repos/twentyhq/ci-privileged/dispatches "${ARGS[@]}"
dispatch-main:
if: >-
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Check if screenshots artifact exists
id: check-artifact
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const runId = context.payload.workflow_run.id;
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId,
});
const found = artifacts.artifacts.some(a => a.name === 'argos-screenshots-twenty-ui');
core.setOutput('exists', found ? 'true' : 'false');
if (!found) {
core.info(`Artifact not found in run ${runId} — skipping`);
}
- name: Dispatch to ci-privileged
if: steps.check-artifact.outputs.exists == 'true'
env:
GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }}
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
REPOSITORY: ${{ github.repository }}
PROJECT: ${{ steps.project.outputs.project }}
BRANCH: ${{ github.event.workflow_run.head_branch }}
COMMIT: ${{ github.event.workflow_run.head_sha }}
run: |
gh api repos/twentyhq/ci-privileged/dispatches \
--method POST \
-f event_type=visual-regression \
-f "client_payload[pr_number]=$PR_NUMBER" \
-f "client_payload[run_id]=$RUN_ID" \
-f "client_payload[run_id]=$WORKFLOW_RUN_ID" \
-f "client_payload[repo]=$REPOSITORY" \
-f "client_payload[project]=$PROJECT" \
-f "client_payload[branch]=$BRANCH" \
-f "client_payload[commit]=$COMMIT"
+1
View File
@@ -56,3 +56,4 @@ TRANSLATION_QA_REPORT.md
.playwright-mcp/
.playwright-cli/
output/playwright/
screenshots/
+1 -6
View File
@@ -147,13 +147,9 @@ Want to go deeper? Read the <a href="https://docs.twenty.com/user-guide/introduc
- <a href="https://nestjs.com/"><img src="./packages/twenty-website/public/images/readme/stack-nestjs.svg" width="14" height="14"/> NestJS</a>, with <a href="https://bullmq.io/">BullMQ</a>, <a href="https://www.postgresql.org/"><img src="./packages/twenty-website/public/images/readme/stack-postgresql.svg" width="14" height="14"/> PostgreSQL</a>, <a href="https://redis.io/"><img src="./packages/twenty-website/public/images/readme/stack-redis.svg" width="14" height="14"/> Redis</a>
- <a href="https://reactjs.org/"><img src="./packages/twenty-website/public/images/readme/stack-react.svg" width="14" height="14"/> React</a>, with <a href="https://jotai.org/">Jotai</a>, <a href="https://linaria.dev/">Linaria</a> and <a href="https://lingui.dev/">Lingui</a>
# Thanks
<p align="center">
<a href="https://www.chromatic.com/"><img src="./packages/twenty-website/public/images/readme/chromatic.webp" height="28" alt="Chromatic" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://greptile.com"><img src="./packages/twenty-website/public/images/readme/greptile.webp" height="28" alt="Greptile" /></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://sentry.io/"><img src="./packages/twenty-website/public/images/readme/sentry.webp" height="28" alt="Sentry" /></a>
@@ -161,8 +157,7 @@ Want to go deeper? Read the <a href="https://docs.twenty.com/user-guide/introduc
<a href="https://crowdin.com/"><img src="./packages/twenty-website/public/images/readme/crowdin.webp" height="28" alt="Crowdin" /></a>
</p>
Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
Thanks to these amazing services that we use and recommend for code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
# Join the Community
-19
View File
@@ -220,25 +220,6 @@
}
}
},
"chromatic": {
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"commands": [
{
"command": "nx storybook:build {projectName}",
"forwardAllArgs": false
},
"chromatic --storybook-build-dir=storybook-static {args.ci}"
],
"parallel": false
},
"configurations": {
"ci": {
"ci": "--exit-zero-on-changes"
}
}
},
"@nx/jest:jest": {
"cache": true,
"inputs": [
+1
View File
@@ -52,6 +52,7 @@
"packages/twenty-server",
"packages/twenty-emails",
"packages/twenty-ui",
"packages/twenty-new-ui",
"packages/twenty-utils",
"packages/twenty-zapier",
"packages/twenty-website",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "2.9.0",
"version": "2.10.0",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
@@ -17,8 +17,7 @@
"test:watch": "vitest"
},
"dependencies": {
"twenty-client-sdk": "TO-BE-GENERATED",
"twenty-sdk": "TO-BE-GENERATED"
"twenty-client-sdk": "TO-BE-GENERATED"
},
"devDependencies": {
"@types/node": "^24.7.2",
@@ -26,6 +25,7 @@
"oxlint": "^0.16.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"twenty-sdk": "TO-BE-GENERATED",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^4.2.1",
"vitest": "^3.1.1"
@@ -33,9 +33,11 @@ const TEMPLATE_PACKAGE_JSON = {
license: 'MIT',
scripts: { twenty: 'twenty' },
dependencies: {
'twenty-sdk': '0.0.0',
'twenty-client-sdk': '0.0.0',
},
devDependencies: {
'twenty-sdk': '0.0.0',
},
};
describe('copyBaseApplicationProject', () => {
@@ -147,7 +149,7 @@ describe('copyBaseApplicationProject', () => {
join(testAppDirectory, 'package.json'),
);
expect(packageJson.name).toBe('my-test-app');
expect(packageJson.dependencies['twenty-sdk']).toBe(
expect(packageJson.devDependencies['twenty-sdk']).toBe(
createTwentyAppPackageJson.version,
);
expect(packageJson.dependencies['twenty-client-sdk']).toBe(
@@ -120,7 +120,8 @@ const updatePackageJson = async ({
const packageJson = await fs.readJson(join(appDirectory, 'package.json'));
packageJson.name = appName;
packageJson.dependencies['twenty-sdk'] = createTwentyAppPackageJson.version;
packageJson.devDependencies['twenty-sdk'] =
createTwentyAppPackageJson.version;
packageJson.dependencies['twenty-client-sdk'] =
createTwentyAppPackageJson.version;
@@ -3,7 +3,7 @@ import { NavigationMenuItemType } from 'twenty-shared/types';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
export default defineNavigationMenuItem({
universalIdentifier: 'c1a2b3c4-0001-4a7b-8c9d-0e1f2a3b4c5d',
universalIdentifier: 'e8031eca-d6ea-4a4b-b828-38227dba896a',
position: 0,
type: NavigationMenuItemType.OBJECT,
targetObjectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
@@ -0,0 +1,10 @@
import { defineViewField } from 'twenty-sdk/define';
import { ALL_POST_CARDS_VIEW_ID } from '../views/all-post-cards.view';
export default defineViewField({
fieldMetadataUniversalIdentifier: '7b57bd63-5a4c-46ca-9d52-42c8f02d1df6',
position: 5,
universalIdentifier: 'cd582d11-ea21-4dc3-b9c1-0298ce3b6b54',
viewUniversalIdentifier: ALL_POST_CARDS_VIEW_ID,
isVisible: true,
});
@@ -43,7 +43,7 @@ A brief note on the overall tone of the conversation (collaborative, tense, expl
- If the transcript is very short (under 200 words), provide a brief 2-3 sentence summary instead of the full structure.
## Saving the Summary
After generating the summary, use \`update_callRecording\` to save it in the \`summary\` field with the format:
After generating the summary, use \`update_one_call_recording\` to save it in the \`summary\` field with the format:
\`\`\`json
{ "summary": { "blocknote": null, "markdown": "<your summary>" } }
\`\`\`
@@ -0,0 +1,32 @@
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn
# codegen
generated
# dev
/dist/
.twenty
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files
.env*
# typescript
*.tsbuildinfo
*.d.ts
@@ -0,0 +1,70 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript", "import", "unicorn"],
"categories": {
"correctness": "off"
},
"ignorePatterns": ["node_modules", "dist"],
"rules": {
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"no-console": [
"warn",
{ "allow": ["group", "groupCollapsed", "groupEnd"] }
],
"no-control-regex": "off",
"no-debugger": "error",
"no-duplicate-imports": "error",
"no-undef": "off",
"no-unused-vars": "off",
"no-redeclare": "off",
"import/no-duplicates": "error",
"typescript/no-redeclare": "error",
"typescript/ban-ts-comment": "error",
"typescript/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"fixStyle": "inline-type-imports"
}
],
"typescript/explicit-function-return-type": "off",
"typescript/explicit-module-boundary-types": "off",
"typescript/no-empty-object-type": [
"error",
{
"allowInterfaces": "with-single-extends"
}
],
"typescript/no-empty-function": "off",
"typescript/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}
],
"typescript/no-explicit-any": "off"
},
"overrides": [
{
"files": ["**/*.logic-function.ts", "**/logic-functions/**/*.ts"],
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": [
{
"group": ["twenty-shared", "twenty-shared/*"],
"message": "Logic functions must not import from twenty-shared directly. Import runtime types and helpers from `twenty-sdk/logic-function` instead so the logic-function bundle stays minimal."
}
]
}
]
}
}
]
}
@@ -0,0 +1 @@
nodeLinker: node-modules
@@ -0,0 +1,114 @@
# People Data Labs enrichment app
Enriches **Person** and **Company** records with [People Data Labs](https://www.peopledatalabs.com/) (PDL) data.
> **Status: data-model scaffold.** This package defines the fields, relation, indexes,
> views, role, and manifest. The enrichment **logic function (the "mapper") is not yet
> implemented** — see [What the mapper must do](#what-the-mapper-must-do).
---
## Data-model decisions
### Bundle scope
Only the core PDL company fields are defined. Premium / Comprehensive / specialized fields
(`inferred_revenue`, `linkedin_follower_count`, employee growth/churn/tenure, parent /
subsidiary, exec movement, top employers, `funding_details`, …) are **out of scope** for this app.
### Enums → SELECT / MULTI_SELECT
Every PDL enum that has a canonical file is a SELECT, **validated 0-missing/0-extra against
PDL schema v34.1**:
| Field | Type | Options |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------ |
| `pdlSeniority` (`job_title_levels`, array) | MULTI_SELECT | 10 |
| `pdlFundingStages` (`funding_stages`, array) | MULTI_SELECT | 29 |
| `pdlIndustry` / `pdlJobCompanyIndustry` (`industry`) | SELECT | 147 |
| `pdlJobTitleSubRole` (`job_title_sub_role`) | SELECT | 106 |
| `pdlJobTitleClass`, `pdlInferredSalary`, `pdlSex`, `pdlCompanyType`, `pdlSizeRange`, `pdlJobCompanySize`, `pdlLatestFundingStage`, `pdlLocationContinent`, `pdlLocationMetro`, `pdlMicExchange` | SELECT | 5 / 11 / 2 / 6 / 8 / 8 / 29 / 7 / 384 / 70 |
- Option `value`s are normalized to **GraphQL enum names** (`united states``UNITED_STATES`):
uppercase, accents stripped, non-alphanumeric → `_`, digit-leading prefixed.
- Option `universalIdentifier`s are **unique per field** (shared enums like industry get a
separate id-set per field).
- **Stays `TEXT`** (no canonical PDL enum file exists): `pdlIndustryDetail` (`industry_v2`),
`pdlJobOnetCode`, `pdlLocationRegion`.
### Standard-field mapping
`pdl*` shadows are **removed** where an equivalent standard field exists; the mapper writes
the standard field instead:
| Object | Removed shadow → standard target |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Person | `pdlLinkedinUrl``linkedinLink`, `pdlJobTitle``jobTitle`, `pdlFullName``name`, `pdlWorkEmail`/`pdlPersonalEmails``emails`, `pdlMobilePhone`/`pdlPhoneNumbers``phones` |
| Company | `pdlLinkedinUrl``linkedinLink`, `pdlWebsite``domainName`, `pdlDisplayName``name` |
Shadows are **kept** where no reliable standard field is available: `pdlEmployeeCount`,
`pdlTwitterUrl`.
_Trade-off:_ PDL's work/personal-email and mobile/other-phone distinction is dropped (folded
into the standard bags).
### Location → ADDRESS composite
- **Company** location → the **standard `address`** composite (street/city/state/postcode/country/geo).
- **Person** has no standard address field → dedicated **`pdlLocation` (ADDRESS)**.
- `pdlLocationMetro` (both) and `pdlLocationContinent` (company) stay SELECT — ADDRESS has no slot.
_Trade-off:_ ADDRESS `country` is free text, so the country SELECT was dropped.
### Relation
Dedicated **`pdlCurrentCompany`** (Person `MANY_TO_ONE` → Company) ↔ inverse
**`pdlCurrentEmployees`** (Company `ONE_TO_MANY` → Person). Deliberately **not** the standard
`company` relation, so PDL's detected employer can't overwrite the user's CRM account link.
### Enrichment metadata
- `pdlId` — PDL record id (re-enrich by id: more precise than by email).
- `pdlLikelihood` (Person, NUMBER) — PDL match confidence 110.
- `pdlEnrichmentStatus` (SELECT: `MATCHED` / `NOT_FOUND` / `ERROR`) — distinguishes
"no match" from "never tried" (drives re-enrichment scheduling).
- `pdlLastEnrichedAt` (DATE_TIME), `pdlRawPayload` (RAW_JSON, full response).
### Other
- `pdlTotalFunding` is `CURRENCY` (mapper must convert the bare USD float → micros).
- **Indexes**: `pdlId` and `pdlLastEnrichedAt` on both objects.
- **Views**: a curated "People Data Labs" TABLE view per object.
- **Role**: read/update on Person & Company (object-level; tighten to field-scoped later).
---
## What the mapper must do
The logic function (to be built) must:
**Orchestration**
1. Trigger via manual command-menu action / record create / batch (TBD).
2. Call PDL Person and/or Company Enrichment with `PDL_API_KEY`; pass `min_likelihood` /
`required_fields` to control match quality.
3. On `200` → write fields + set `pdlEnrichmentStatus = MATCHED`; on `404`
`NOT_FOUND`; on error → `ERROR`.
4. Respect PDL rate limits (queue / throttle on `429`).
5. **TTL guard**: skip re-enrichment if `pdlLastEnrichedAt` is recent; prefer re-enriching by `pdlId`.
**Field writing**
6. Write **standard fields** (fill-only-if-empty to avoid overwriting user data):
Person `name`, `emails`, `phones`, `linkedinLink`, `jobTitle`; Company `name`,
`domainName`, `linkedinLink`, `address`.
7. Write `pdl*` fields for everything else.
8. **SELECT guard**: only write a SELECT/MULTI_SELECT value if the normalized value exists in
the field's option set; otherwise skip and keep it in `pdlRawPayload` (handles PDL schema
versions newer than v34.1). Use the same normalization as the option `value`s.
9. **MULTI_SELECT** arrays: `job_title_levels``pdlSeniority`; `funding_stages``pdlFundingStages`.
10. **CURRENCY**: `total_funding_raised` (USD float) → `{ amountMicros: value × 1_000_000, currencyCode: 'USD' }`.
11. **ADDRESS**: split PDL `location.*` into the composite — Company → standard `address`,
Person → `pdlLocation`.
12. **Relation**: resolve `job_company_id` → find/upsert a Company record → link `pdlCurrentCompany`.
13. **Dates**: handle partial PDL dates (`YYYY`, `YYYY-MM`) for `job_start_date`,
`last_funding_date`, `birth_date`.
14. Always set `pdlId`, `pdlLastEnrichedAt`, `pdlRawPayload`, `pdlLikelihood` (person).
@@ -0,0 +1,36 @@
{
"name": "people-data-labs",
"version": "0.1.0",
"description": "People Data Labs enrichment app for Twenty",
"license": "MIT",
"engines": {
"node": "^24.5.0",
"npm": "please-use-yarn",
"yarn": ">=4.0.2"
},
"keywords": [
"twenty-app"
],
"packageManager": "yarn@4.9.2",
"scripts": {
"twenty": "twenty",
"lint": "oxlint -c .oxlintrc.json .",
"lint:fix": "oxlint --fix -c .oxlintrc.json .",
"test": "vitest run --passWithNoTests",
"test:watch": "vitest"
},
"dependencies": {
"twenty-client-sdk": "2.4.0",
"twenty-sdk": "2.4.0"
},
"devDependencies": {
"@types/node": "^24.7.2",
"@types/react": "^19.0.0",
"oxlint": "^0.16.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^4.2.1",
"vitest": "^3.1.1"
}
}
@@ -0,0 +1,16 @@
import { defineApplication } from 'twenty-sdk/define';
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
export default defineApplication({
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
displayName: 'People Data Labs',
description: 'Enrich People and Companies with People Data Labs data.',
serverVariables: {
PDL_API_KEY: {
description: 'People Data Labs API key',
isSecret: true,
isRequired: true,
},
},
});
@@ -0,0 +1,19 @@
import { type SelectOptionMeta } from 'src/types/select-option-meta.type';
export const ENRICHMENT_STATUS_OPTIONS: readonly SelectOptionMeta[] = [
{
key: 'matched',
value: 'MATCHED',
label: 'Matched',
color: 'green',
position: 0,
},
{
key: 'notFound',
value: 'NOT_FOUND',
label: 'No Match',
color: 'gray',
position: 1,
},
{ key: 'error', value: 'ERROR', label: 'Error', color: 'red', position: 2 },
];
@@ -0,0 +1,195 @@
import { type SelectOptionMeta } from 'src/types/select-option-meta.type';
export const FUNDING_STAGE_OPTIONS: readonly SelectOptionMeta[] = [
{
key: 'angel',
value: 'ANGEL',
label: 'Angel',
color: 'blue',
position: 0,
},
{
key: 'convertibleNote',
value: 'CONVERTIBLE_NOTE',
label: 'Convertible Note',
color: 'red',
position: 1,
},
{
key: 'corporateRound',
value: 'CORPORATE_ROUND',
label: 'Corporate Round',
color: 'green',
position: 2,
},
{
key: 'debtFinancing',
value: 'DEBT_FINANCING',
label: 'Debt Financing',
color: 'orange',
position: 3,
},
{
key: 'equityCrowdfunding',
value: 'EQUITY_CROWDFUNDING',
label: 'Equity Crowdfunding',
color: 'purple',
position: 4,
},
{
key: 'fundingRound',
value: 'FUNDING_ROUND',
label: 'Funding Round',
color: 'yellow',
position: 5,
},
{ key: 'grant', value: 'GRANT', label: 'Grant', color: 'pink', position: 6 },
{
key: 'initialCoinOffering',
value: 'INITIAL_COIN_OFFERING',
label: 'Initial Coin Offering',
color: 'cyan',
position: 7,
},
{
key: 'nonEquityAssistance',
value: 'NON_EQUITY_ASSISTANCE',
label: 'Non-Equity Assistance',
color: 'brown',
position: 8,
},
{
key: 'postIpoDebt',
value: 'POST_IPO_DEBT',
label: 'Post-IPO Debt',
color: 'lime',
position: 9,
},
{
key: 'postIpoEquity',
value: 'POST_IPO_EQUITY',
label: 'Post-IPO Equity',
color: 'violet',
position: 10,
},
{
key: 'postIpoSecondary',
value: 'POST_IPO_SECONDARY',
label: 'Post-IPO Secondary',
color: 'gold',
position: 11,
},
{
key: 'preSeed',
value: 'PRE_SEED',
label: 'Pre-Seed',
color: 'turquoise',
position: 12,
},
{
key: 'privateEquity',
value: 'PRIVATE_EQUITY',
label: 'Private Equity',
color: 'crimson',
position: 13,
},
{
key: 'productCrowdfunding',
value: 'PRODUCT_CROWDFUNDING',
label: 'Product Crowdfunding',
color: 'sky',
position: 14,
},
{
key: 'secondaryMarket',
value: 'SECONDARY_MARKET',
label: 'Secondary Market',
color: 'amber',
position: 15,
},
{ key: 'seed', value: 'SEED', label: 'Seed', color: 'plum', position: 16 },
{
key: 'seriesA',
value: 'SERIES_A',
label: 'Series A',
color: 'grass',
position: 17,
},
{
key: 'seriesB',
value: 'SERIES_B',
label: 'Series B',
color: 'tomato',
position: 18,
},
{
key: 'seriesC',
value: 'SERIES_C',
label: 'Series C',
color: 'iris',
position: 19,
},
{
key: 'seriesD',
value: 'SERIES_D',
label: 'Series D',
color: 'mint',
position: 20,
},
{
key: 'seriesE',
value: 'SERIES_E',
label: 'Series E',
color: 'ruby',
position: 21,
},
{
key: 'seriesF',
value: 'SERIES_F',
label: 'Series F',
color: 'bronze',
position: 22,
},
{
key: 'seriesG',
value: 'SERIES_G',
label: 'Series G',
color: 'jade',
position: 23,
},
{
key: 'seriesH',
value: 'SERIES_H',
label: 'Series H',
color: 'gray',
position: 24,
},
{
key: 'seriesI',
value: 'SERIES_I',
label: 'Series I',
color: 'blue',
position: 25,
},
{
key: 'seriesJ',
value: 'SERIES_J',
label: 'Series J',
color: 'red',
position: 26,
},
{
key: 'seriesUnknown',
value: 'SERIES_UNKNOWN',
label: 'Series Unknown',
color: 'green',
position: 27,
},
{
key: 'undisclosed',
value: 'UNDISCLOSED',
label: 'Undisclosed',
color: 'orange',
position: 28,
},
];
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
import { type SelectOptionMeta } from 'src/types/select-option-meta.type';
export const SIZE_OPTIONS: readonly SelectOptionMeta[] = [
{
key: 'oneToTen',
value: 'ONE_TO_TEN',
label: '1-10',
color: 'blue',
position: 0,
},
{
key: 'elevenToFifty',
value: 'ELEVEN_TO_FIFTY',
label: '11-50',
color: 'red',
position: 1,
},
{
key: 'fiftyOneToTwoHundred',
value: 'FIFTY_ONE_TO_TWO_HUNDRED',
label: '51-200',
color: 'green',
position: 2,
},
{
key: 'twoHundredOneToFiveHundred',
value: 'TWO_HUNDRED_ONE_TO_FIVE_HUNDRED',
label: '201-500',
color: 'orange',
position: 3,
},
{
key: 'fiveHundredOneToOneThousand',
value: 'FIVE_HUNDRED_ONE_TO_ONE_THOUSAND',
label: '501-1000',
color: 'purple',
position: 4,
},
{
key: 'oneThousandOneToFiveThousand',
value: 'ONE_THOUSAND_ONE_TO_FIVE_THOUSAND',
label: '1001-5000',
color: 'yellow',
position: 5,
},
{
key: 'fiveThousandOneToTenThousand',
value: 'FIVE_THOUSAND_ONE_TO_TEN_THOUSAND',
label: '5001-10000',
color: 'pink',
position: 6,
},
{
key: 'tenThousandOnePlus',
value: 'TEN_THOUSAND_ONE_PLUS',
label: '10001+',
color: 'cyan',
position: 7,
},
];
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlAffiliatedProfiles,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.ARRAY,
name: 'pdlAffiliatedProfiles',
label: 'Affiliated Profiles',
description: 'Affiliated company profiles returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlAlternativeDomains,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.ARRAY,
name: 'pdlAlternativeDomains',
label: 'Alternative Domains',
description: 'Alternative domains returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlAlternativeNames,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.ARRAY,
name: 'pdlAlternativeNames',
label: 'Alternative Names',
description: 'Alternative names returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,65 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlCompanyType,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlCompanyType',
label: 'Company Type',
description: 'People Data Labs canonical company type.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyType.public,
value: 'PUBLIC',
label: 'Public',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyType.private,
value: 'PRIVATE',
label: 'Private',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyType.publicSubsidiary,
value: 'PUBLIC_SUBSIDIARY',
label: 'Public Subsidiary',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyType.educational,
value: 'EDUCATIONAL',
label: 'Educational',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyType.government,
value: 'GOVERNMENT',
label: 'Government',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyType.nonprofit,
value: 'NONPROFIT',
label: 'Nonprofit',
color: 'yellow',
position: 5,
},
],
});
@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlCurrentEmployees,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.RELATION,
name: 'pdlCurrentEmployees',
label: 'PDL Current Employees',
description:
'People whose current employer is this company, per People Data Labs.',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlCurrentCompany,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
icon: 'IconUsers',
});
@@ -0,0 +1,20 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlEmployeeCountByCountry,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlEmployeeCountByCountry',
label: 'Employee Count by Country',
description:
'Employee count broken down by country returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlEmployeeCount,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.NUMBER,
name: 'pdlEmployeeCount',
label: 'Employees',
description: 'Exact employee count returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { ENRICHMENT_STATUS_OPTIONS } from 'src/constants/enrichment-status-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlEnrichmentStatus,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlEnrichmentStatus',
label: 'Enrichment Status',
description: 'Outcome of the latest People Data Labs enrichment attempt.',
isNullable: true,
options: buildSelectOptions(
ENRICHMENT_STATUS_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyEnrichmentStatus,
),
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlFacebookUrl,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.LINKS,
name: 'pdlFacebookUrl',
label: 'Facebook',
description: 'Facebook page returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlFoundedYear,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.NUMBER,
name: 'pdlFoundedYear',
label: 'Founded Year',
description: 'Founding year returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { FUNDING_STAGE_OPTIONS } from 'src/constants/funding-stage-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlFundingStages,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.MULTI_SELECT,
name: 'pdlFundingStages',
label: 'Funding Stages',
description: 'People Data Labs canonical funding stages.',
isNullable: true,
options: buildSelectOptions(
FUNDING_STAGE_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.fundingStage,
),
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlHeadline,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlHeadline',
label: 'Headline',
description: 'People Data Labs company headline or summary.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlId,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlId',
label: 'PDL ID',
description: 'People Data Labs company identifier.',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlIndustryDetail,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlIndustryDetail',
label: 'Industry (detailed)',
description: 'Detailed People Data Labs industry v2 value.',
isNullable: true,
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { INDUSTRY_OPTIONS } from 'src/constants/industry-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlIndustry,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlIndustry',
label: 'Industry',
description: 'People Data Labs canonical industry.',
isNullable: true,
options: buildSelectOptions(
INDUSTRY_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyIndustry,
),
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlLastEnrichedAt,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.DATE_TIME,
name: 'pdlLastEnrichedAt',
label: 'Last Enriched At',
description: 'Timestamp of the latest People Data Labs enrichment.',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlLastFundingDate,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.DATE,
name: 'pdlLastFundingDate',
label: 'Last Funding Date',
description: 'Date of the latest funding round returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { FUNDING_STAGE_OPTIONS } from 'src/constants/funding-stage-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlLatestFundingStage,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlLatestFundingStage',
label: 'Latest Funding Stage',
description: 'People Data Labs canonical latest funding stage.',
isNullable: true,
options: buildSelectOptions(
FUNDING_STAGE_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.fundingStage,
),
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlLinkedinId,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlLinkedinId',
label: 'LinkedIn ID',
description: 'LinkedIn identifier returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,79 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlLocationContinent,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlLocationContinent',
label: 'Continent',
description: 'People Data Labs canonical continent.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent
.africa,
value: 'AFRICA',
label: 'Africa',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent
.antarctica,
value: 'ANTARCTICA',
label: 'Antarctica',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent.asia,
value: 'ASIA',
label: 'Asia',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent
.europe,
value: 'EUROPE',
label: 'Europe',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent
.northAmerica,
value: 'NORTH_AMERICA',
label: 'North America',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent
.oceania,
value: 'OCEANIA',
label: 'Oceania',
color: 'yellow',
position: 5,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationContinent
.southAmerica,
value: 'SOUTH_AMERICA',
label: 'South America',
color: 'pink',
position: 6,
},
],
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { METRO_OPTIONS } from 'src/constants/metro-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlLocationMetro,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlLocationMetro',
label: 'Metro Area',
description: 'People Data Labs canonical metro area.',
isNullable: true,
options: buildSelectOptions(
METRO_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyLocationMetro,
),
});
@@ -0,0 +1,513 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlMicExchange,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlMicExchange',
label: 'Stock Exchange (MIC)',
description: 'People Data Labs canonical market identifier code.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.aqse,
value: 'AQSE',
label: 'AQSE',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.arcx,
value: 'ARCX',
label: 'ARCX',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.asex,
value: 'ASEX',
label: 'ASEX',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.bcxe,
value: 'BCXE',
label: 'BCXE',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.bvmf,
value: 'BVMF',
label: 'BVMF',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.dsmd,
value: 'DSMD',
label: 'DSMD',
color: 'yellow',
position: 5,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.misx,
value: 'MISX',
label: 'MISX',
color: 'pink',
position: 6,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.neoe,
value: 'NEOE',
label: 'NEOE',
color: 'cyan',
position: 7,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xams,
value: 'XAMS',
label: 'XAMS',
color: 'brown',
position: 8,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xase,
value: 'XASE',
label: 'XASE',
color: 'lime',
position: 9,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xasx,
value: 'XASX',
label: 'XASX',
color: 'violet',
position: 10,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xber,
value: 'XBER',
label: 'XBER',
color: 'gold',
position: 11,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbkk,
value: 'XBKK',
label: 'XBKK',
color: 'turquoise',
position: 12,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbog,
value: 'XBOG',
label: 'XBOG',
color: 'crimson',
position: 13,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbom,
value: 'XBOM',
label: 'XBOM',
color: 'sky',
position: 14,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbru,
value: 'XBRU',
label: 'XBRU',
color: 'amber',
position: 15,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbsp,
value: 'XBSP',
label: 'XBSP',
color: 'plum',
position: 16,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbud,
value: 'XBUD',
label: 'XBUD',
color: 'grass',
position: 17,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xbue,
value: 'XBUE',
label: 'XBUE',
color: 'tomato',
position: 18,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xcai,
value: 'XCAI',
label: 'XCAI',
color: 'iris',
position: 19,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xcbo,
value: 'XCBO',
label: 'XCBO',
color: 'mint',
position: 20,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xcnq,
value: 'XCNQ',
label: 'XCNQ',
color: 'ruby',
position: 21,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xcse,
value: 'XCSE',
label: 'XCSE',
color: 'bronze',
position: 22,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xdfm,
value: 'XDFM',
label: 'XDFM',
color: 'jade',
position: 23,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xdub,
value: 'XDUB',
label: 'XDUB',
color: 'gray',
position: 24,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xdus,
value: 'XDUS',
label: 'XDUS',
color: 'blue',
position: 25,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xeqy,
value: 'XEQY',
label: 'XEQY',
color: 'red',
position: 26,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xfra,
value: 'XFRA',
label: 'XFRA',
color: 'green',
position: 27,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xham,
value: 'XHAM',
label: 'XHAM',
color: 'orange',
position: 28,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xhel,
value: 'XHEL',
label: 'XHEL',
color: 'purple',
position: 29,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xhkg,
value: 'XHKG',
label: 'XHKG',
color: 'yellow',
position: 30,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xice,
value: 'XICE',
label: 'XICE',
color: 'pink',
position: 31,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xidx,
value: 'XIDX',
label: 'XIDX',
color: 'cyan',
position: 32,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xjpx,
value: 'XJPX',
label: 'XJPX',
color: 'brown',
position: 33,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xjse,
value: 'XJSE',
label: 'XJSE',
color: 'lime',
position: 34,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xkls,
value: 'XKLS',
label: 'XKLS',
color: 'violet',
position: 35,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xkos,
value: 'XKOS',
label: 'XKOS',
color: 'gold',
position: 36,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xkrx,
value: 'XKRX',
label: 'XKRX',
color: 'turquoise',
position: 37,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xkuw,
value: 'XKUW',
label: 'XKUW',
color: 'crimson',
position: 38,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xlis,
value: 'XLIS',
label: 'XLIS',
color: 'sky',
position: 39,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xlon,
value: 'XLON',
label: 'XLON',
color: 'amber',
position: 40,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xmad,
value: 'XMAD',
label: 'XMAD',
color: 'plum',
position: 41,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xmex,
value: 'XMEX',
label: 'XMEX',
color: 'grass',
position: 42,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xmil,
value: 'XMIL',
label: 'XMIL',
color: 'tomato',
position: 43,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xmun,
value: 'XMUN',
label: 'XMUN',
color: 'iris',
position: 44,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xnas,
value: 'XNAS',
label: 'XNAS',
color: 'mint',
position: 45,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xnse,
value: 'XNSE',
label: 'XNSE',
color: 'ruby',
position: 46,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xnys,
value: 'XNYS',
label: 'XNYS',
color: 'bronze',
position: 47,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xnze,
value: 'XNZE',
label: 'XNZE',
color: 'jade',
position: 48,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xosl,
value: 'XOSL',
label: 'XOSL',
color: 'gray',
position: 49,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xotc,
value: 'XOTC',
label: 'XOTC',
color: 'blue',
position: 50,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xpar,
value: 'XPAR',
label: 'XPAR',
color: 'red',
position: 51,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xpra,
value: 'XPRA',
label: 'XPRA',
color: 'green',
position: 52,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xris,
value: 'XRIS',
label: 'XRIS',
color: 'orange',
position: 53,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xsau,
value: 'XSAU',
label: 'XSAU',
color: 'purple',
position: 54,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xses,
value: 'XSES',
label: 'XSES',
color: 'yellow',
position: 55,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xsgo,
value: 'XSGO',
label: 'XSGO',
color: 'pink',
position: 56,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xshe,
value: 'XSHE',
label: 'XSHE',
color: 'cyan',
position: 57,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xshg,
value: 'XSHG',
label: 'XSHG',
color: 'brown',
position: 58,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xstc,
value: 'XSTC',
label: 'XSTC',
color: 'lime',
position: 59,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xsto,
value: 'XSTO',
label: 'XSTO',
color: 'violet',
position: 60,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xstu,
value: 'XSTU',
label: 'XSTU',
color: 'gold',
position: 61,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xswx,
value: 'XSWX',
label: 'XSWX',
color: 'turquoise',
position: 62,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xtae,
value: 'XTAE',
label: 'XTAE',
color: 'crimson',
position: 63,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xtai,
value: 'XTAI',
label: 'XTAI',
color: 'sky',
position: 64,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xtal,
value: 'XTAL',
label: 'XTAL',
color: 'amber',
position: 65,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xtse,
value: 'XTSE',
label: 'XTSE',
color: 'plum',
position: 66,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xtsx,
value: 'XTSX',
label: 'XTSX',
color: 'grass',
position: 67,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xwar,
value: 'XWAR',
label: 'XWAR',
color: 'tomato',
position: 68,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.companyMicExchange.xwbo,
value: 'XWBO',
label: 'XWBO',
color: 'iris',
position: 69,
},
],
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlNaics,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlNaics',
label: 'NAICS Codes',
description: 'NAICS classification codes returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlNumberFundingRounds,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.NUMBER,
name: 'pdlNumberFundingRounds',
label: 'Number of Funding Rounds',
description: 'Number of funding rounds returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlRawPayload,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlRawPayload',
label: 'PDL Raw Payload',
description: 'Full People Data Labs company enrichment response.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlSic,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlSic',
label: 'SIC Codes',
description: 'SIC classification codes returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SIZE_OPTIONS } from 'src/constants/size-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlSizeRange,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlSizeRange',
label: 'Employee Range',
description: 'People Data Labs canonical self-reported employee range.',
isNullable: true,
options: buildSelectOptions(
SIZE_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.sizeRange,
),
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlSummary,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlSummary',
label: 'Summary',
description: 'Company summary returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlTags,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.ARRAY,
name: 'pdlTags',
label: 'Tags',
description: 'Tags returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlTicker,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlTicker',
label: 'Ticker',
description: 'Company ticker returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlTotalFunding,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.CURRENCY,
name: 'pdlTotalFunding',
label: 'Total Funding Raised',
description: 'Total funding raised in USD returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlTwitterUrl,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
type: FieldType.LINKS,
name: 'pdlTwitterUrl',
label: 'X / Twitter',
description: 'X or Twitter company profile returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlBirthDate,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.DATE,
name: 'pdlBirthDate',
label: 'Birth Date',
description: 'Birth date returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlBirthYear,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.NUMBER,
name: 'pdlBirthYear',
label: 'Birth Year',
description: 'Birth year returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlCertifications,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlCertifications',
label: 'Certifications',
description: 'Certifications returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlCurrentCompany,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'pdlCurrentCompany',
label: 'PDL Current Company',
description: 'Current employer detected by People Data Labs.',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlCurrentEmployees,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
},
icon: 'IconBuilding',
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlEducation,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlEducation',
label: 'Education History',
description: 'Education history returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { ENRICHMENT_STATUS_OPTIONS } from 'src/constants/enrichment-status-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlEnrichmentStatus,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlEnrichmentStatus',
label: 'Enrichment Status',
description: 'Outcome of the latest People Data Labs enrichment attempt.',
isNullable: true,
options: buildSelectOptions(
ENRICHMENT_STATUS_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personEnrichmentStatus,
),
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlExperience,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlExperience',
label: 'Experience History',
description: 'Experience history returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlFacebookUrl,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.LINKS,
name: 'pdlFacebookUrl',
label: 'Facebook',
description: 'Facebook profile returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlGithubUrl,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.LINKS,
name: 'pdlGithubUrl',
label: 'GitHub',
description: 'GitHub profile returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlHeadline,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlHeadline',
label: 'Headline',
description: 'People Data Labs person headline or job summary.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlId,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlId',
label: 'PDL ID',
description: 'People Data Labs person identifier.',
isNullable: true,
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { INDUSTRY_OPTIONS } from 'src/constants/industry-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlIndustry,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlIndustry',
label: 'Industry',
description: 'People Data Labs canonical industry.',
isNullable: true,
options: buildSelectOptions(
INDUSTRY_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personIndustry,
),
});
@@ -0,0 +1,111 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlInferredSalary,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlInferredSalary',
label: 'Inferred Salary (range)',
description: 'People Data Labs canonical inferred salary range.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.under20000,
value: 'UNDER_20000',
label: '<20,000',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from20000To25000,
value: 'FROM_20000_TO_25000',
label: '20,000-25,000',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from25000To35000,
value: 'FROM_25000_TO_35000',
label: '25,000-35,000',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from35000To45000,
value: 'FROM_35000_TO_45000',
label: '35,000-45,000',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from45000To55000,
value: 'FROM_45000_TO_55000',
label: '45,000-55,000',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from55000To70000,
value: 'FROM_55000_TO_70000',
label: '55,000-70,000',
color: 'yellow',
position: 5,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from70000To85000,
value: 'FROM_70000_TO_85000',
label: '70,000-85,000',
color: 'pink',
position: 6,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from85000To100000,
value: 'FROM_85000_TO_100000',
label: '85,000-100,000',
color: 'cyan',
position: 7,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from100000To150000,
value: 'FROM_100000_TO_150000',
label: '100,000-150,000',
color: 'brown',
position: 8,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.from150000To250000,
value: 'FROM_150000_TO_250000',
label: '150,000-250,000',
color: 'lime',
position: 9,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personInferredSalary
.over250000,
value: 'OVER_250000',
label: '>250,000',
color: 'violet',
position: 10,
},
],
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlInterests,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.ARRAY,
name: 'pdlInterests',
label: 'Interests',
description: 'Interests returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobCompanyId,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlJobCompanyId',
label: 'Current Company PDL ID',
description: 'Current company PDL identifier returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,29 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { INDUSTRY_OPTIONS } from 'src/constants/industry-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobCompanyIndustry,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlJobCompanyIndustry',
label: 'Current Company Industry',
description:
'Current company canonical industry returned by People Data Labs.',
isNullable: true,
options: buildSelectOptions(
INDUSTRY_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobCompanyIndustry,
),
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobCompanyLinkedinUrl,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.LINKS,
name: 'pdlJobCompanyLinkedinUrl',
label: 'Current Company LinkedIn',
description: 'Current company LinkedIn returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobCompanyName,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlJobCompanyName',
label: 'Current Company',
description: 'Current company name returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SIZE_OPTIONS } from 'src/constants/size-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobCompanySize,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlJobCompanySize',
label: 'Current Company Size',
description: 'Current company size range returned by People Data Labs.',
isNullable: true,
options: buildSelectOptions(
SIZE_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobCompanySize,
),
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobCompanyWebsite,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.LINKS,
name: 'pdlJobCompanyWebsite',
label: 'Current Company Website',
description: 'Current company website returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobHistory,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlJobHistory',
label: 'Job History',
description: 'Job history returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobOnetCode,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlJobOnetCode',
label: 'O*NET Code',
description: 'O*NET occupation code returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,191 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobRole,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlJobRole',
label: 'Job Role',
description: 'People Data Labs canonical job title role.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.advisory,
value: 'ADVISORY',
label: 'Advisory',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.analyst,
value: 'ANALYST',
label: 'Analyst',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.creative,
value: 'CREATIVE',
label: 'Creative',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.education,
value: 'EDUCATION',
label: 'Education',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.engineering,
value: 'ENGINEERING',
label: 'Engineering',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.finance,
value: 'FINANCE',
label: 'Finance',
color: 'yellow',
position: 5,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.fulfillment,
value: 'FULFILLMENT',
label: 'Fulfillment',
color: 'pink',
position: 6,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.health,
value: 'HEALTH',
label: 'Health',
color: 'cyan',
position: 7,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.hospitality,
value: 'HOSPITALITY',
label: 'Hospitality',
color: 'brown',
position: 8,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.humanResources,
value: 'HUMAN_RESOURCES',
label: 'Human Resources',
color: 'lime',
position: 9,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.legal,
value: 'LEGAL',
label: 'Legal',
color: 'violet',
position: 10,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.manufacturing,
value: 'MANUFACTURING',
label: 'Manufacturing',
color: 'gold',
position: 11,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.marketing,
value: 'MARKETING',
label: 'Marketing',
color: 'turquoise',
position: 12,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.operations,
value: 'OPERATIONS',
label: 'Operations',
color: 'crimson',
position: 13,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.partnerships,
value: 'PARTNERSHIPS',
label: 'Partnerships',
color: 'sky',
position: 14,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.product,
value: 'PRODUCT',
label: 'Product',
color: 'amber',
position: 15,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.professionalService,
value: 'PROFESSIONAL_SERVICE',
label: 'Professional Service',
color: 'plum',
position: 16,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.publicService,
value: 'PUBLIC_SERVICE',
label: 'Public Service',
color: 'grass',
position: 17,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.research,
value: 'RESEARCH',
label: 'Research',
color: 'tomato',
position: 18,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.sales,
value: 'SALES',
label: 'Sales',
color: 'iris',
position: 19,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.salesEngineering,
value: 'SALES_ENGINEERING',
label: 'Sales Engineering',
color: 'mint',
position: 20,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.support,
value: 'SUPPORT',
label: 'Support',
color: 'ruby',
position: 21,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.trade,
value: 'TRADE',
label: 'Trade',
color: 'bronze',
position: 22,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.jobRole.unemployed,
value: 'UNEMPLOYED',
label: 'Unemployed',
color: 'jade',
position: 23,
},
],
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobStartDate,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.DATE,
name: 'pdlJobStartDate',
label: 'Job Start Date',
description: 'Current job start date returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobSummary,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlJobSummary',
label: 'Job Summary',
description: 'Current job summary returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,61 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobTitleClass,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlJobTitleClass',
label: 'Job Class',
description: 'People Data Labs canonical job class.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobClass
.generalAndAdministrative,
value: 'GENERAL_AND_ADMINISTRATIVE',
label: 'General and Administrative',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobClass
.researchAndDevelopment,
value: 'RESEARCH_AND_DEVELOPMENT',
label: 'Research and Development',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobClass
.salesAndMarketing,
value: 'SALES_AND_MARKETING',
label: 'Sales and Marketing',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobClass.services,
value: 'SERVICES',
label: 'Services',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobClass.unemployed,
value: 'UNEMPLOYED',
label: 'Unemployed',
color: 'purple',
position: 4,
},
],
});
@@ -0,0 +1,803 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlJobTitleSubRole,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlJobTitleSubRole',
label: 'Job Sub-Role',
description: 'People Data Labs canonical job sub-role.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.academic,
value: 'ACADEMIC',
label: 'Academic',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.accountExecutive,
value: 'ACCOUNT_EXECUTIVE',
label: 'Account Executive',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.accountManagement,
value: 'ACCOUNT_MANAGEMENT',
label: 'Account Management',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.accounting,
value: 'ACCOUNTING',
label: 'Accounting',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.accountingServices,
value: 'ACCOUNTING_SERVICES',
label: 'Accounting Services',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.administrative,
value: 'ADMINISTRATIVE',
label: 'Administrative',
color: 'yellow',
position: 5,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.advisor,
value: 'ADVISOR',
label: 'Advisor',
color: 'pink',
position: 6,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.agriculture,
value: 'AGRICULTURE',
label: 'Agriculture',
color: 'cyan',
position: 7,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.aides,
value: 'AIDES',
label: 'Aides',
color: 'brown',
position: 8,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.architecture,
value: 'ARCHITECTURE',
label: 'Architecture',
color: 'lime',
position: 9,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.artist,
value: 'ARTIST',
label: 'Artist',
color: 'violet',
position: 10,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.boardMember,
value: 'BOARD_MEMBER',
label: 'Board Member',
color: 'gold',
position: 11,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.bookkeeping,
value: 'BOOKKEEPING',
label: 'Bookkeeping',
color: 'turquoise',
position: 12,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.brand,
value: 'BRAND',
label: 'Brand',
color: 'crimson',
position: 13,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.buildingAndGrounds,
value: 'BUILDING_AND_GROUNDS',
label: 'Building and Grounds',
color: 'sky',
position: 14,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.businessAnalyst,
value: 'BUSINESS_ANALYST',
label: 'Business Analyst',
color: 'amber',
position: 15,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.businessDevelopment,
value: 'BUSINESS_DEVELOPMENT',
label: 'Business Development',
color: 'plum',
position: 16,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.chemical,
value: 'CHEMICAL',
label: 'Chemical',
color: 'grass',
position: 17,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.compliance,
value: 'COMPLIANCE',
label: 'Compliance',
color: 'tomato',
position: 18,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.construction,
value: 'CONSTRUCTION',
label: 'Construction',
color: 'iris',
position: 19,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.consulting,
value: 'CONSULTING',
label: 'Consulting',
color: 'mint',
position: 20,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.content,
value: 'CONTENT',
label: 'Content',
color: 'ruby',
position: 21,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.corporateDevelopment,
value: 'CORPORATE_DEVELOPMENT',
label: 'Corporate Development',
color: 'bronze',
position: 22,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.curation,
value: 'CURATION',
label: 'Curation',
color: 'jade',
position: 23,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.customerSuccess,
value: 'CUSTOMER_SUCCESS',
label: 'Customer Success',
color: 'gray',
position: 24,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.customerSupport,
value: 'CUSTOMER_SUPPORT',
label: 'Customer Support',
color: 'blue',
position: 25,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.dataAnalyst,
value: 'DATA_ANALYST',
label: 'Data Analyst',
color: 'red',
position: 26,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.dataEngineering,
value: 'DATA_ENGINEERING',
label: 'Data Engineering',
color: 'green',
position: 27,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.dataScience,
value: 'DATA_SCIENCE',
label: 'Data Science',
color: 'orange',
position: 28,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.dental,
value: 'DENTAL',
label: 'Dental',
color: 'purple',
position: 29,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.devops,
value: 'DEVOPS',
label: 'DevOps',
color: 'yellow',
position: 30,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.doctor,
value: 'DOCTOR',
label: 'Doctor',
color: 'pink',
position: 31,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.electric,
value: 'ELECTRIC',
label: 'Electric',
color: 'cyan',
position: 32,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.electrical,
value: 'ELECTRICAL',
label: 'Electrical',
color: 'brown',
position: 33,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.emergencyServices,
value: 'EMERGENCY_SERVICES',
label: 'Emergency Services',
color: 'lime',
position: 34,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.entertainment,
value: 'ENTERTAINMENT',
label: 'Entertainment',
color: 'violet',
position: 35,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.executive,
value: 'EXECUTIVE',
label: 'Executive',
color: 'gold',
position: 36,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.fashion,
value: 'FASHION',
label: 'Fashion',
color: 'turquoise',
position: 37,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.financial,
value: 'FINANCIAL',
label: 'Financial',
color: 'crimson',
position: 38,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.fitness,
value: 'FITNESS',
label: 'Fitness',
color: 'sky',
position: 39,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.fraud,
value: 'FRAUD',
label: 'Fraud',
color: 'amber',
position: 40,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.graphicDesign,
value: 'GRAPHIC_DESIGN',
label: 'Graphic Design',
color: 'plum',
position: 41,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.growth,
value: 'GROWTH',
label: 'Growth',
color: 'grass',
position: 42,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.hairStylist,
value: 'HAIR_STYLIST',
label: 'Hair Stylist',
color: 'tomato',
position: 43,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.hardware,
value: 'HARDWARE',
label: 'Hardware',
color: 'iris',
position: 44,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.healthAndSafety,
value: 'HEALTH_AND_SAFETY',
label: 'Health and Safety',
color: 'mint',
position: 45,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.humanResources,
value: 'HUMAN_RESOURCES',
label: 'Human Resources',
color: 'ruby',
position: 46,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.implementation,
value: 'IMPLEMENTATION',
label: 'Implementation',
color: 'bronze',
position: 47,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.industrial,
value: 'INDUSTRIAL',
label: 'Industrial',
color: 'jade',
position: 48,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.informationTechnology,
value: 'INFORMATION_TECHNOLOGY',
label: 'Information Technology',
color: 'gray',
position: 49,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.insurance,
value: 'INSURANCE',
label: 'Insurance',
color: 'blue',
position: 50,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.investmentBanking,
value: 'INVESTMENT_BANKING',
label: 'Investment Banking',
color: 'red',
position: 51,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.investor,
value: 'INVESTOR',
label: 'Investor',
color: 'green',
position: 52,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.investorRelations,
value: 'INVESTOR_RELATIONS',
label: 'Investor Relations',
color: 'orange',
position: 53,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.journalism,
value: 'JOURNALISM',
label: 'Journalism',
color: 'purple',
position: 54,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.judicial,
value: 'JUDICIAL',
label: 'Judicial',
color: 'yellow',
position: 55,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.legal,
value: 'LEGAL',
label: 'Legal',
color: 'pink',
position: 56,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.legalServices,
value: 'LEGAL_SERVICES',
label: 'Legal Services',
color: 'cyan',
position: 57,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.logistics,
value: 'LOGISTICS',
label: 'Logistics',
color: 'brown',
position: 58,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.machinist,
value: 'MACHINIST',
label: 'Machinist',
color: 'lime',
position: 59,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.marketingDesign,
value: 'MARKETING_DESIGN',
label: 'Marketing Design',
color: 'violet',
position: 60,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.marketingServices,
value: 'MARKETING_SERVICES',
label: 'Marketing Services',
color: 'gold',
position: 61,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.mechanic,
value: 'MECHANIC',
label: 'Mechanic',
color: 'turquoise',
position: 62,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.mechanical,
value: 'MECHANICAL',
label: 'Mechanical',
color: 'crimson',
position: 63,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.military,
value: 'MILITARY',
label: 'Military',
color: 'sky',
position: 64,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.network,
value: 'NETWORK',
label: 'Network',
color: 'amber',
position: 65,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.nursing,
value: 'NURSING',
label: 'Nursing',
color: 'plum',
position: 66,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.partnerships,
value: 'PARTNERSHIPS',
label: 'Partnerships',
color: 'grass',
position: 67,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.pharmacy,
value: 'PHARMACY',
label: 'Pharmacy',
color: 'tomato',
position: 68,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.planningAndAnalysis,
value: 'PLANNING_AND_ANALYSIS',
label: 'Planning and Analysis',
color: 'iris',
position: 69,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.plumbing,
value: 'PLUMBING',
label: 'Plumbing',
color: 'mint',
position: 70,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.political,
value: 'POLITICAL',
label: 'Political',
color: 'ruby',
position: 71,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.primaryAndSecondary,
value: 'PRIMARY_AND_SECONDARY',
label: 'Primary and Secondary',
color: 'bronze',
position: 72,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.procurement,
value: 'PROCUREMENT',
label: 'Procurement',
color: 'jade',
position: 73,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.productDesign,
value: 'PRODUCT_DESIGN',
label: 'Product Design',
color: 'gray',
position: 74,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.productManagement,
value: 'PRODUCT_MANAGEMENT',
label: 'Product Management',
color: 'blue',
position: 75,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.professor,
value: 'PROFESSOR',
label: 'Professor',
color: 'red',
position: 76,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.projectManagement,
value: 'PROJECT_MANAGEMENT',
label: 'Project Management',
color: 'green',
position: 77,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.protectiveService,
value: 'PROTECTIVE_SERVICE',
label: 'Protective Service',
color: 'orange',
position: 78,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.qaEngineering,
value: 'QA_ENGINEERING',
label: 'QA Engineering',
color: 'purple',
position: 79,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.qualityAssurance,
value: 'QUALITY_ASSURANCE',
label: 'Quality Assurance',
color: 'yellow',
position: 80,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.realtor,
value: 'REALTOR',
label: 'Realtor',
color: 'pink',
position: 81,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.recruiting,
value: 'RECRUITING',
label: 'Recruiting',
color: 'cyan',
position: 82,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.restaurants,
value: 'RESTAURANTS',
label: 'Restaurants',
color: 'brown',
position: 83,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.retail,
value: 'RETAIL',
label: 'Retail',
color: 'lime',
position: 84,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.revenueOperations,
value: 'REVENUE_OPERATIONS',
label: 'Revenue Operations',
color: 'violet',
position: 85,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.risk,
value: 'RISK',
label: 'Risk',
color: 'gold',
position: 86,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.salesDevelopment,
value: 'SALES_DEVELOPMENT',
label: 'Sales Development',
color: 'turquoise',
position: 87,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.scientific,
value: 'SCIENTIFIC',
label: 'Scientific',
color: 'crimson',
position: 88,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.security,
value: 'SECURITY',
label: 'Security',
color: 'sky',
position: 89,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.socialService,
value: 'SOCIAL_SERVICE',
label: 'Social Service',
color: 'amber',
position: 90,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.software,
value: 'SOFTWARE',
label: 'Software',
color: 'plum',
position: 91,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.solutionsEngineer,
value: 'SOLUTIONS_ENGINEER',
label: 'Solutions Engineer',
color: 'grass',
position: 92,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.strategy,
value: 'STRATEGY',
label: 'Strategy',
color: 'tomato',
position: 93,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.student,
value: 'STUDENT',
label: 'Student',
color: 'iris',
position: 94,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.talentAnalytics,
value: 'TALENT_ANALYTICS',
label: 'Talent Analytics',
color: 'mint',
position: 95,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.therapy,
value: 'THERAPY',
label: 'Therapy',
color: 'ruby',
position: 96,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole
.tourAndTravel,
value: 'TOUR_AND_TRAVEL',
label: 'Tour and Travel',
color: 'bronze',
position: 97,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.training,
value: 'TRAINING',
label: 'Training',
color: 'jade',
position: 98,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.translation,
value: 'TRANSLATION',
label: 'Translation',
color: 'gray',
position: 99,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.transport,
value: 'TRANSPORT',
label: 'Transport',
color: 'blue',
position: 100,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.unemployed,
value: 'UNEMPLOYED',
label: 'Unemployed',
color: 'red',
position: 101,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.veterinarian,
value: 'VETERINARIAN',
label: 'Veterinarian',
color: 'green',
position: 102,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.warehouse,
value: 'WAREHOUSE',
label: 'Warehouse',
color: 'orange',
position: 103,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.web,
value: 'WEB',
label: 'Web',
color: 'purple',
position: 104,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personJobSubRole.wellness,
value: 'WELLNESS',
label: 'Wellness',
color: 'yellow',
position: 105,
},
],
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLanguages,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlLanguages',
label: 'Languages',
description: 'Languages returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLastEnrichedAt,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.DATE_TIME,
name: 'pdlLastEnrichedAt',
label: 'Last Enriched At',
description: 'Timestamp of the latest People Data Labs enrichment.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLikelihood,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.NUMBER,
name: 'pdlLikelihood',
label: 'Match Likelihood',
description: 'People Data Labs match confidence score (1-10).',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLinkedinConnections,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.NUMBER,
name: 'pdlLinkedinConnections',
label: 'LinkedIn Connections',
description: 'LinkedIn connection count returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier:
PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLinkedinUsername,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'pdlLinkedinUsername',
label: 'LinkedIn Username',
description: 'LinkedIn username returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { METRO_OPTIONS } from 'src/constants/metro-options';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
import { buildSelectOptions } from 'src/utils/build-select-options';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLocationMetro,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlLocationMetro',
label: 'Metro Area',
description: 'People Data Labs canonical metro area.',
isNullable: true,
options: buildSelectOptions(
METRO_OPTIONS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.personLocationMetro,
),
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlLocation,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.ADDRESS,
name: 'pdlLocation',
label: 'Location',
description: 'Location returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlNameAliases,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.ARRAY,
name: 'pdlNameAliases',
label: 'Name Aliases',
description: 'Name aliases returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlProfiles,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlProfiles',
label: 'Social Profiles',
description: 'All social profiles returned by People Data Labs.',
isNullable: true,
});
@@ -0,0 +1,18 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PDL_FIELD_UNIVERSAL_IDENTIFIERS } from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlRawPayload,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RAW_JSON,
name: 'pdlRawPayload',
label: 'PDL Raw Payload',
description: 'Full People Data Labs person enrichment response.',
isNullable: true,
});
@@ -0,0 +1,93 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlSeniority,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.MULTI_SELECT,
name: 'pdlSeniority',
label: 'Seniority',
description: 'People Data Labs canonical job title levels.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.cxo,
value: 'CXO',
label: 'CXO',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.owner,
value: 'OWNER',
label: 'Owner',
color: 'red',
position: 1,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.vp,
value: 'VP',
label: 'VP',
color: 'green',
position: 2,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.director,
value: 'DIRECTOR',
label: 'Director',
color: 'orange',
position: 3,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.partner,
value: 'PARTNER',
label: 'Partner',
color: 'purple',
position: 4,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.senior,
value: 'SENIOR',
label: 'Senior',
color: 'yellow',
position: 5,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.manager,
value: 'MANAGER',
label: 'Manager',
color: 'pink',
position: 6,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.entry,
value: 'ENTRY',
label: 'Entry',
color: 'cyan',
position: 7,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.training,
value: 'TRAINING',
label: 'Training',
color: 'brown',
position: 8,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.seniority.unpaid,
value: 'UNPAID',
label: 'Unpaid',
color: 'lime',
position: 9,
},
],
});
@@ -0,0 +1,37 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PDL_FIELD_UNIVERSAL_IDENTIFIERS,
PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS,
} from 'src/constants/universal-identifiers';
export default defineField({
universalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlSex,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'pdlSex',
label: 'Sex',
description: 'Sex returned by People Data Labs.',
isNullable: true,
options: [
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.sex.male,
value: 'MALE',
label: 'Male',
color: 'blue',
position: 0,
},
{
id: PDL_SELECT_OPTION_UNIVERSAL_IDENTIFIERS.sex.female,
value: 'FEMALE',
label: 'Female',
color: 'red',
position: 1,
},
],
});

Some files were not shown because too many files have changed in this diff Show More