Compare commits

..
Author SHA1 Message Date
Paul RastoinandGitHub 54a9a1087b Fix identify remaining entities command to cover soft-deleted rows (#17304)
# Introduction
Some entities aren't passing the migration due to not covering the soft
deleted rows

## Entities that implements soft deletion
Inserted with universal programmatically
- rowLevelPermissionPredicate
- rowLevelPermissionPredicateGroup
- pageLayout
- pageLayoutTab
- pageLayoutWidget

Legacy might contains null
- viewFilterGroup
- serverlessFunction
- viewSort
2026-01-21 15:36:26 +01:00
Charles BochetandGitHub d74d74da4c Improvements on SDK watcher (#17291)
# Improve cross-entity duplicate detection in manifest validation

- Refactored findDuplicates to receive the full manifest, enabling
cross-entity duplicate checks
-ObjectExtensionEntityBuilder now validates that extension field IDs
don't conflict with object field IDs
- Renamed DuplicateId type to EntityIdWithLocation for clarity
- Updated all entity builders to use the new signature
2026-01-21 15:31:38 +01:00
neo773andGitHub 2ecc1ba897 fix gmail message import policy (#17272)
Gmail sync was importing messages from folders users had explicitly
disabled because the folder filtering logic lived in the parent service
but the Gmail driver wasn't aware of the import policy when building its
API queries.
Moved messageFolderImportPolicy down to the driver level so Gmail can
- Build proper -label: exclusions during initial sync
- Added `buildGmailLabelSearchName` as our syntax for nested folders was
wrong
- Filter out messages from disabled folders during incremental sync via
history API
2026-01-21 12:49:58 +00:00
Paul RastoinandGitHub f377727ff5 Update create entity cursor rule (#17299)
Following https://github.com/twentyhq/twenty/pull/17279
2026-01-21 10:59:19 +00:00
Lucas BordeauandGitHub 2835935f11 Handled SSE create event (#17293)
This PR handles SSE create event.

It also fixes a regression on board, which was making it flash with
skeletons on any update / create.

This PR was a good test case to experience how each main components of
the app : table, board, calendar, reacts to a create event.

It is not straightforward for now, because each component handles
records with its own state management to turn those records into a
coherent display of rows or cards.

The requests for each component are also different : fetch more, group
by, multiple requests in parallel, so the cleanest way to handle
optimistic effect requires to create one small optimistic engine per
component tuned for its internal data logic.

For now we've decided to implement what's doable in a reasonable amount
of time and that includes not handling table with groups for now.

Creating a clean optimistic logic for each component will be done later.

# QA

## Importing 100 records via the API (could be a script, a workflow, an
AI calling tools, a CSV import, etc.)


https://github.com/user-attachments/assets/d38a3770-8b0a-4f83-8275-5e7d1be0a5c6

## Creating from different components and seeing the SSE event being
processed by other components


https://github.com/user-attachments/assets/106b2f49-19cb-4190-92b7-0653c8373366
2026-01-21 10:49:20 +00:00
Paul RastoinandGitHub 596b7cc62d Deprecate nullable syncableEntity (#17279)
# Introduction
As we've been identifying both standard and custom entities for all the
metadata that had standard
We now still need to identify all custom entities enforcing them to have
an `applicationId` and `universalIdentifier`

In this PR we've removed the `SyncableEntityRequired` in favor requiring
props directly in the `SyncableEntity`
Which means that all metadata in db will now expect non nullable
applicationId and universalIdentifier across the whole application

Will add some type cleanup later in
https://github.com/twentyhq/twenty/pull/17277
2026-01-21 10:23:55 +00:00
Baptiste DevessierandGitHub b56f45e871 Remove extra spacing for empty widgets (#17289)
See the _Account Owner_ field

## Before

<img width="1148" height="2120" alt="CleanShot 2026-01-20 at 16 10
11@2x"
src="https://github.com/user-attachments/assets/5b726009-e68c-492e-bb36-de42062db6bd"
/>

## After

<img width="1148" height="2120" alt="CleanShot 2026-01-20 at 16 09
44@2x"
src="https://github.com/user-attachments/assets/f421efa3-d0f7-4ec0-93c0-a6b1f5d0e36c"
/>

<img width="1148" height="2120" alt="CleanShot 2026-01-20 at 16 09
57@2x"
src="https://github.com/user-attachments/assets/636ed2f0-34d8-4bd5-a47c-aef2121d1cbc"
/>
2026-01-21 09:21:26 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>DevessierDevessier
9b7953e7a3 Fix widget injection order: place relation widgets after FIELDS, Notes after relations (#17274)
> [!NOTE]
> This code is temporary. It will be dropped once Record Page Layouts
can be fully configured.

## Before



https://github.com/user-attachments/assets/3f6aed00-2fd5-47d4-bd74-002a68030627



## After

<img width="3456" height="2160" alt="CleanShot 2026-01-20 at 15 17
15@2x"
src="https://github.com/user-attachments/assets/2f9baf0a-e003-4cb6-a023-6e104b097df3"
/>

The `usePageLayoutWithRelationWidgets` hook was appending relation
widgets to the end of the widget list. This resulted in incorrect
ordering where relation widgets appeared after Notes and other widgets
instead of immediately following the FIELDS widget.

## Changes

- **Widget injection logic**: Modified `injectRelationWidgetsIntoLayout`
to find the first FIELDS widget and insert relation widgets immediately
after it, rather than appending to end
- **Notes positioning**: Extract and reposition NOTES widget to appear
after all relation widgets, maintaining correct semantic order: `FIELDS
→ Relations → Notes → Other widgets`
- **Fallback behavior**: When no FIELDS widget exists, append relation
widgets to end as before
- **Test coverage**: Added 6 test cases covering injection order, Notes
positioning, and edge cases (missing widgets, empty relations,
non-record pages)

## Example

Before:
```
[FIELDS, NOTES, GRAPH] → [FIELDS, NOTES, GRAPH, Relation1, Relation2]
```

After:
```
[FIELDS, NOTES, GRAPH] → [FIELDS, Relation1, Relation2, NOTES, GRAPH]
```

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `googlechromelabs.github.io`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (dns block)
> -
`https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-headless-shell-linux64.zip`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (http block)
> -
`https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-linux64.zip`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (http block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/twentyhq/twenty/settings/copilot/coding_agent)
(admins only)
>
> </details>

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

Start implementation

<analysis>
[Chronological Review: The conversation began with the user requesting
an update to the `usePageLayoutWithRelationWidgets` function to inject
relation widgets after the first FIELDS widget and to position a Note
widget after all relation widgets. The user then asked for a plan
without comments, followed by a request to start the implementation of
the plan.]

[Intent Mapping: 
1. "Update #sym:usePageLayoutWithRelationWidgets. We should inject the
relation widgets directly after the first FIELDS widget."
2. "We want the Note widget to be after all the relation widgets we
inject."
3. "Prepare a plan. Don't write any comment."
4. "Start implementation."]

[Technical Inventory: 
- Function: `usePageLayoutWithRelationWidgets`
- Context: This function is part of a page layout management system,
likely dealing with dynamic widget placement based on certain
conditions.]

[Code Archaeology: 
- File:
`/Users/devessier/Web/twenty/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts`
- Function Purpose: To manage the layout of widgets on a page,
specifically handling relation widgets and their placement relative to
other widgets.]

[Progress Assessment: 
- Completed: User has defined the requirements for widget placement.
- Pending: Implementation of the specified changes to the
`usePageLayoutWithRelationWidgets` function.]

[Context Validation: All necessary context for continuing the
implementation of the widget placement logic is captured, including the
specific order of widget injection.]

[Recent Commands Analysis: 
- Last Agent Commands: The user requested to "Start implementation."
- Tool Results Summary: No specific tool results were generated as the
conversation focused on planning and implementation requests.
- Pre-Summary State: The agent was actively preparing to implement the
changes to the `usePageLayoutWithRelationWidgets` function when the
token budget was exceeded.
- Operation Context: The commands executed were directly related to the
user's goal of modifying the widget layout in the application.]

</analysis>

<summary>
1. Conversation Overview:
- Primary Objectives: 
- "Update #sym:usePageLayoutWithRelationWidgets. We should inject the
relation widgets directly after the first FIELDS widget."
- "We want the Note widget to be after all the relation widgets we
inject."
  - "Prepare a plan. Don't write any comment."
  - "Start implementation."
- Session Context: The user is focused on modifying the widget layout in
a page layout management system, specifically the order of relation and
Note widgets.
- User Intent Evolution: The user transitioned from defining
requirements to requesting a plan and then moving directly to
implementation.

2. Technical Foundation:
- Function: `usePageLayoutWithRelationWidgets`
- Purpose: To manage the layout of widgets on a page, particularly the
placement of relation widgets.

3. Codebase Status:
- File Name: `usePageLayoutWithRelationWidgets.ts`
- Purpose: To handle the layout of widgets dynamically based on
specified rules.
- Current State: Awaiting implementation of the user-defined widget
placement logic.
- Key Code Segments: The function signature is defined, but the
implementation details are yet to be added.

4. Problem Resolution:
- Issues Encountered: No specific technical problems were reported; the
focus was on planning and implementation.
- Solutions Implemented: None yet, as the implementation phase has just
begun.
- Debugging Context: No ongoing troubleshooting efforts were mentioned.
- Lessons Learned: The importance of clear widget placement requirements
was emphasized.

5. Progress Tracking:
- Completed Tasks: User has articulated the requirements for widget
placement.
- Partially Complete Work: Implementation of the specified changes is
pending.
- Validated Outcomes: No features have been confirmed working yet as
implementation has not started.

6. Active Work State:
- Current Focus: The user is preparing to implement the changes to the
`usePageLayoutWithRelationWidgets` function.
- Recent Context: The user has defined the order of widget placement and
is ready to start coding.
- Working Code: The function is currently defined but lacks the
implementation logic.
- Immediate Context: The user is focused on implementing the logic for
injecting relation widgets and positioning the Note widget.

7. Recent Operations:
- Last Agent Commands: "Start implementation."
- Tool Results Summary: No specific results were generated; the focus
was on user requests.
- Pre-Summary State: The agent was preparing to implement the changes to
the `usePageLayoutWithRelationWidgets` function.
- Operation Context: The commands executed were directly related to the
user's goal of modifying the widget layout.

8. Continuation Plan:
- Pending Task 1: Implement the logic to inject relation widgets after
the first FIELDS widget.
- Pending Task 2: Ensure the Note widget is positioned after all
relation widgets...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

Created from [VS
Code](https://code.visualstudio.com/docs/copilot/copilot-coding-agent).

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
2026-01-21 08:38:08 +00:00
6b97e3b428 i18n - translations (#17296)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 22:21:02 +01:00
3ed67b825e feat: implement generic many-to-many junction relation support (#16820)
## Overview

This PR implements **generic many-to-many relation support** through
junction tables (also known as associative entities or join tables).
This replaces the need for hardcoded taskTarget/noteTarget logic and
provides a flexible foundation for modeling complex entity
relationships.

## Architecture

### Data Model

Many-to-many relationships are implemented using a **junction object
pattern**:

```
┌─────────┐       ┌──────────────────┐       ┌─────────┐
│  Pet    │──────>│   PetRocket      │<──────│ Rocket  │
│         │ 1:N   │  (junction)      │  N:1  │         │
│ rockets ├───────┤ pet    : Pet     ├───────┤         │
└─────────┘       │ rocket : Rocket  │       └─────────┘
                  └──────────────────┘
```

The junction object (PetRocket) has:
- A `MANY_TO_ONE` relation to **Pet** (the source)
- A `MANY_TO_ONE` relation to **Rocket** (the target)

The source object (Pet) has a `ONE_TO_MANY` relation pointing to the
junction, with **field settings** that specify which target field to
follow.

### Field Settings Schema

Junction configuration is stored in `FieldMetadataRelationSettings`:

```typescript
{
  relationType: "ONE_TO_MANY",
  // Points to the target field on the junction object
  junctionTargetFieldId?: string;      // For regular relations
  junctionTargetMorphId?: string;      // For polymorphic relations
}
```

**Two configuration modes:**
1. **`junctionTargetFieldId`** - References a specific `RELATION` field
on the junction
2. **`junctionTargetMorphId`** - References a `morphId` group for
polymorphic targets (e.g., link to Person OR Company)

### GraphQL Query Generation

When a junction relation is detected, the GraphQL fields are generated
to fetch the nested target:

```graphql
query GetPetWithRockets {
  pet(id: "...") {
    rockets {           # ONE_TO_MANY to junction
      id
      rocket {          # Target field on junction
        id
        name
        __typename
      }
    }
  }
}
```

For polymorphic junction targets:
```graphql
caretakerPerson { id, name }
caretakerCompany { id, name }
```

## Frontend Architecture

### Display Flow

1. **Detection**: `hasJunctionConfig()` checks if field has junction
settings
2. **Config Resolution**: `getJunctionConfig()` resolves junction object
metadata and target fields
3. **Record Extraction**: `extractTargetRecordsFromJunction()` extracts
target records from junction records
4. **Rendering**: Target records displayed as chips (not junction
records)

### Edit Flow

1. **Picker Opening**: Initializes the multi-record picker with:
   - Searchable object types (derived from junction target fields)
   - Pre-selected items (extracted from existing junction records)
   
2. **Selection Handling**: Manages create/delete of junction records:
   - **Select**: Creates new junction record with source + target IDs
   - **Deselect**: Finds and deletes the junction record
- **Optimistic Updates**: Manually updates Recoil store before API call

### Key Trade-offs

| Decision | Trade-off |
|----------|-----------|
| Junction records managed manually | More control over optimistic
updates, but requires manual cache management |
| Settings stored per-field | Flexible (same junction can power
different views), but requires UI to configure |
| Polymorphic via morphId groups | Supports N target types, but adds
query complexity |
| Feature flag gated | Safe rollout, but requires flag management |

## Backend Changes

- **Validation**: Junction target field must exist and be a valid
`MANY_TO_ONE` relation
- **Settings**: Extended `FieldMetadataRelationSettings` type with
junction fields
- **Dev Seeder**: Added sample junction objects (PetRocket,
EmploymentHistory, PetCareAgreement) for testing

## How to Test

1. Enable the `IS_JUNCTION_RELATIONS_ENABLED` feature flag
2. Create objects with junction pattern (Pet → PetRocket → Rocket)
3. Configure the junction target in field settings (advanced mode)
4. Verify:
- Display shows target objects (Rockets), not junction records
(PetRockets)
   - Picker allows selecting/deselecting targets
   - Changes persist correctly



https://github.com/user-attachments/assets/d04f057a-228c-4de8-af48-76bb2d72cac1

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-01-20 21:58:13 +01:00
0e7e471312 [Dashboards] fix tooltip item ordering to match visual stacking in bar and line chart (#17288)
related to the second bug from this bucket -
https://github.com/twentyhq/core-team-issues/issues/2109

<img width="929" height="509" alt="CleanShot 2026-01-20 at 20 40 20"
src="https://github.com/user-attachments/assets/c29d86af-faec-48da-92d6-e4dd695ce91f"
/>

---------

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2026-01-20 19:35:42 +00:00
b1bd749abd i18n - translations (#17295)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 20:07:22 +01:00
Paul RastoinandGitHub e35b4b86cb Migrate serverless function service to v2 (#17285)
# Introduction

In this PR we're migrating the serverless function service that was
using the SF repo directly to the v2 build and runner.
The whole serverless engine now deals with flat entities only

## Resolvers
Refactored the resolvers ( serverlessFunction, route, database and cron
trigger) :
- return types to `dto`
- Standardized the flat to dto transpilation within the resolvers
- Find and findMany passing by the cached data

## Services
Refactored the services ( serverlessFunction, route, database and cron
trigger) :
- return type to be `flat`
- always calling v2 and computing cache

## New additional caches
- application variables ( cf
https://github.com/twentyhq/core-team-issues/issues/2116 )
- serverless function layer

## What to test:
- CRUD ( database trigger  , route trigger, cron trigger, serverless
function through workflows  )
- Duplicating a workflow with a serverless function code node  

## Concerns
We need to implement the cron that will hard delete soft deleted s3
serverless functions, not in this PR though ( cf
https://github.com/twentyhq/twenty/pull/17285#discussion_r2709168570 and
https://github.com/twentyhq/core-team-issues/issues/2118 )
2026-01-20 18:32:22 +00:00
ccf2eae684 i18n - translations (#17294)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 18:40:35 +01:00
EtienneandGitHub 30c13fc947 Files v2 - Add new FILES field type (#17236)
In this PR : 
- New field type FieldMetadataType.FILES added (as jsonb in DB)
- Backend: GraphQL types, validation, REST API schema, data processor
handling
- Frontend: field configuration in settings
- Feature flag IS_FILES_FIELD_ENABLED to gate the feature
- Integration tests for create/filter validation
- Dev seed: Feature flag enabled + FILES field on survey result object


To do in next PRs : 
- Backend : 
-- new workspaceFile controller (for download) & new workspaceFile
upload resolver (for upload)
-- pre-hook/post-hook to ensure fileId existence + listener to ensure
cleaning
- Frontend : FILES field display/edit in table view, record, ...
2026-01-20 17:03:49 +00:00
41329a0ea9 i18n - translations (#17292)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 18:09:44 +01:00
Thomas TrompetteandGitHub 799b08e1f2 Stop workflow in any status (#17271)
Workflows can now be cancelled when not started or enqueued.
Also displaying the stop option when selecting all.

We got the case of a client that did an infinite loop. So he had a lot
of workflows to stop. Supporting select all would have avoid him to wait
for 4 hours so we process the runs throttled. This is not something that
is supposed to happen often so I did not see the point of refactoring
the endpoint. But I wanted the users to have this option just in case
2026-01-20 16:38:39 +00:00
e40130f575 i18n - translations (#17290)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-20 17:42:30 +01:00
9cf88df67b Improve streamToBuffer error handling and memory safety (#17255)
## Description

This PR improves error handling and memory safety for the streamToBuffer
utility function.

### Changes
- Add proper cleanup of event listeners to prevent memory leaks
- Add checks for already-ended streams to handle edge cases gracefully
- Add protection against multiple resolve/reject calls with isResolved
flag
- Add handling for close events with appropriate error messages
- Improve robustness when streams are destroyed or closed externally

### Impact
This fixes potential memory leaks and race conditions when streams are
cancelled, destroyed, or closed before completion.

### Code Statistics
- 1 file changed
- ~53 lines added, 9 lines modified
- ~95% code changes (functional improvements)

---------

Co-authored-by: GitTensor Miner <miner@gittensor.io>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
2026-01-20 16:19:47 +00:00
Charles BochetandGitHub 351e2030ff Rework watcher (#17284)
Heavy Refactoring of the watcher, sorry about this one, I'll keep
iterating on it.

In a nutshell:
- app-dev.ts is maintaining 3 watchers in parallel: manifest, function
and frontComponent
2026-01-20 17:25:44 +01:00
WeikoandGitHub b6635ba272 Add RLS Entitlement check (#17179)
## Context
- Add RLS entitlement to billing
- Check value in the backend (for RLS predicate entity
queries/mutations)
- Expose billingEntitlements to the API inside currentWorkspace to check
available features to the workspace and display the role components
accordingly
- Cleanup RLS when plan changes back to one without RLS.

This should cover almost everything, imho we don't need to check in the
ORM because => We can't create RLS without the correct PLAN and
switching back to a PLAN without RLS deletes existing RLS through
stripes webhooks
2026-01-20 16:06:37 +00:00
martmullandGitHub 579c59bd11 2093 extensibility add twenty auth switch command (#17286)
add auth:switch and auth:list commands
2026-01-20 15:12:59 +00:00
Thomas TrompetteandGitHub e599d6c4d0 Handle dynamic predicates (#17287)
Fetch and send full workspace member to handle dynamic predicate. I only
tested that it did not break the current logic since the frontend does
not yet send queries we dynamic predicates.
2026-01-20 14:57:36 +00:00
martmullandGitHub 02a181a49f Add endpoint to upload application file (#17270)
As title
2026-01-20 14:10:34 +00:00
martmullandGitHub 801878cb2b 2091 extensibility twenty sdk add command twenty app function logs and twenty app function test (#17278)
add `function:logs` and `function:execute` cli commands
2026-01-20 14:10:01 +00:00
Raphaël BosiandGitHub f9c400cd78 Fix hotkey bugs (#17273)
- Fix a race condition due to a page change effect re-trigger which
reset the focus stack (A user had a bug which happened when he opened
the command menu quickly after the page load. The focus stack was reset
and the hotkeys listening were the one from the table instead of the one
from the command menu)
- Fix the focus id in the side panel input which prevented using the
hotkeys
2026-01-20 12:19:58 +00:00
nitinandGitHub a004662221 [Dashboards] align cumulative range filtering with raw-value filters (#17265)
closes https://github.com/twentyhq/core-team-issues/issues/2097

- Align cumulative range filtering with non‑cumulative behavior by
applying rangeMin/rangeMax to raw values before cumulative totals are
computed.
- Remove cumulative‑value filtering to prevent hidden points from
influencing visible totals.
2026-01-20 12:15:24 +00:00
nitinandGitHub 196de28a0c [Dashboards] add missing hideEmptyCategory field to PieChartConfiguration GraphQL fragment (#17266)
related to the first bug from this bucket -
https://github.com/twentyhq/core-team-issues/issues/2109

<img width="761" height="75" alt="CleanShot 2026-01-20 at 13 20 23"
src="https://github.com/user-attachments/assets/c30712ce-3959-4647-9178-68a2b0230c03"
/>
2026-01-20 10:54:15 +00:00
Charles BochetandGitHub c388b29f7c Simplify config file script in sdk (#17267)
Some simplifications about loading the manifest as there is no
difference between all entities
2026-01-20 12:03:36 +01:00
EtienneandGitHub 472fe4eec3 Fix dev env (#17269) 2026-01-20 09:57:20 +00:00
Charles BochetandGitHub 29c0c952e7 Add Front components to SDK (#17259)
## Add `frontComponent` entity type to SDK

This PR adds a new `frontComponent` entity type at parity with
`serverlessFunction`. Front components are React components that can be
defined and bundled as part of Twenty applications.

### Changes

#### New Entity Type
- Added `FRONT_COMPONENT` to `SyncableEntity` enum
- Front components use `*.front-component.tsx` file naming convention
- CLI command `twenty app:add` now includes `front-component` as an
option

#### SDK Application Layer (`twenty-sdk`)
- Added `defineFrontComponent()` function for defining front component
configurations with validation
- Added `FrontComponentConfig` type for component configuration
- Added `getFrontComponentBaseFile()` template generator for scaffolding
new front components
- Added `loadFrontComponentModule()` to load front component modules and
extract component metadata

#### Shared Types (`twenty-shared`)
- Added `FrontComponentManifest` type with `universalIdentifier`,
`name`, `description`, `componentPath`, and `componentName` fields
- Updated `ApplicationManifest` to include optional `frontComponents`
array

#### Manifest Build System
- Updated `manifest-build.ts` to discover and load
`*.front-component.tsx` files
- Updated `manifest-validate.ts` to validate front components and check
for duplicate IDs
- Updated `manifest-display.ts` to display front component count in
build summary
- Updated `manifest-plugin.ts` to display front component entry points
and watch `.tsx` files

#### Template (`create-twenty-app`)
- New applications now include a sample
`hello-world.front-component.tsx` file

#### Tests
- Added unit tests for `defineFrontComponent()`
- Added unit tests for `getFrontComponentBaseFile()`
2026-01-20 05:54:14 +00:00
WeikoandGitHub b64951634e fix rls create new record with composite (#17243)
## Context
buildValueFromFilter was not handling composite filters which was needed
for RLS. This PR implements that and fix some issues with RLS

Tested with a few composite + relation fields
<img width="559" height="206" alt="Screenshot 2026-01-19 at 15 38 42"
src="https://github.com/user-attachments/assets/d64afa6e-3e12-4843-a215-a693665112c5"
/>
2026-01-20 05:36:38 +00:00
39470b74a4 i18n - docs translations (#17260)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 19:40:33 +01:00
neo773andGitHub 527c00d326 refresh oAuth tokens when sending email (#17250)
calls `connectedAccountRefreshTokensService` in `SendEmailTool`
resolving error `Lifetime validation failed, the token is expired.`
2026-01-19 17:54:07 +00:00
038f9cc44f i18n - docs translations (#17251)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 19:02:22 +01:00
dc982abb78 i18n - translations (#17258)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 19:01:11 +01:00
Charles BochetandGitHub 70582d063e Twenty SDK watch build of functions (#17252)
## Summary

This PR adds function build support to the `twenty dev` command,
enabling tree-shaken production builds of serverless functions during
development with Vite's incremental build capabilities.

## Changes

### New Features
- **Function building in dev mode**: Serverless functions are now
compiled to tree-shaken bundles in `.twenty/functions/` during
development
- **Automatic restart on entry point changes**: When functions are added
or removed, the watcher automatically restarts with the new
configuration
- **Function entry point logging**: Dev mode now displays detected
function entry points with their names and paths
2026-01-19 17:43:40 +00:00
042b15a7d1 i18n - translations (#17257)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 18:49:13 +01:00
MarieandGitHub 1731c3427e [App] Small fixes (#17254)
- update doc
- update generated Twenty client following [comment on previous
pr](https://github.com/twentyhq/twenty/pull/17240#pullrequestreview-3678830392)
2026-01-19 17:32:43 +00:00
Raphaël BosiandGitHub d0bc8b9ffe [DASHBOARDS] Move all the graph computing logic to the backend (#17189)
- Create resolvers for each type of charts which needs data
transformation after the group by operation: Bar Chart, Line Chart and
Pie Chart
- Move all the utils to the backend and refactored some into services

This allows all the computation to be done in the backend, improving
performances in the frontend.
2026-01-19 17:25:45 +00:00
WeikoandGitHub 1f1ccb281e Cache WorkspaceMember (#17247)
## Context
Due to RLS feature, workspaceMember entity and its properties is
becoming necessary in different places of the backend. To avoid querying
many times, this PR adds a map of workspace members per workspaceId,
leveraging WorkspaceCache pattern for WorkspaceEntity (in opposition
with CoreEntity)

Due to its content (mostly PII), I prefer to cache it locally only (node
process VS Redis) using localDataOnly.

TODO: invalidation logic when the workspaceMember object is updated
(more precisely, when its field map is updated). There is no easy way
today to update that list so it can be done in another PR imho
2026-01-19 17:13:10 +00:00
Thomas TrompetteandGitHub b15c042e77 SSE - Protect query api (#17241)
- Ensure user can only add/remove queries of its own stream
- Prevent stream from being highjacked
2026-01-19 17:12:41 +00:00
83d9c580a0 i18n - translations (#17256)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 18:21:37 +01:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>DevessierBaptiste Devessier
4ce6119be7 Add progressive loading to FIELD widget CARD display mode (#17202)
https://github.com/user-attachments/assets/3a444937-0290-4505-85e1-64b7b713bc3b



- [x] Analyze the existing `FieldWidgetRelationCard` and
`FieldWidgetMorphRelationCard` components
- [x] Review existing patterns for "More" buttons (found
`IconChevronDown` pattern)
- [x] Create a reusable `FieldWidgetShowMoreButton` component for the
"More (X)" button
- [x] Update `FieldWidgetRelationCard` to show max 5 items initially
with progressive loading
- [x] Update `FieldWidgetMorphRelationCard` to show max 5 items
initially with progressive loading
- [x] Add Storybook story with play function to test progressive loading
for regular relations
- [x] Verify changes visually in Storybook (all tests pass)
- [x] Run code review (no issues found)
- [x] Run CodeQL security check (no code changes for CodeQL to analyze)
- [x] Address PR review feedback:
- [x] Move constants to separate files
(`FieldWidgetRelationCardInitialVisibleItems.ts` and
`FieldWidgetRelationCardLoadMoreIncrement.ts`)
- [x] Add translation for "More (X)" string using `t` macro from
`@lingui/core/macro`

## Summary

This PR adds progressive loading functionality to the FIELD widget with
CARD display mode:

1. **New Component**: Created `FieldWidgetShowMoreButton` - A styled
button component that displays "More (X)" with a chevron icon, showing
the remaining count of hidden items.

2. **Updated Components**:
- `FieldWidgetRelationCard`: Now displays max 5 relation cards
initially, with a "More (X)" button that loads 5 more items on each
click
- `FieldWidgetMorphRelationCard`: Same progressive loading behavior for
morph relations

3. **Constants**: Moved to separate files following codebase
conventions:
   - `FIELD_WIDGET_RELATION_CARD_INITIAL_VISIBLE_ITEMS` (5)
   - `FIELD_WIDGET_RELATION_CARD_LOAD_MORE_INCREMENT` (5)

4. **Storybook Story**: Added
`OneToManyRelationCardWidgetWithProgressiveLoading` story with play
function that tests:
   - Initial display of 5 items
   - "More (7)" button visibility
   - Loading additional items on click
   - Button disappearing when all items are shown

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[RPL] Allow users to progressively load relations in
FIELD widget with CARD display mode</issue_title>
> <issue_description>## Current state
> 
> The FIELD widget should display max 5 relations in CARD display mode.
> 
> ## Goal
> 
> Display a "More (12)" button. Clicking on this button loads 5 more
items. After a click, "More (12)" becomes "More (7)".
> 
> <img width="1334" height="712" alt="Image"
src="https://github.com/user-attachments/assets/e801cca2-af99-4d87-8ce2-0c639775b0b1"
/>
> 
> ## Technical input
> 
> `FieldWidgetRelationCard` already loads all the relations and maps
over each one of them. Create a state that's updated when clicking on
the More button, slicing the relations to display.
> 
> ## Acceptance criteria
> 
> - Must work for relations and morph relations in _card_ display mode
> - Create stories to assert it works properly (with play function to
actually test the components)</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes twentyhq/core-team-issues#2063

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
2026-01-19 16:58:23 +00:00
Charles BochetandGitHub 5a5e9eeb9e Watch command skeleton (#17246)
Summary

Rewrites the app:dev command to use Vite's dev server instead of manual
file watching with chokidar. This provides better file watching
capabilities and aligns with the existing build tooling.

<img width="1588" height="822" alt="image"
src="https://github.com/user-attachments/assets/7c54bd39-4905-456f-8e3d-64e97b031de0"
/>
2026-01-19 16:05:49 +00:00
374303455a i18n - translations (#17249)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 17:02:08 +01:00
Abdul RahmanandGitHub 3c26ebace2 Fix workflow if-else node issues (#17244) 2026-01-19 15:42:06 +00:00
Paul RastoinandGitHub 28e98086b0 Identify index (#17239)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract
2026-01-19 15:36:41 +00:00
e0d1edb940 i18n - translations (#17248)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-19 16:50:31 +01:00
MarieandGitHub 1a38152451 Support referencing updatedFields array in databaseEvent triggers (#17240)
Closes https://github.com/twentyhq/core-team-issues/issues/1838

DatabaseEventTriggers can now define a field-level granularity on which
updates they should react to.
After a discussion with the team, we have decided to rely on field names
and not UID, just as we do for objects. The rationale is that we want to
keep a pleasant devX and consider it's not on twenty to ensure
continuity of api usage around an object if their name is updated: for
instance if a user has based their webhook on the name of an object, if
they decide to update it, they have to take care of updating their
webhook.
2026-01-19 15:33:49 +00:00
EtienneandGitHub f381516d30 BILLING - Send dis-sync error to sentry (#17242)
Investigate Stripe activity when workspace not found, creating
[alert](https://twenty-v7.sentry.io/issues/alerts/rules/twenty-server/16611050/details/)
in Sentry
2026-01-19 15:31:37 +00:00
Paul RastoinandGitHub 59c7295033 Identify standard role (#17234)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract

## Note
Added build to typeorm nx generate migration command so we never forgot
to build server before
2026-01-19 15:04:33 +00:00
Charles BochetandGitHub 905898d109 Twenty SDK command renaming and dev mode (#17245)
## Description

This PR improves the developer experience for the `twenty-sdk` and
`create-twenty-app` packages by reorganizing commands, adding
development tooling, and improving documentation.

## Changes

### twenty-sdk

#### Command Refactoring
- **Renamed `app-watch` → `app-dev`**: Renamed `AppWatchCommand` to
`AppDevCommand` and moved to `app-dev.ts` for consistent naming with the
CLI command `app:dev`
- **Moved `app-add` → `entity-add`**: Relocated entity creation logic
from `app/app-add.ts` to `entity/entity-add.ts` and renamed
`AppAddCommand` to `EntityAddCommand` for better separation of concerns

#### Development Tooling
- **Added `dev` target**: New Nx target `npx nx run twenty-sdk:dev` that
runs the build in watch mode for faster development iteration

### create-twenty-app

#### Improved Scaffolded Project
- **Enhanced base-application README** with:
  - Updated Getting Started to recommend `yarn dev` for development
  - Added "Available Commands" section listing all available scripts
  
#### Better Onboarding
- **Improved success message** after app creation with formatted next
steps:
2026-01-19 16:02:12 +01:00
martmullandGitHub 3958664e87 Move assets folder at root level (#17238)
fix assets management
2026-01-19 16:01:54 +01:00
Abdullah.andGitHub 3c0314d14a fix: jsdiff has a denial of service vulnerability in parsePatch and applyPatch (#17235)
Resolves [Dependabot Alert
366](https://github.com/twentyhq/twenty/security/dependabot/366).
2026-01-19 14:19:18 +00:00
Charles BochetandGitHub e61b92132c Rework folder structure (#17230)
<img width="700" height="771" alt="image"
src="https://github.com/user-attachments/assets/b784fbe6-f976-4c9a-84c1-3d90a9010665"
/>

Second one :)
2026-01-19 14:08:05 +00:00
Thomas TrompetteandGitHub 7cf0dc522a Add permissions to SSE (#17201)
Flow:
- fetch user role from context stored in cache - do not support api
context yet
- check object permissions
- filter restricted fields on event
- fetch role rls predicate and combine it with the query filter

Do not support dynamic predicates yet.
2026-01-19 13:39:12 +00:00
Baptiste DevessierandGitHub 2505c631de fix: ensure we display fields in the same order as in production (#17231)
- Do not render fields that we don't want to render for now
- Preverse alphabetical order

## Without feature flag

<img width="3456" height="2160" alt="CleanShot 2026-01-19 at 11 02
57@2x"
src="https://github.com/user-attachments/assets/886116b5-6e17-4b68-96b6-ab1ed491a1e5"
/>

## With feature flag

<img width="3456" height="2234" alt="CleanShot 2026-01-19 at 11 03
31@2x"
src="https://github.com/user-attachments/assets/9bf47ca3-bc34-47de-b8e5-9615eb684a05"
/>

## With feature flag–before

<img width="3456" height="2160" alt="CleanShot 2026-01-19 at 11 03
53@2x"
src="https://github.com/user-attachments/assets/71c7dc16-b3da-4dfa-86b2-2c2ec7ff0bd8"
/>
2026-01-19 13:18:36 +00:00
Paul RastoinandGitHub 859e718cf2 Identify view group (#17220)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract
2026-01-19 12:56:52 +00:00
Félix MalfaitandGitHub 0b3f243f24 fix: E2E login test flakiness (#17233)
## Summary
- Fix E2E login test flakiness by using `click()` auto-waiting instead
of `isVisible()` check
- The login form shows a loader while GraphQL data loads. The previous
`isVisible()` check returned immediately (no waiting) and would fail
while the loader was showing
- Using `click()` which has built-in auto-waiting for elements to be
visible and actionable fixes this

## Test plan
- E2E tests should pass more reliably in CI
- Login setup test should no longer timeout waiting for the email field
2026-01-19 12:39:14 +00:00
4ab95235ce [Breaking: DEPLOY SERVER BEFORE FRONT] fix: allow custom domain without Cloudflare API key (#17160)
## Summary

Fixes #17101

When self-hosting TwentyCRM, users can now set workspace custom domains
without requiring CLOUDFLARE_API_KEY to be configured. This enables
manual DNS configuration for those not using Cloudflare.

## Changes

- Added isCloudflareConfigured method to DnsManagerService
- Modified all Cloudflare-dependent methods to gracefully handle missing
configuration
- Added getManualDnsRecords helper that provides DNS configuration
instructions when Cloudflare is not available
- isHostnameWorking returns true in manual mode, allowing the domain to
be saved and enabled
- Added comprehensive tests for non-Cloudflare scenarios

## Behavior

When CLOUDFLARE_API_KEY is set: Works exactly as before with automatic
Cloudflare provisioning

When CLOUDFLARE_API_KEY is NOT set:
- Custom domain can be saved to the database
- User receives manual DNS configuration instructions
- Domain is marked as working, user is responsible for external DNS and
TLS configuration

## Testing

Added tests covering non-Cloudflare scenarios. Full test suite requires
Docker which was not run locally.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces a Cloudflare integration feature flag and wires it through
server and front to gate the custom domain UI.
> 
> - Server: adds `isCloudflareIntegrationEnabled` to `ClientConfig`,
computed from `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ZONE_ID` in
`client-config.service`; updates GraphQL schema and unit tests.
> - Frontend: adds `isCloudflareIntegrationEnabledState`, extends
`ClientConfig` type, sets the flag in `useClientConfig`, and
conditionally renders `SettingsCustomDomain` in `SettingsDomain` when
enabled.
> - Updates mocked client config to include the new flag.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0c76dde03b. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2026-01-19 12:47:09 +01:00
Félix MalfaitandGitHub dc93cf4c59 feat: add TypeScript Go (tsgo) for faster type checking (#17211)
## Summary

- Add `@typescript/native-preview` (tsgo) for dramatically faster type
checking on frontend projects
- Configure tsgo as default for frontend projects (twenty-front,
twenty-ui, twenty-shared, etc.)
- Keep tsc for twenty-server (faster for NestJS decorator-heavy code)
- Fix type imports for tsgo compatibility (DOMPurify, AxiosInstance)
- Remove deprecated `baseUrl` from tsconfigs where safe

## Performance Results

| Project | tsgo | tsc -b | Speedup |
|---------|------|--------|---------|
| **twenty-front** | 1.4s | 60.7s | **43x faster** |
| **twenty-server** | 2m42s | 1m10s | tsc is faster (decorators) |

tsgo excels at modern React/JSX codebases but struggles with
decorator-heavy NestJS backends, so we use the optimal checker for each.

## Usage

```bash
# Default (tsgo for frontend, tsc for backend)
nx typecheck twenty-front
nx typecheck twenty-server

# Force tsc fallback if needed
nx typecheck twenty-front --configuration=tsc

# Force tsgo on backend (slower, not recommended)
nx typecheck twenty-server --configuration=tsgo
```

## Test plan

- [x] `nx typecheck twenty-front` passes with tsgo
- [x] `nx typecheck twenty-server` passes with tsc
- [x] `nx run-many -t typecheck --exclude=fireflies` passes
- [ ] CI tests pass
2026-01-19 12:46:34 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
42c6a04be6 Bump eslint-plugin-prettier from 5.2.1 to 5.5.5 (#17228)
Bumps
[eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier)
from 5.2.1 to 5.5.5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/eslint-plugin-prettier/releases">eslint-plugin-prettier's
releases</a>.</em></p>
<blockquote>
<h2>v5.5.5</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/772">#772</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7264ed0a6cf47fc36befed32f459e7d875f5992c"><code>7264ed0</code></a>
Thanks <a href="https://github.com/BPScott"><code>@​BPScott</code></a>!
- Bump prettier-linter-helpers dependency to v1.0.1</p>
</li>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/776">#776</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/77651a33cd16fd4c50b7346971990b900a42408b"><code>77651a3</code></a>
Thanks <a href="https://github.com/aswils"><code>@​aswils</code></a>! -
fix: bump synckit for yarn PnP ESM issue</p>
</li>
</ul>
<h2>v5.5.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/755">#755</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/723f7a803f014746f2146e5be021c9071fa52d7e"><code>723f7a8</code></a>
Thanks <a href="https://github.com/kbrilla"><code>@​kbrilla</code></a>!
- fix: add 'oxc', 'oxc-ts' and 'hermes' parsers to
<code>parserBlocklist</code></p>
</li>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/751">#751</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/cf52b306a533b971bf40bbbf0d2033a1ed4f3c5d"><code>cf52b30</code></a>
Thanks <a
href="https://github.com/andreww2012"><code>@​andreww2012</code></a>! -
fix: disallow extra properties in rule options</p>
</li>
</ul>
<h2>v5.5.3</h2>
<p>republish the latest version</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.2...v5.5.3">https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.2...v5.5.3</a></p>
<h2>v5.5.2</h2>
<p>republish the latest version</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.1...v5.5.2">https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.1...v5.5.2</a></p>
<h2>v5.5.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/748">#748</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/bfd1e9547de9afaaf30318735f2f441c0250b77e"><code>bfd1e95</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- fix: use <code>prettierRcOptions</code> directly for prettier
3.6+</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.0...v5.5.1">https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.0...v5.5.1</a></p>
<h2>v5.5.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/743">#743</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/92f2c9c8f0b083a0208b4236cf5c8e4af5612a8b"><code>92f2c9c</code></a>
Thanks <a
href="https://github.com/dotcarmen"><code>@​dotcarmen</code></a>! -
feat: support non-js languages like <code>css</code> for
<code>@eslint/css</code> and <code>json</code> for
<code>@eslint/json</code></li>
</ul>
<h3>New Contributors</h3>
<ul>
<li><a href="https://github.com/dotcarmen"><code>@​dotcarmen</code></a>
made their first contribution in <a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/743">prettier/eslint-plugin-prettier#743</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.4.1...v5.5.0">https://github.com/prettier/eslint-plugin-prettier/compare/v5.4.1...v5.5.0</a></p>
<h2>v5.4.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/740">#740</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/c21521ffbe7bfb60bdca8cbf6349fba4de774d21"><code>c21521f</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- fix(deps): bump <code>synckit</code> to v0.11.7 to fix potential
<code>TypeError: Cannot read properties of undefined (reading
'message')</code> error</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.4.0...v5.4.1">https://github.com/prettier/eslint-plugin-prettier/compare/v5.4.0...v5.4.1</a></p>
<h2>v5.4.0</h2>
<h3>Minor Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md">eslint-plugin-prettier's
changelog</a>.</em></p>
<blockquote>
<h2>5.5.5</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/772">#772</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7264ed0a6cf47fc36befed32f459e7d875f5992c"><code>7264ed0</code></a>
Thanks <a href="https://github.com/BPScott"><code>@​BPScott</code></a>!
- Bump prettier-linter-helpers dependency to v1.0.1</p>
</li>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/776">#776</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/77651a33cd16fd4c50b7346971990b900a42408b"><code>77651a3</code></a>
Thanks <a href="https://github.com/aswils"><code>@​aswils</code></a>! -
fix: bump synckit for yarn PnP ESM issue</p>
</li>
</ul>
<h2>5.5.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/755">#755</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/723f7a803f014746f2146e5be021c9071fa52d7e"><code>723f7a8</code></a>
Thanks <a href="https://github.com/kbrilla"><code>@​kbrilla</code></a>!
- fix: add 'oxc', 'oxc-ts' and 'hermes' parsers to
<code>parserBlocklist</code></p>
</li>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/751">#751</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/cf52b306a533b971bf40bbbf0d2033a1ed4f3c5d"><code>cf52b30</code></a>
Thanks <a
href="https://github.com/andreww2012"><code>@​andreww2012</code></a>! -
fix: disallow extra properties in rule options</p>
</li>
</ul>
<h2>5.5.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/748">#748</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/bfd1e9547de9afaaf30318735f2f441c0250b77e"><code>bfd1e95</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- fix: use <code>prettierRcOptions</code> directly for prettier
3.6+</li>
</ul>
<h2>5.5.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/743">#743</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/92f2c9c8f0b083a0208b4236cf5c8e4af5612a8b"><code>92f2c9c</code></a>
Thanks <a
href="https://github.com/dotcarmen"><code>@​dotcarmen</code></a>! -
feat: support non-js languages like <code>css</code> for
<code>@eslint/css</code> and <code>json</code> for
<code>@eslint/json</code></li>
</ul>
<h2>5.4.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/740">#740</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/c21521ffbe7bfb60bdca8cbf6349fba4de774d21"><code>c21521f</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- fix(deps): bump <code>synckit</code> to v0.11.7 to fix potential
<code>TypeError: Cannot read properties of undefined (reading
'message')</code> error</li>
</ul>
<h2>5.4.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/736">#736</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/59a0cae5f27801d7e00f257c6be059a848b32fbe"><code>59a0cae</code></a>
Thanks <a
href="https://github.com/yashtech00"><code>@​yashtech00</code></a>! -
refactor: migrate <code>worker.js</code> to <code>worker.mjs</code></li>
</ul>
<h2>5.3.1</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/pull/734">#734</a>
<a
href="https://github.com/prettier/eslint-plugin-prettier/commit/dcf2c8083e0f7146b7b7d641224ee2db8b318189"><code>dcf2c80</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- ci: enable <code>NPM_CONFIG_PROVENANCE</code> env</li>
</ul>
<h2>5.3.0</h2>
<h3>Minor Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/e2c154a7214d4548dad225a56ee1e333d6389b66"><code>e2c154a</code></a>
chore: release eslint-plugin-prettier (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/773">#773</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/6795c1abf6dc9949da8681b05ec31d323794d00c"><code>6795c1a</code></a>
build(deps): Bump the actions group across 1 directory with 2 updates
(<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/774">#774</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/77651a33cd16fd4c50b7346971990b900a42408b"><code>77651a3</code></a>
fix: bump synckit for yarn PnP ESM issue (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/776">#776</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/7264ed0a6cf47fc36befed32f459e7d875f5992c"><code>7264ed0</code></a>
chore: bump prettier-linter-helpers to v1.0.1 (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/772">#772</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/e11a5b7e71f41b3238da944ba1ee84f7f518a4f4"><code>e11a5b7</code></a>
build(deps): Bump the actions group across 1 directory with 3 updates
(<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/769">#769</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/befda88381335cd5491d2aaa16b67350ba3cc602"><code>befda88</code></a>
ci: enable trusted publishing (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/757">#757</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/e2c31d20f326133157a12d0989097ebd52860c5b"><code>e2c31d2</code></a>
chore: release eslint-plugin-prettier (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/756">#756</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/98a8bfd269f0f2ead6750ec88eb81f6d59b6c005"><code>98a8bfd</code></a>
chore(deps): update all dependencies (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/750">#750</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/cf52b306a533b971bf40bbbf0d2033a1ed4f3c5d"><code>cf52b30</code></a>
fix: disallow extra properties in rule options (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/751">#751</a>)</li>
<li><a
href="https://github.com/prettier/eslint-plugin-prettier/commit/723f7a803f014746f2146e5be021c9071fa52d7e"><code>723f7a8</code></a>
fix: add 'oxc', 'oxc-ts' and 'hermes' parsers to
<code>parserBlocklist</code> (<a
href="https://redirect.github.com/prettier/eslint-plugin-prettier/issues/755">#755</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.1...v5.5.5">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by [GitHub Actions](<a
href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a>
Actions), a new releaser for eslint-plugin-prettier since your current
version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=eslint-plugin-prettier&package-manager=npm_and_yarn&previous-version=5.2.1&new-version=5.5.5)](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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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-01-19 10:26:33 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
58619e3b02 Bump json-2-csv from 5.5.5 to 5.5.10 (#17226)
Bumps [json-2-csv](https://github.com/mrodrig/json-2-csv) from 5.5.5 to
5.5.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mrodrig/json-2-csv/releases">json-2-csv's
releases</a>.</em></p>
<blockquote>
<h2>NPM Release v5.5.10</h2>
<ul>
<li>Ability to rename individual field headers - thanks to <a
href="https://github.com/petterw03"><code>@​petterw03</code></a> - <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/280">#280</a></li>
<li>NPM Audit Fix patches</li>
</ul>
<h2>NPM Release v5.5.9</h2>
<ul>
<li>Thanks to <a
href="https://github.com/jose-cabral"><code>@​jose-cabral</code></a> for
reporting <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/273">#273</a>
and implementing a fix for it in <a
href="https://redirect.github.com/mrodrig/json-2-csv/pull/275">mrodrig/json-2-csv#275</a></li>
</ul>
<h2>NPM Release v5.5.8</h2>
<ul>
<li>Incorporates <a
href="https://github.com/sevrai"><code>@​sevrai</code></a>'s fix from <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/271">#271</a></li>
<li>Dev Dependency vulnerability patch</li>
</ul>
<h2>NPM Release v5.5.7</h2>
<ul>
<li>Fixes the bug identified in <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/265">#265</a></li>
<li>Patches a high severity vulnerability identified in a dependency by
<code>npm audit</code></li>
</ul>
<h2>NPM Release v5.5.6</h2>
<ul>
<li>Thanks <a href="https://github.com/Altioc"><code>@​Altioc</code></a>
for finding and fixing <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/264">#264</a></li>
<li>Patches a moderate severity vulnerability in the dependency chain
via <code>npm audit fix</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/dd154d63966b8edd9396d3c323d16aa1597e3e3a"><code>dd154d6</code></a>
Merge pull request <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/283">#283</a>
from mrodrig/mr-patch-oct25</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/2c5da6c5c3f8a876d3d6b781c1f0e0ca19200e05"><code>2c5da6c</code></a>
chore(rel): 5.5.10</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/6db4ca1ce1cc903cfa0fe7411a7e4b037b231998"><code>6db4ca1</code></a>
chore(deps): npm audit fix</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/e3a9bcfbf2991a8fece6e85d33a56054635a1c39"><code>e3a9bcf</code></a>
Merge pull request <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/280">#280</a>
from petterw03/rename-header-fields</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/a617948ae26649196a3e9e3c06e7df701a4dba89"><code>a617948</code></a>
add documentation for fieldTitleMap</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/d057d2423da089f72f5cb627bc26b5a64d96ee27"><code>d057d24</code></a>
remove redundant type definition</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/4b6a177ab0aa649b97e6aaa32c71ee085bae7dd2"><code>4b6a177</code></a>
allow user to rename individual field headers</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/6f43ada8a2310c3e16fb98a43b7429fbc07af792"><code>6f43ada</code></a>
chore(rel): 5.5.9</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/98af2bdc8d6f7a2bb4d73fccfabe2de15996777b"><code>98af2bd</code></a>
Merge pull request <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/275">#275</a>
from jose-cabral/nested-arrays-273</li>
<li><a
href="https://github.com/mrodrig/json-2-csv/commit/32ca8c4cc578a13fcd231f27f17e6bb8445bfe59"><code>32ca8c4</code></a>
fixes <a
href="https://redirect.github.com/mrodrig/json-2-csv/issues/273">#273</a>:
nested arrays are not always unwound</li>
<li>Additional commits viewable in <a
href="https://github.com/mrodrig/json-2-csv/compare/5.5.5...5.5.10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=json-2-csv&package-manager=npm_and_yarn&previous-version=5.5.5&new-version=5.5.10)](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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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-01-19 10:26:06 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
66ec2f124f Bump eslint-config-prettier from 9.1.0 to 9.1.2 (#17227)
Bumps
[eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)
from 9.1.0 to 9.1.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md">eslint-config-prettier's
changelog</a>.</em></p>
<blockquote>
<h1>eslint-config-prettier</h1>
<h2>10.1.5</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/332">#332</a>
<a
href="https://github.com/prettier/eslint-config-prettier/commit/60fef02574467d31d10ff47ecb567d378483c9d4"><code>60fef02</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- chore: add <code>funding</code> field into
<code>package.json</code></li>
</ul>
<h2>10.1.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/328">#328</a>
<a
href="https://github.com/prettier/eslint-config-prettier/commit/94b47999e7eb13b703835729331376cef598b850"><code>94b4799</code></a>
Thanks <a
href="https://github.com/silvenon"><code>@​silvenon</code></a>! -
fix(cli): do not crash on no rules configured</li>
</ul>
<h2>10.1.3</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/325">#325</a>
<a
href="https://github.com/prettier/eslint-config-prettier/commit/4e95a1d50073f1a24f004239ad6e1a4ffa8476df"><code>4e95a1d</code></a>
Thanks <a href="https://github.com/pilikan"><code>@​pilikan</code></a>!
- fix: this package is <code>commonjs</code>, align its types
correctly</li>
</ul>
<h2>10.1.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/321">#321</a>
<a
href="https://github.com/prettier/eslint-config-prettier/commit/a8768bfe54a91d08f0cef8705f91de2883436bb0"><code>a8768bf</code></a>
Thanks <a href="https://github.com/Fdawgs"><code>@​Fdawgs</code></a>! -
chore(package): add homepage for some 3rd-party registry - see <a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/321">#321</a>
for more details</li>
</ul>
<h2>10.1.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/309">#309</a>
<a
href="https://github.com/prettier/eslint-config-prettier/commit/eb56a5e09964e49045bccde3c616275eb0a0902d"><code>eb56a5e</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- fix: separate the <code>/flat</code> entry for compatibility</p>
<p>For flat config users, the previous
<code>&quot;eslint-config-prettier&quot;</code> entry still works, but
<code>&quot;eslint-config-prettier/flat&quot;</code> adds a new
<code>name</code> property for <a
href="https://eslint.org/blog/2024/04/eslint-config-inspector/">config-inspector</a>,
we just can't add it for the default entry for compatibility.</p>
<p>See also <a
href="https://redirect.github.com/prettier/eslint-config-prettier/issues/308">prettier/eslint-config-prettier#308</a></p>
<pre lang="ts"><code>// before
import eslintConfigPrettier from &quot;eslint-config-prettier&quot;;
<p>// after<br />
import eslintConfigPrettier from
&quot;eslint-config-prettier/flat&quot;;<br />
</code></pre></p>
</li>
</ul>
<h2>10.1.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/prettier/eslint-config-prettier/pull/306">#306</a>
<a
href="https://github.com/prettier/eslint-config-prettier/commit/56e2e3466391d0fdfc200e42130309c687aaab53"><code>56e2e34</code></a>
Thanks <a href="https://github.com/JounQin"><code>@​JounQin</code></a>!
- feat: migrate to exports field</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/prettier/eslint-config-prettier/commits">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/~jounqin">jounqin</a>, a new releaser for
eslint-config-prettier since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=eslint-config-prettier&package-manager=npm_and_yarn&previous-version=9.1.0&new-version=9.1.2)](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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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-01-19 10:25:44 +00:00
Charles BochetandGitHub 7c713577f1 Rework folder structure (#17229)
<img width="700" height="771" alt="image"
src="https://github.com/user-attachments/assets/509b95b5-9c4f-474d-a47f-f950dee189ea"
/>
2026-01-19 09:36:40 +00:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>DevessierDevessier
72e89d5c6b Add "See all" widget action for ONE_TO_MANY relation fields (#17192)
FIELD widgets displaying ONE_TO_MANY relations now show a "See all"
action button (arrow-up-right icon) that navigates to the record index
with filtered relations.

### Demo



https://github.com/user-attachments/assets/fd632553-70e3-4757-8f26-80aa8d2ce0d2



### Changes

- **`WidgetAction` type**: Added `'see-all'` to `WidgetActionId`
- **`useWidgetActions` hook**: Returns `'see-all'` action for
ONE_TO_MANY relation fields (position 0, before edit)
- **`WidgetActionFieldSeeAll` component**: Renders the navigation button
with:
  - `IconArrowUpRight` icon
  - Link computed using same logic as `RecordDetailRelationSection`
  - Hover visibility behavior matching existing edit button
- **`WidgetActionRenderer`**: Added case for `'see-all'` action
- **Storybook**: Added `OneToManyRelationFieldWidgetWithSeeAllButton`
story verifying button visibility and well-formed link

### Link computation

Uses the same filter URL pattern as `RecordDetailRelationSection`:

```typescript
const filterQueryParams = {
  filter: {
    [relationFieldMetadataItem.name]: {
      [ViewFilterOperand.IS]: {
        selectedRecordIds: [targetRecord.id],
      },
    },
  },
  viewId: indexViewId,
};

const filterLinkHref = getAppPath(
  AppPath.RecordIndexPage,
  { objectNamePlural: relationObjectMetadataItem.namePlural },
  filterQueryParams,
);
```

The "see-all" action is shown regardless of field read-only status since
it's a navigation action, not an edit action.

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[RPL] Display "See all" widget action for FIELD widget
displaying relations</issue_title>
> <issue_description>The "See all" widget action is the button you can
see in the top right corner in the following screen. We should redirect
the user to the record index showing the filtered relations.
> 
> <img width="1334" height="712" alt="Image"
src="https://github.com/user-attachments/assets/c8aca25e-2136-43b3-8896-abd161a5693e"
/>
> 
> Example of interaction today in production:
> 
>
https://github.com/user-attachments/assets/3730301c-d04b-4195-b2fb-a51f8efee08a
> 
> ## Technical details
> 
> See `useWidgetActions` and `WidgetActionRenderer`. Use the
`arrow-up-right` icon.
> 
> See how link is computed here:
https://github.com/twentyhq/twenty/blob/3cf7803ee1ae545add02f0c628a933618ce736d5/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSection.tsx#L193-L204.
Reuse the same link.
> 
> ## Acceptance criteria
> 
> - The action should be displayed in addition to other actions that
might be rendered today
> - The action must only be displayed for ONE_TO_MANY relations
> - Ensure you write at least one story to ensure the button is
correctly displayed; the button should a well formed
link</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes twentyhq/core-team-issues#2064

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
2026-01-19 09:09:02 +00:00
92a080b704 Improve image upload error handling and validation (#17188)
- Add URL validation in getImageBufferFromUrl utility
- Add response status validation and content-type checking
- Add timeout and connection error handling with specific error messages
- Validate buffer is not empty before processing
- Validate file type detection results before proceeding
- Ensure detected file type is actually an image format
- Add proper type safety for Axios error handling

This improves robustness when uploading images from URLs by:
- Preventing invalid URLs from being processed
- Providing clear error messages for different failure scenarios
- Ensuring only valid image files are processed
- Handling network errors gracefully

---------

Co-authored-by: GitTensor Miner <miner@gittensor.io>
2026-01-19 08:40:02 +00:00
5cef07af45 Twenty SDK iteration (#17223)
Opening a PR to merge the wip work by @martmull 
We will re-organize twenty-sdk in an upcoming PR

---------

Co-authored-by: martmull <martmull@hotmail.fr>
2026-01-19 09:41:54 +01:00
b001991047 i18n - translations (#17224)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-18 20:00:54 +01:00
MarieandGitHub 2c596e7b1e [Apps] App misc - fixes + settings permissions for apps + uploadFile (#17167)
In this PR

- handle settings permission check for applications. Until then this was
unhandled and applications could not perform actions requiring settings
permissions, even if they were granted them

- fix ties to attachment, noteTarget etc: 
When an object had their fields synchronized in the app, the system
fields created as a side-effect of the object creation (relations to
noteTarget, attachment, taskTarget, favorites, timelineActivities -
created with `isCustom: true`, not sure that is correct btw) were then
deleted because they are not declared in the app, and identified as
deletable because of `isCustom: true`.
Updating the logic to exclude system fields from the logic that detects
fields to delete.
I think this outline the confusion we have around isCustom, isSystem
etc.

- introduce uploadFile util in generated twenty client as it cannot be
handled by the client's query / mutation. I had to use this for my
invoicing app
2026-01-18 18:41:48 +00:00
Paul RastoinandGitHub 6070dbf16a Identify agent (#17221)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract
2026-01-18 16:31:25 +00:00
Paul RastoinandGitHub fae6d0e262 Improve cleaning job (#17208)
# Introduction

Refactored the workspace deletion to dynamically iterate over all known
v2 syncable entities repos and delete all of them from child to parent
Exception for field metadata that we chunk delete in order to avoid
locking the core schema too long, it does not have an impact on perfs at
all ( neither plus or less ) Chunking by constraint within a transaction
is not necessary both does not cost more

## From 
30s for a workspace complete deletion
```ts
[Nest] 93244  - 01/16/2026, 10:24:52 PM     LOG [WorkspaceService] workspace WS_ID cache flushed
[Runner] Total execution: 26.290s // ( deleteAllObjectMetadatas v2 )
[Nest] 93244  - 01/16/2026, 10:25:22 PM     LOG [WorkspaceService] workspace WS_ID hard deleted
```

## To
3s !

```ts
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [DatabaseConfigDriver] [INIT] Config variables loaded: 0 values found in DB, 69 falling to env vars/defaults
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [CleanSuspendedWorkspacesCommand] IGNORING GRACE PERIOD - Cleaning 1 suspended workspaces
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [CleanerWorkspaceService] batchWarnOrCleanSuspendedWorkspaces running...
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [CleanerWorkspaceService] Processing workspace - 1/1
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [CleanerWorkspaceService] Destroying workspace Twenty Eng
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace user workspaces deleted
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace cache flushed
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 80 viewFilter record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 21 pageLayoutWidget record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 1515 viewField record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 91 index record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 66 roleTarget record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 174 viewGroup record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 1 agent record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 7 pageLayout record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 111 view record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 1/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 2/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 3/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 4/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 5/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 6/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 7/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 8/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 9/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 10/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 11/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 12/15 - deleted 51 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 13/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 14/15 - deleted 50 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: fieldMetadata chunk 15/15 - deleted 36 record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 737 fieldMetadata record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 6 role record(s)
[Nest] 65112  - 01/18/2026, 4:37:38 PM     LOG [WorkspaceService] workspace: deleted 78 serverlessFunction record(s)
[Nest] 65112  - 01/18/2026, 4:37:39 PM     LOG [WorkspaceService] workspace: deleted 43 objectMetadata record(s)
[Nest] 65112  - 01/18/2026, 4:37:41 PM     LOG [WorkspaceService] workspace hard deleted
[Nest] 65112  - 01/18/2026, 4:37:41 PM     LOG [CleanerWorkspaceService] Destroyed 1 workspaces on 5 limit durings this execution
[Nest] 65112  - 01/18/2026, 4:37:41 PM     LOG [CleanerWorkspaceService] batchWarnOrCleanSuspendedWorkspaces done!
[Nest] 65112  - 01/18/2026, 4:37:41 PM     LOG [CleanSuspendedWorkspacesCommand] Command completed!
```


## Update
Discussed with @charlesBochet ended debugging and analyzing sql query
operations
He discovered that we were not indexing foreignKey effectively
We've ended up fixing all the FK indeces coverage leading to 

## Cleaning
Removed the
```sh
npx nx run twenty-server:command workspace:clean-soft-deleted-suspended-workspaces --ignore-grace-period
```
In favor of
```sh
npx nx run twenty-server:command workspace:clean --only-operation destroy --ignore-destroy-grace-period
```

## Conclusion
Not that crazy but still worth it and could demultiply in production
2026-01-18 16:22:26 +00:00
Lucas BordeauandGitHub d51c988a9e Implemented SSE update across main front components (#17205)
This PR implements SSE update events across the main components of the
application : tables, boards, calendars, show pages.

There is still work to do on other event type and on making sure
everything works fine, but this first implementation should be robust
enough to start with.

Some problems encountered along the way : 
- Events are returning raw Postgres output, because they are not
normalized by the GraphQL layer, so we ended up with amountMicros as
string values, which the frontend does not like, so I implemented a
small util in our `formatResult` generic pipeline to turn amountMicros
to a number if it's a string value. We could implement other formatters
for composite fields if we see problems with events.
-`action` property was missing in SSE events, which is required by the
frontend to know what kind of event it is.

# QA


https://github.com/user-attachments/assets/393b45ac-59d2-48b0-855b-2ce9c4b8ae57


https://github.com/user-attachments/assets/cb214e7a-1595-4b85-bdb6-87630993d2e2
2026-01-18 16:14:36 +00:00
Paul RastoinandGitHub a91bac9dd9 Identify view filter (#17197)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous standard ids

## Test
tested prod extract
2026-01-18 11:08:21 +00:00
Abdullah.andGitHub 92eff62523 Replace test-runner with vitest for storybook (#17187) 2026-01-18 03:25:45 +00:00
Félix MalfaitandGitHub c737028dd6 Move tools/eslint-rules to packages/twenty-eslint-rules (#17203)
## Summary

Moves the custom ESLint rules from `tools/eslint-rules` to
`packages/twenty-eslint-rules` for better organization within the
monorepo packages structure.

## Changes

- Move `eslint-rules` from `tools/` to `packages/twenty-eslint-rules`
- Use `loadWorkspaceRules` from `@nx/eslint-plugin` to load custom rules
- Update all ESLint configs to use the `twenty/` rule prefix instead of
`@nx/workspace-`
- Update `project.json`, `jest.config.mjs` with new paths
- Update `package.json` workspaces and `nx.json` cache inputs
- Update Dockerfile reference

## Technical Details

The custom ESLint rules are now loaded using Nx's `loadWorkspaceRules`
utility which:
- Handles TypeScript transpilation automatically
- Allows loading workspace rules from any directory
- Provides a cleaner approach than the previous `@nx/workspace-`
convention

## Testing

- Verified all 17 custom ESLint rules load correctly from the new
location
- Verified linting works on dependent packages (twenty-front,
twenty-server, etc.)
2026-01-17 07:37:17 +01:00
a429bc922d i18n - docs translations (#17204)
Created by Github action

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Improves translation accuracy and terminology across localized docs.
> 
> - AR `chart-settings.mdx`: corrected "Group by" image `alt` text
> - RO `chart-settings.mdx`: fixed diacritic in `"Sursă"` image `alt`
and table header `"Opțiune"`
> - TR `widgets.mdx`: corrected iFrame image `alt` text
> - TR `send-emails-from-workflows.mdx`: updated section title and table
header under attachments usage
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2a19997384. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2026-01-16 20:31:07 +00:00
1b9dc414c7 i18n - translations (#17206)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-16 20:20:38 +01:00
Abdul RahmanandGitHub 5a617e4718 Add command menu item entity (#17181) 2026-01-16 19:02:07 +00:00
b7fbe1c49e i18n - docs translations (#17199)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-16 18:36:39 +01:00
Baptiste DevessierandGitHub 5bcbe43596 Various layout fixes so that RPL v1 look the same as production version (#17195)
> [!WARNING]
> Most of the quick fixes will be dropped once we release the feature
and deprecate the old show pages code.

This PR fixes padding and alignment issues so that the new Record Page
Layout feature looks like the old show pages.

## This PR

<img width="3456" height="2160" alt="CleanShot 2026-01-16 at 17 24
40@2x"
src="https://github.com/user-attachments/assets/f3640553-2316-498c-8dd0-14b09ed913f1"
/>

<img width="3456" height="2160" alt="CleanShot 2026-01-16 at 17 27
14@2x"
src="https://github.com/user-attachments/assets/c4ccb28f-5629-4e68-83ad-863f21066962"
/>


## The production

<img width="3456" height="2160" alt="CleanShot 2026-01-16 at 17 25
11@2x"
src="https://github.com/user-attachments/assets/a91f7216-0b57-404b-9f9d-f6f45d4858d6"
/>

<img width="3456" height="2234" alt="CleanShot 2026-01-16 at 17 27
19@2x"
src="https://github.com/user-attachments/assets/aaf1ee6b-b323-4844-9f1a-a59807cdbe40"
/>
2026-01-16 17:06:34 +00:00
8a27c4987d i18n - translations (#17200)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-16 17:40:58 +01:00
Charles BochetandGitHub 5fc4e810f7 Front Extensibility: Introduce Front Component Entity (#17175)
As part of the extensibility effort, we are introducing a new engine
entity called "Front Component". This represents a dynamic react
component that will be rendered in CommandMenu actions or in PageLayout
widgets

This PR introduce the entity and all the necessary boilerplate to make
it syncable and cachable in the engine
2026-01-16 17:23:46 +01:00
fe0d84c97e i18n - translations (#17198)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-16 17:22:02 +01:00
EtienneandGitHub 76cbe59929 File v2 - update core.file table (#17172)
For the following, I'll create the issues first
2026-01-16 15:59:39 +00:00
Félix MalfaitandGitHub dcdf9e4d9c fix: add base_path to Crowdin configs for correct path resolution (#17196)
## Problem
The Crowdin GitHub Actions were failing with:
```
 No sources found for 'packages/twenty-docs/user-guide/**/*.mdx' pattern
 No sources found for 'packages/twenty-docs/developers/**/*.mdx' pattern
 No sources found for 'packages/twenty-docs/twenty-ui/**/*.mdx' pattern
 No sources found for 'packages/twenty-docs/navigation/navigation.template.json' pattern
```

## Root Cause
The Crowdin config files are located at `.github/crowdin-docs.yml` and
`.github/crowdin-app.yml`. By default, the Crowdin CLI resolves source
paths relative to the config file's directory (`.github/`), not the
repository root.

So paths like `packages/twenty-docs/...` were being resolved as
`.github/packages/twenty-docs/...`, which doesn't exist.

## Fix
Added `base_path: ".."` to both Crowdin config files to make paths
resolve relative to the repository root.
2026-01-16 16:54:40 +01:00
Félix MalfaitandGitHub 6a709d9c50 feat(serverless): add basic sandbox isolation and flexible driver options (#17176)
## Overview
- Add a DISABLED serverless driver to explicitly turn off execution
- Clarify self-hosting docs with driver options and recommended usage
- Keep integration coverage for serverless function execution (default +
external package example)

## Notes
- Local driver remains the default for development usage; Lambda or
Disabled recommended for production deployments
- No functional changes to Lambda execution

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces flexible serverless execution modes and safer local
execution.
> 
> - **New driver:** `DISABLED` serverless driver with wiring in
`serverless.interface`, factory, module provider, and GraphQL exception
mapping; new exception code `SERVERLESS_FUNCTION_DISABLED`.
> - **Local driver hardening:** Strip `NODE_OPTIONS` when spawning child
processes; cleanup promise signature; better log capture.
> - **Dependency build reliability:** Use `execFile` with bundled Yarn
(`.yarn/releases/yarn-4.9.2.cjs`), strip `NODE_OPTIONS`, improved error
messages, and parallel cleanup excluding `node_modules`.
> - **Docs:** Add serverless section detailing `SERVERLESS_TYPE` options
(LOCAL, LAMBDA, DISABLED), security notice, and recommended configs.
> - **Config/env:** Default
`IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS` set to `true`
(examples/tests default `false`); sample envs updated.
> - **Tests:** Add integration tests and GraphQL helpers for creating,
updating, publishing, executing, and deleting serverless functions,
including external package usage and error paths.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1a2958cc19. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2026-01-16 15:54:44 +01:00
nitinandGitHub 9620961f16 [Dashboards] [ai] expand widget schemas with subfield grouping, color, and styling options (#17185)
Got rid of filters for now -- filters would need extra tailoring since
they depend on field names/types and the RecordGqlOperationFilter DSL
(nested AND/OR and composite subfields), which the AI can’t reliably
construct without additional metadata and skills

created a followup to tackle the same
https://github.com/twentyhq/core-team-issues/issues/2108
2026-01-16 13:22:34 +00:00
Paul RastoinandGitHub 964f1c6227 Remove delete-field and delete-object aggregators (#17183)
# Introduction
~~Testing first this might break some constraints can't remember the
initial motivation~~ -> it does not

The goal here is to avoid relying on pg cascading which will lock the
schema longer than if done in n operations
2026-01-16 12:44:23 +00:00
Paul RastoinandGitHub 9750bcba81 Improve view field identification command perfs and reliability (#17168)
# Introduction
Related to https://github.com/twentyhq/core-team-issues/issues/1989
Same continuity than what we've done in
https://github.com/twentyhq/twenty/pull/17161 comparing expect to
existing instead of existing versus expected

Plus various fixes
Tried both view and view field identification on prod extract, ( clean
soft deleted workspace and applied the migration WIP )
2026-01-16 09:35:41 +00:00
martmullandGitHub 1917c1c9b9 Add forwardedRequestHeaders in routeTriggers (#17151)
- add `forwardedRequestHeaders` `string[]` column in `core.routeTrigger`
- filter request headers and forward filtered headers to function
payload (avoid spreading unexpectedly token or cookie)
- add `forwardedRequestHeaders` option in twenty-sdk `defineFunction`
util

BREAKING for actual routeTrigger payload but only 16 to migrate in
production
2026-01-16 09:19:28 +00:00
nitinandGitHub 8908e0785f [Dashboards] [ai] update dashboard tools schema and skill with correct config field names (#17180)
<img width="2560" height="1353" alt="CleanShot 2026-01-16 at 00 55 56"
src="https://github.com/user-attachments/assets/c242071c-13d3-494d-bcce-9d45f36fcd77"
/>
2026-01-15 20:55:47 +00:00
Thomas TrompetteandGitHub 0fa8098c50 Add get current workflow version tool (#17177)
This is required so an agent can easily understand what version should
be updated from the workflow show page.
2026-01-15 16:59:11 +00:00
WeikoandGitHub 8413c6f3dd Fix insert new record with RLS (#17164)
## Context
Now that RLS predicates are applied, creating a record through the FE
(which is empty by default) is failing if your role has predicates and
your input does not respect them (which will always be true since, as
said above, input will be pretty much empty)

## Implementation
- Moved isMatching* filters to twenty-shared
- Implemented isMatchingRlsPredicates utils in the backend (ORM) to
check before insertion/update if the record is matching the current user
role Rls predicates, reusing the isMatching* filters utils moved to
twenty-shared
- Frontend now applies RLS predicates before creating a new record
(similarly to what we do with view filters)

Note:
It seems composite were not properly handled with view-filter insertion
logic, since I'm reusing the util for now, the issue remains for RLS and
will need to be addressed
2026-01-15 16:40:47 +00:00
Lucas BordeauandGitHub 2cff75d772 Refactored object operation dispatch for SSE (#17174)
This PR refactors object record operation dispatch through a browser
event instead of a state that was registering all operations.

This is a cleaner pattern as it is a synchronous event code path instead
of relying on a useEffect to watch state change, which is not ideal.

We introduce this change first to then rely on this new pattern to
dispatch SSE events in a following PR.
2026-01-15 16:38:21 +00:00
nitinandGitHub 3f28fde03a [Dashboards] fix widget type switching when navigating back in command menu (#17166)
before - 



https://github.com/user-attachments/assets/ab1e1719-f636-4d49-8c3f-cbc6b5e1f61f





after - 


https://github.com/user-attachments/assets/4bebe9c5-d8eb-49ca-9265-70e571408465
2026-01-15 13:47:02 +00:00
161e8670d0 fix: show save button when creating a new role (#17163)
## Summary
Fixes a regression where the save button was not visible when creating a
new role.

## Root Cause
PR #17062 (RLS FE implementation) introduced a change to the `isDirty`
logic that added `isDefined(settingsPersistedRole)` as a condition:

```typescript
const isDirty =
  isDefined(settingsPersistedRole) &&
  !isDeeplyEqual(settingsDraftRole, settingsPersistedRole);
```

However, in create mode, `settingsPersistedRole` is intentionally set to
`undefined` (in `SettingsRoleCreateEffect.tsx`), causing `isDirty` to
always evaluate to `false` and hiding the save button.

## Fix
Added `isCreateMode` to the `isDirty` condition so the save button shows
when creating a new role:

```typescript
const isDirty =
  isCreateMode ||
  (isDefined(settingsPersistedRole) &&
    !isDeeplyEqual(settingsDraftRole, settingsPersistedRole));
```

cc @Weiko

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Restores save button visibility when creating a role.
> 
> - Simplifies `isDirty` to `!isDeeplyEqual(settingsDraftRole,
settingsPersistedRole)`, removing the `isDefined(settingsPersistedRole)`
check so create-mode is considered dirty.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
21593d54c3. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Weiko <corentin@twenty.com>
2026-01-15 13:42:23 +00:00
nitinandGitHub abdccf2940 [Dashboards] restrict dashboard actions based on object permissions (#17169) 2026-01-15 13:37:08 +00:00
f218d652df [Dashboard] Add chart settings/config documentation (#17053)
closes https://github.com/twentyhq/core-team-issues/issues/1973

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-15 13:05:39 +00:00
Paul RastoinandGitHub 466d272f9c Create syncable entity cursor rule (#17165)
# Introduction

As per title
We could improve the integration tests section ( maybe a dedicated tool
)
2026-01-15 13:05:30 +00:00
nitinandGitHub 975401e18f fix widget skeleton loader not being centered (#17157) 2026-01-15 13:04:12 +00:00
nitinandGitHub 55b4ac4314 [Dashboards] [Performance] Bar chart re renders fix (#17162)
before - 


https://github.com/user-attachments/assets/c9b4a60a-d4bd-43d5-8621-54777a326171


after - 




https://github.com/user-attachments/assets/74358810-067e-44f7-82f1-f4dcd7956956
2026-01-15 12:40:28 +00:00
Paul RastoinandGitHub d8f0115fef Improve view identification command perfs and reliability (#17161)
# Introduction
Instead of comparing existing to expected comparing expected to existing
Also improved logs and fallback use cases

# Test
Tested on a prod extract
2026-01-15 12:27:07 +00:00
martmullandGitHub f894f6b1c4 Fix universalIdentifier ignored when creating object (#17158)
As title
2026-01-15 08:41:44 +00:00
Thomas TrompetteandGitHub 6e53f5ab92 Throw 400 for webhook triggering deleted workspaces + enqueue workflows by batches (#17154)
Fixes https://github.com/twentyhq/twenty/issues/17027

Fixes https://github.com/twentyhq/twenty/issues/16824
2026-01-14 17:35:05 +00:00
MarieandGitHub 8379e19587 [Apps] Small improvements (#17129)
- Fix undefined in breadcrumbs
<img width="783" height="220" alt="breadcrumbs_bad"
src="https://github.com/user-attachments/assets/557648da-f825-4bf6-b66f-2ac3992eeab1"
/>
- Make role required in app definition
2026-01-14 17:28:51 +00:00
Thomas TrompetteandGitHub 79e0207efa Filter valid fields in record steps (#17145)
Fixes https://github.com/twentyhq/twenty/issues/16775

When a field is deleted from the model, the workflow action step still
stores the deleted field name in `step.settings.input.objectRecord`.

We need to filter out the fields that are not valid anymore. This logic
existed before but had been removed with the migration to tool services.
2026-01-14 14:43:35 +00:00
Thomas TrompetteandGitHub 93aef30046 Add feature flag for sse (#17152)
As title
2026-01-14 14:20:45 +00:00
nitinandGitHub a9e07d4f97 [Dashboards] disable chart drill-down and pointer cursor for relation group-by fields (#17150) 2026-01-14 14:15:33 +00:00
Paul RastoinandGitHub 9e4247c934 Clean soft deleted suspended workspace command and ignore-grace-period flag (#17144)
# Introduction

In the https://github.com/twentyhq/core-team-issues/issues/1989 's
context we will apply migration through an upgrade command post entity
backfill to fit the applied constraint. But suspended soft deleted
workspace are not included in the upgrade workspace batches

In order to be able to pass the constraint in production, discussed with
@FelixMalfait, we will manually clear all the currently suspended and
soft deleted workspace ignore their grace period

## Force mode
When running the command in force it will ignore the limit per execution
( which really serve the cron job ) and the grace period

## Test
Tested on a production extract
2026-01-14 14:15:15 +00:00
Paul RastoinandGitHub 2c24180b44 Identify standard view fields and views (#17118)
# Introduction
Related https://github.com/twentyhq/core-team-issues/issues/1989

1/ Migration, applicationId and universalIdentifier are required on
entity ( save point migration + upgrade command fallback pattern )
2/ Backfill using previous `standardId` or `isCustom`

## Test
Both tested on prod extract
Some view field are set as non custom is prod whereas they should for
several manually handle-able workspace amount
2026-01-14 14:15:07 +00:00
StephanieJoly4andGitHub f70b171843 Updating the user guide to reflect available features (#17139)
updated text regarding email attachments in workflows
2026-01-14 13:49:41 +00:00
Lucas BordeauandGitHub 6b14cfaa89 Fixes two critical bugs for DATE type (#17143)
This PR fixes two critical bugs with the DATE type, following up the
recent refactor around dates and time zones :
https://github.com/twentyhq/twenty/pull/16544

There are a lot of related bugs in Sentry, this PR should fix all bugs
that are of the form :

`Cannot parse: 2026-02-04T00:00:00.000Z`

# `node-postgres` date type

The package `node-postgres`, used by TypeORM, returns by default a Date
object for the date OID type.

But we can change this behavior without patching anything.

See the documentation for `pg-types` :
https://github.com/brianc/node-pg-types

Our fix is to call `setTypeParser` from `pg` to have the postgres "date"
type returned as a string always.

```ts
export const setPgDateTypeParser = () => {
  types.setTypeParser(PG_DATE_TYPE_OID, (val: string) => val);
};
```

Then in server's main.ts : 

```ts
const bootstrap = async () => {
  setPgDateTypeParser();
```

## Are we safe with this very low-level fix ?

Since TypeORM bypasses a string value returned by `pg` we are safe with
this modification at a very low-level.

```ts
else if (columnMetadata.type === "date") {
  value = DateUtils.mixedDateToDateString(value)
}
```


https://github.com/typeorm/typeorm/blob/0ec4079cd3760dc49247a02c54415f16a2a51766/src/driver/postgres/PostgresDriver.ts#L838

```ts
/**
* Converts given value into date string in a "YYYY-MM-DD" format.
*/
static mixedDateToDateString(value: string | Date): string {
  if (value instanceof Date) {
      return (
          this.formatZerolessValue(value.getFullYear(), 4) +
          "-" +
          this.formatZerolessValue(value.getMonth() + 1) +
          "-" +
          this.formatZerolessValue(value.getDate())
      )
  }

  return value
}
```


https://github.com/typeorm/typeorm/blob/6f3788b83730463e3b787b2a98bb41695e13caf8/src/util/DateUtils.ts#L28

For reference the original type parser seems to be configured in
node-pg-types, on the 1182 OID, which is an array of date / 1082 OID,
this type parser calls another small library `postgres-date` which
creates a JS Date. I couldn't find a type parser explicitly on 1082 in
the stack TypeORM -> node-postgres -> node-pg-types -> postgres-date

```ts
register(1182, parseStringArray) // date[]
``` 


https://github.com/brianc/node-pg-types/blob/26bfe645a8ddc0c73830b1b8c63f2c4f8265b24f/lib/textParsers.js#L168C19-L168C45

```ts
 static parse (dateString) {
  return new PGDateParser(dateString).getJSDate()
}
```


https://github.com/bendrucker/postgres-date/blob/b3060560ed62c250f7800d29e4b68a9d5a0622bd/index.js#L285


# Calendar view mixing DATE and DATE_TIME

At the time of the refactor, DATE type wasn't properly tested with
calendar view, thus the drag and drop of a card with a calendar view on
a DATE field and not a DATE_TIME field, was not working, this is now
fixed.
2026-01-14 13:38:24 +00:00
neo773andGitHub 0412ce2cda IMAP Error handling enhancement and fix revoked authentication edge case (#17133)
## Changes

- Removed complex retry logic in `ImapClientProvider` by delegating to
root orchestrator
- Added `parseImapAuthenticationError` for handling revoked
authentication credentials previously the code existed in
`parse-imap-error.util` but it didn't belong there and nor was not used
in `ImapClientProvider` causing revoked channels to be stuck in limbo
- Removed `parseImapError` from `*-error-handler.service.ts `
- Created `isImapNetworkError`  for consistency with Gmail and Microsoft
- `isImapNetworkError` is called at utility level for consistency
2026-01-14 13:00:43 +00:00
Thomas TrompetteandGitHub ef8d7c18c1 Set back amount micros (#17124)
The amount to amount micros in UI breaks variable. Setting back amount
micros in UI with a hint

<img width="399" height="178" alt="Capture d’écran 2026-01-13 à 15 54
04"
src="https://github.com/user-attachments/assets/52f53a78-05a8-421d-80fe-f5cd0e8aad19"
/>
2026-01-14 14:18:28 +01:00
Félix MalfaitandGitHub 245bd510ae chore: cleanup repository root structure (#17147)
## Summary

This PR reduces clutter at the repository root to improve navigation on
GitHub. The README is now visible much sooner when browsing the repo.

## Changes

### Deleted from root
- `nx` wrapper script → use `npx nx` instead
- `render.yaml` → no longer used
- `jest.preset.js` → inlined `@nx/jest/preset` directly in each
package's jest.config
- `.prettierrc` → moved config to `package.json`
- `.prettierignore` → patterns already covered by `.gitignore`

### Moved/Consolidated
| From | To |
|------|-----|
| `Makefile` | `packages/twenty-docker/Makefile` (merged) |
| `crowdin-app.yml` | `.github/crowdin-app.yml` |
| `crowdin-docs.yml` | `.github/crowdin-docs.yml` |
| `.vale.ini` | `.github/vale.ini` |
| `tools/eslint-rules/` | `packages/twenty-eslint-rules/` |
| `eslint.config.react.mjs` |
`packages/twenty-front/eslint.config.react.mjs` |

## Result

Root items reduced from ~32 to ~22 (folders + files).

## Files updated

- GitHub workflow files updated to reference new crowdin config paths
- Jest configs updated to use `@nx/jest/preset` directly
- ESLint configs updated with new import paths
- `nx.json` updated with new paths
- `package.json` now includes prettier config and updated workspace
paths
- Dockerfile updated with new eslint-rules path
2026-01-14 12:56:30 +00:00
Abdullah.andGitHub 6f18eaa3f1 fix: AWS SDK for JavaScript v3 adopted defense in depth enhancement for region parameter value (#17148)
Resolves [Dependabot Alert
362](https://github.com/twentyhq/twenty/security/dependabot/362).

AWS SDK minor version upgrades are backward compatible, so it's safe to
upgrade to the newer versions.
2026-01-14 12:35:50 +00:00
Thomas TrompetteandGitHub ec87b29286 Move query matching before publication (#17121)
- new channel EVENT_STREAM_CHANNEL based on event stream id
- on event, perform the matching and publish only to the right streams
- store a list of active streams per workspace
- store the user id along with the queries for each stream

Bonus:
- remove onSubscriptionMatch
2026-01-14 12:29:12 +00:00
3ecfb24939 i18n - docs translations (#17149)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-14 13:46:53 +01:00
Paul RastoinandGitHub 70704264ac Fix dev seed page layout cache invalidation (#17141)
# Introduction

followup https://github.com/twentyhq/twenty/pull/16962

Root cause: as now the twenty standard app installation in seeded
workspace invalidates and set the page layout-xxx caches the inserted
through direct repo access data in the prefill legacy page layout
methods do not interact with the cache
The cache being set in prior when recompute does not result in db
introspection as before as when was unset
2026-01-14 12:08:51 +00:00
Félix MalfaitandGitHub d95ff4e252 fix: e2e login test - handle optional Continue with Email button (#17146)
## Summary
The e2e login test was failing because it unconditionally tried to click
'Continue with Email' button, but this button doesn't exist when
password is the only auth method.

## Root Cause
In `SignInUpWorkspaceScopeFormEffect.tsx`, when a workspace only has
password authentication (no Google/Microsoft/SSO), the effect
automatically calls `continueWithEmail()` which skips the Init step and
shows the email field directly.

## Changes
1. **loginPage.ts**: Added `clickLoginWithEmailIfVisible()` method that
only clicks the button if it exists
2. **login.setup.ts**: 
- Replaced `clickLoginWithEmail()` with `clickLoginWithEmailIfVisible()`
- Updated regex from `/Welcome to .+/` to `/Welcome, .+/` to match the
recent UI change
2026-01-14 11:57:15 +00:00
20b5a8f5e7 i18n - docs translations (#17142)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-14 13:01:48 +01:00
1976 changed files with 72467 additions and 29505 deletions
+3 -1
View File
@@ -13,6 +13,7 @@ This directory contains Twenty's development guidelines and best practices in th
- **architecture.mdc** - Project overview, technology stack, and infrastructure setup (Always Applied)
- **nx-rules.mdc** - Nx workspace guidelines and best practices (Auto-attached to Nx files)
- **server-migrations.mdc** - Backend migration and TypeORM guidelines for `twenty-server` (Auto-attached to server entities and migration files)
- **creating-syncable-entity.mdc** - Comprehensive guide for creating new syncable entities (with universalIdentifier and applicationId) in the workspace migration system (Agent-requested for metadata-modules and workspace-migration files)
### Code Quality
- **typescript-guidelines.mdc** - TypeScript best practices and conventions (Auto-attached to .ts/.tsx files)
@@ -39,6 +40,7 @@ You can manually reference any rule using the `@ruleName` syntax:
- `@nx-rules` - Include Nx-specific guidance
- `@react-general-guidelines` - Load React best practices
- `@testing-guidelines` - Get testing recommendations
- `@creating-syncable-entity` - Guide for creating new syncable entities
### Rule Types Used
- **Always Applied** - Loaded in every context (architecture.mdc, README.mdc)
@@ -53,7 +55,7 @@ You can manually reference any rule using the `@ruleName` syntax:
# Testing
npx nx test twenty-front # Run unit tests
npx nx storybook:build twenty-front # Build Storybook
npx nx storybook:serve-and-test:static # Run Storybook tests
npx nx storybook:test # Run Storybook tests
# Development
npx nx lint:diff-with-main twenty-front # Lint files changed vs main (fastest)
File diff suppressed because it is too large Load Diff
@@ -5,6 +5,7 @@
#
"preserve_hierarchy": true
"base_path": ".."
files: [
{
@@ -6,6 +6,7 @@
"project_id": 2
"preserve_hierarchy": true
"base_url": "https://twenty.api.crowdin.com"
"base_path": ".."
files: [
{
View File
+16 -9
View File
@@ -56,8 +56,6 @@ jobs:
run: df -h
- name: Front / Write .env
run: npx nx reset:env twenty-front
- name: Front / Clean storybook-static
run: rm -rf packages/twenty-front/storybook-static
- name: Front / Build storybook
run: npx nx storybook:build twenty-front
- name: Save storybook build cache
@@ -83,18 +81,27 @@ jobs:
fetch-depth: 0
- name: Install dependencies
uses: ./.github/actions/yarn-install
- name: Build dependencies
run: |
npx nx build twenty-shared
npx nx build twenty-ui
- name: Install Playwright
run: cd packages/twenty-front && npx playwright install
- name: Restore storybook build cache
uses: ./.github/actions/restore-cache
with:
key: ${{ env.STORYBOOK_BUILD_CACHE_KEY_FOR_RESTORE_ACTION }}
run: |
cd packages/twenty-front
npx playwright install
- name: Front / Write .env
run: npx nx reset:env twenty-front
- name: Run storybook tests
run: npx nx storybook:serve-and-test:static twenty-front --configuration=${{ matrix.storybook_scope }} --shard=${{ matrix.shard }}/${{ env.SHARD_COUNTER }} --checkCoverage=false
run: npx nx storybook:test twenty-front --configuration=${{ matrix.storybook_scope }} --shard=${{ matrix.shard }}/${{ env.SHARD_COUNTER }}
- name: Rename coverage file
run: mv packages/twenty-front/coverage/storybook/coverage-storybook.json packages/twenty-front/coverage/storybook/coverage-shard-${{matrix.shard}}.json
run: |
if [ -f "packages/twenty-front/coverage/storybook/coverage-final.json" ]; then
mv packages/twenty-front/coverage/storybook/coverage-final.json packages/twenty-front/coverage/storybook/coverage-shard-${{matrix.shard}}.json
else
echo "Error: coverage-final.json not found"
ls -la packages/twenty-front/coverage/storybook/ || echo "Coverage directory does not exist"
exit 1
fi
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
+2 -2
View File
@@ -24,7 +24,7 @@ on:
pull_request:
paths:
- 'packages/twenty-docs/**'
- 'crowdin-docs.yml'
- '.github/crowdin-docs.yml'
- '.github/workflows/docs-i18n-pull.yaml'
concurrency:
@@ -78,7 +78,7 @@ jobs:
for lang in $LANGUAGES; do
echo "=== Pulling translations for $lang ==="
crowdin download \
--config crowdin-docs.yml \
--config .github/crowdin-docs.yml \
--token "$CROWDIN_PERSONAL_TOKEN" \
--base-url "https://twenty.api.crowdin.com" \
--language "$lang" \
+2 -2
View File
@@ -12,7 +12,7 @@ on:
- 'packages/twenty-docs/**/*.mdx'
- '!packages/twenty-docs/l/**'
- 'packages/twenty-docs/navigation/navigation.template.json'
- 'crowdin-docs.yml'
- '.github/crowdin-docs.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -43,7 +43,7 @@ jobs:
download_translations: false
localization_branch_name: i18n-docs
base_url: 'https://twenty.api.crowdin.com'
config: 'crowdin-docs.yml'
config: '.github/crowdin-docs.yml'
env:
# Docs translations project
CROWDIN_PROJECT_ID: '2'
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
create_pull_request: false
skip_ref_checkout: true
dryrun_action: false
config: 'crowdin-app.yml'
config: '.github/crowdin-app.yml'
env:
GITHUB_TOKEN: ${{ github.token }}
# App translations project
+1 -1
View File
@@ -87,7 +87,7 @@ jobs:
download_translations: false
localization_branch_name: i18n
base_url: 'https://twenty.api.crowdin.com'
config: 'crowdin-app.yml'
config: '.github/crowdin-app.yml'
env:
# App translations project
CROWDIN_PROJECT_ID: '1'
+3 -5
View File
@@ -2,11 +2,9 @@
"mcpServers": {
"postgres": {
"type": "stdio",
"command": "uv",
"args": ["run", "postgres-mcp", "--access-mode=unrestricted"],
"env": {
"DATABASE_URI": "${PG_DATABASE_URL}"
}
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${PG_DATABASE_URL}"],
"env": {}
},
"playwright": {
"type": "stdio",
-4
View File
@@ -1,4 +0,0 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage
/.nx/cache
-5
View File
@@ -1,5 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "lf"
}
+2 -1
View File
@@ -73,7 +73,8 @@
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"env": {
"NODE_ENV": "test"
"NODE_ENV": "test",
"NODE_OPTIONS": "--max-old-space-size=12288 --import tsx/esm"
}
},
{
+1 -1
View File
@@ -28,7 +28,7 @@ npx nx run twenty-server:test:integration:with-db-reset # Integration tests wit
# Storybook
npx nx storybook:build twenty-front # Build Storybook
npx nx storybook:serve-and-test:static twenty-front # Run Storybook tests
npx nx storybook:test twenty-front # Run Storybook tests
When testing the UI end to end, click on "Continue with Email" and use the prefilled credentials.
-48
View File
@@ -1,48 +0,0 @@
DOCKER_NETWORK=twenty_network
ensure-docker-network:
docker network inspect $(DOCKER_NETWORK) >/dev/null 2>&1 || docker network create $(DOCKER_NETWORK)
postgres-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) \
--name twenty_pg \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e ALLOW_NOSSL=true \
-v twenty_db_data:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16
@echo "Waiting for PostgreSQL to be ready..."
@until docker exec twenty_pg psql -U postgres -d postgres \
-c 'SELECT pg_is_in_recovery();' 2>/dev/null | grep -q 'f'; do \
sleep 1; \
done
docker exec twenty_pg psql -U postgres -d postgres \
-c "CREATE DATABASE \"default\" WITH OWNER postgres;" \
-c "CREATE DATABASE \"test\" WITH OWNER postgres;"
redis-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
clickhouse-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) --name twenty_clickhouse -p 8123:8123 -p 9000:9000 -e CLICKHOUSE_PASSWORD=devPassword clickhouse/clickhouse-server:latest \
grafana-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) \
--name twenty_grafana \
-p 4000:3000 \
-e GF_SECURITY_ADMIN_USER=admin \
-e GF_SECURITY_ADMIN_PASSWORD=admin \
-e GF_INSTALL_PLUGINS=grafana-clickhouse-datasource \
-v $(PWD)/packages/twenty-docker/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources \
grafana/grafana-oss:latest
opentelemetry-collector-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) \
--name twenty_otlp_collector \
-p 4317:4317 \
-p 4318:4318 \
-p 13133:13133 \
-v $(PWD)/packages/twenty-docker/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector-contrib:latest \
--config /etc/otel-collector-config.yaml
+5 -2
View File
@@ -11,6 +11,8 @@ import unicornPlugin from 'eslint-plugin-unicorn';
import unusedImportsPlugin from 'eslint-plugin-unused-imports';
import jsoncParser from 'jsonc-eslint-parser';
const twentyRules = await nxPlugin.loadWorkspaceRules('packages/twenty-eslint-rules');
export default [
// Base JavaScript configuration
js.configs.recommended,
@@ -195,6 +197,7 @@ export default [
plugins: {
...mdxPlugin.flat.plugins,
'@nx': nxPlugin,
'twenty': { rules: twentyRules },
},
},
mdxPlugin.flatCodeBlocks,
@@ -205,9 +208,9 @@ export default [
'unused-imports/no-unused-imports': 'off',
'unused-imports/no-unused-vars': 'off',
// Enforce JSX tags on separate lines to prevent Crowdin translation issues
'@nx/workspace-mdx-component-newlines': 'error',
'twenty/mdx-component-newlines': 'error',
// Disallow angle bracket placeholders to prevent Crowdin translation errors
'@nx/workspace-no-angle-bracket-placeholders': 'error',
'twenty/no-angle-bracket-placeholders': 'error',
},
},
];
-5
View File
@@ -1,5 +0,0 @@
import { getJestProjects } from '@nx/jest';
export default {
projects: getJestProjects(),
};
+7 -1
View File
@@ -1,3 +1,9 @@
const nxPreset = require('@nx/jest/preset').default;
module.exports = { ...nxPreset };
module.exports = {
...nxPreset,
// Override the new testEnvironmentOptions added in @nx/jest 22.3.3
// which breaks Lingui's module resolution
testEnvironmentOptions: {},
};
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
command -v node >/dev/null 2>&1 || { echo >&2 "Nx requires NodeJS to be available. To install NodeJS and NPM, see: https://nodejs.org/en/download/ ."; exit 1; }
command -v npm >/dev/null 2>&1 || { echo >&2 "Nx requires npm to be available. To install NodeJS and NPM, see: https://nodejs.org/en/download/ ."; exit 1; }
path_to_root=$(dirname $BASH_SOURCE)
node $path_to_root/.nx/nxw.js $@
+36 -113
View File
@@ -4,9 +4,7 @@
"libsDir": "packages"
},
"namedInputs": {
"default": [
"{projectRoot}/**/*"
],
"default": ["{projectRoot}/**/*"],
"excludeStories": [
"default",
"!{projectRoot}/.storybook/*",
@@ -34,26 +32,17 @@
"targetDefaults": {
"build": {
"cache": true,
"inputs": [
"^production",
"production"
],
"dependsOn": [
"^build"
]
"inputs": ["^production", "production"],
"dependsOn": ["^build"]
},
"start": {
"cache": false,
"dependsOn": [
"^build"
]
"dependsOn": ["^build"]
},
"lint": {
"executor": "@nx/eslint:lint",
"cache": true,
"outputs": [
"{options.outputFile}"
],
"outputs": ["{options.outputFile}"],
"options": {
"eslintConfig": "{projectRoot}/eslint.config.mjs",
"cache": true,
@@ -67,9 +56,7 @@
"fix": true
}
},
"dependsOn": [
"^build"
]
"dependsOn": ["^build"]
},
"lint:diff-with-main": {
"executor": "nx:run-commands",
@@ -103,46 +90,36 @@
"write": true
}
},
"dependsOn": [
"^build"
]
"dependsOn": ["^build"]
},
"typecheck": {
"executor": "nx:run-commands",
"cache": true,
"options": {
"cwd": "{projectRoot}",
"command": "tsc -b tsconfig.json --incremental"
"command": "tsgo -p tsconfig.json"
},
"configurations": {
"watch": {
"watch": true
"command": "tsgo -p tsconfig.json --watch"
}
},
"dependsOn": [
"^build"
]
"dependsOn": ["^build"]
},
"test": {
"executor": "@nx/jest:jest",
"cache": true,
"dependsOn": [
"^build"
],
"dependsOn": ["^build"],
"inputs": [
"^default",
"excludeStories",
"{workspaceRoot}/jest.preset.js"
],
"outputs": [
"{projectRoot}/coverage"
],
"outputs": ["{projectRoot}/coverage"],
"options": {
"jestConfig": "{projectRoot}/jest.config.mjs",
"coverage": true,
"coverageReporters": [
"text-summary"
],
"coverageReporters": ["text-summary"],
"cacheDirectory": "../../.cache/jest/{projectRoot}"
},
"configurations": {
@@ -151,10 +128,7 @@
"maxWorkers": 3
},
"coverage": {
"coverageReporters": [
"lcov",
"text"
]
"coverageReporters": ["lcov", "text"]
},
"watch": {
"watch": true
@@ -163,36 +137,25 @@
},
"test:e2e": {
"cache": true,
"dependsOn": [
"^build"
]
"dependsOn": ["^build"]
},
"storybook:build": {
"executor": "nx:run-commands",
"cache": true,
"inputs": [
"^default",
"excludeTests"
],
"outputs": [
"{projectRoot}/{options.output-dir}"
],
"inputs": ["^default", "excludeTests"],
"outputs": ["{projectRoot}/{options.output-dir}"],
"options": {
"cwd": "{projectRoot}",
"command": "NODE_OPTIONS='--max-old-space-size=10240' VITE_DISABLE_TYPESCRIPT_CHECKER=true storybook build --test",
"output-dir": "storybook-static",
"config-dir": ".storybook"
},
"dependsOn": [
"^build"
]
"dependsOn": ["^build"]
},
"storybook:serve:dev": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": [
"^build"
],
"dependsOn": ["^build"],
"options": {
"cwd": "{projectRoot}",
"command": "storybook dev",
@@ -201,9 +164,7 @@
},
"storybook:serve:static": {
"executor": "nx:run-commands",
"dependsOn": [
"storybook:build"
],
"dependsOn": ["storybook:build"],
"options": {
"cwd": "{projectRoot}",
"command": "npx http-server {args.staticDir} -a={args.host} --port={args.port} --silent={args.silent}",
@@ -216,38 +177,21 @@
"storybook:test": {
"executor": "nx:run-commands",
"cache": true,
"inputs": [
"^default",
"excludeTests"
],
"outputs": [
"{projectRoot}/coverage/storybook"
],
"inputs": ["^default", "excludeTests"],
"outputs": ["{projectRoot}/coverage/storybook"],
"options": {
"cwd": "{projectRoot}",
"commands": [
"test-storybook --url http://localhost:{args.port} --maxWorkers=3 --coverage --coverageDirectory={args.coverageDir} --shard={args.shard}",
"nx storybook:coverage {projectName} --coverageDir={args.coverageDir} --checkCoverage={args.checkCoverage}"
],
"shard": "1/1",
"parallel": false,
"coverageDir": "coverage/storybook",
"port": 6006,
"checkCoverage": true
"command": "vitest run --coverage --shard={args.shard}",
"shard": "1/1"
}
},
"storybook:test:no-coverage": {
"executor": "nx:run-commands",
"inputs": [
"^default",
"excludeTests"
],
"inputs": ["^default", "excludeTests"],
"options": {
"cwd": "{projectRoot}",
"commands": [
"test-storybook --url http://localhost:{args.port} --maxWorkers=2"
],
"port": 6006
"command": "vitest run --shard={args.shard}",
"shard": "1/1"
}
},
"storybook:coverage": {
@@ -274,17 +218,6 @@
}
}
},
"storybook:serve-and-test:static": {
"executor": "nx:run-commands",
"options": {
"commands": [
"npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --shard={args.shard} --checkCoverage={args.checkCoverage} --port={args.port} --configuration={args.scope}'"
],
"shard": "1/1",
"checkCoverage": true,
"port": 6006
}
},
"chromatic": {
"executor": "nx:run-commands",
"options": {
@@ -326,29 +259,21 @@
"inputs": [
"default",
"{workspaceRoot}/eslint.config.mjs",
"{workspaceRoot}/tools/eslint-rules/**/*"
]
},
"@nx/vite:test": {
"cache": true,
"inputs": [
"default",
"^default"
"{workspaceRoot}/packages/twenty-eslint-rules/**/*"
]
},
"@nx/vite:build": {
"cache": true,
"dependsOn": [
"^build"
],
"inputs": [
"default",
"^default"
]
"dependsOn": ["^build"],
"inputs": ["default", "^default"]
},
"@nx/vitest:test": {
"cache": true,
"inputs": ["default", "^default"]
}
},
"installation": {
"version": "22.0.3"
"version": "22.3.3"
},
"generators": {
"@nx/react": {
@@ -376,9 +301,7 @@
"tasksRunnerOptions": {
"default": {
"options": {
"cacheableOperations": [
"storybook:build"
]
"cacheableOperations": ["storybook:build"]
}
}
},
+27 -14
View File
@@ -72,19 +72,20 @@
"@graphql-codegen/typescript": "^3.0.4",
"@graphql-codegen/typescript-operations": "^3.0.4",
"@graphql-codegen/typescript-react-apollo": "^3.3.7",
"@nx/eslint": "22.0.3",
"@nx/eslint-plugin": "22.0.3",
"@nx/jest": "22.0.3",
"@nx/js": "22.0.3",
"@nx/react": "22.0.3",
"@nx/storybook": "22.0.3",
"@nx/vite": "22.0.3",
"@nx/web": "22.0.3",
"@nx/eslint": "22.3.3",
"@nx/eslint-plugin": "22.3.3",
"@nx/jest": "22.3.3",
"@nx/js": "22.3.3",
"@nx/react": "22.3.3",
"@nx/storybook": "22.3.3",
"@nx/vite": "22.3.3",
"@nx/web": "22.3.3",
"@sentry/types": "^8",
"@storybook-community/storybook-addon-cookie": "^5.0.0",
"@storybook/addon-coverage": "^3.0.0",
"@storybook/addon-docs": "^10.1.11",
"@storybook/addon-links": "^10.1.11",
"@storybook/addon-vitest": "^10.1.11",
"@storybook/icons": "^2.0.1",
"@storybook/react-vite": "^10.1.11",
"@storybook/test-runner": "^0.24.2",
@@ -134,7 +135,11 @@
"@typescript-eslint/eslint-plugin": "^8.39.0",
"@typescript-eslint/parser": "^8.39.0",
"@typescript-eslint/utils": "^8.39.0",
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
"@vitejs/plugin-react-swc": "3.11.0",
"@vitest/browser-playwright": "^4.0.17",
"@vitest/coverage-istanbul": "^4.0.17",
"@vitest/coverage-v8": "^4.0.17",
"@yarnpkg/types": "^4.0.0",
"chromatic": "^6.18.0",
"concurrently": "^8.2.2",
@@ -163,9 +168,9 @@
"jest-environment-node": "^29.4.1",
"jest-fetch-mock": "^3.0.3",
"jsdom": "~22.1.0",
"msw": "^2.0.11",
"msw-storybook-addon": "^2.0.5",
"nx": "22.0.3",
"msw": "^2.12.7",
"msw-storybook-addon": "^2.0.6",
"nx": "22.3.3",
"prettier": "^3.1.1",
"raw-loader": "^4.0.2",
"rimraf": "^5.0.5",
@@ -179,7 +184,8 @@
"ts-node": "10.9.1",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.17.0",
"vite": "^7.0.0"
"vite": "^7.0.0",
"vitest": "^4.0.17"
},
"engines": {
"node": "^24.5.0",
@@ -195,7 +201,9 @@
"typescript": "5.9.2",
"graphql-redis-subscriptions/ioredis": "^5.6.0",
"prosemirror-view": "1.40.0",
"prosemirror-transform": "1.10.4"
"prosemirror-transform": "1.10.4",
"@lingui/core": "5.1.2",
"@types/qs": "6.9.16"
},
"version": "0.2.1",
"nx": {},
@@ -220,7 +228,12 @@
"packages/twenty-apps",
"packages/twenty-cli",
"packages/create-twenty-app",
"tools/eslint-rules"
"packages/twenty-eslint-rules"
]
},
"prettier": {
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "lf"
}
}
+15 -15
View File
@@ -28,29 +28,29 @@ Create Twenty App is the official scaffolding CLI for building apps on top of [T
npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
# Get Help
yarn run help
# Authenticate using your API key (you'll be prompted)
yarn auth
yarn auth:login
# Add a new entity to your application (guided)
yarn create-entity
yarn entity:add
# Generate a typed Twenty client and workspace entity types
yarn generate
yarn app:generate
# Start dev mode: automatically syncs local changes to your workspace
yarn dev
yarn app:dev
# Or run a onetime sync
yarn sync
yarn app:sync
# Watch your application's functions logs
yarn logs
yarn function:logs
# Uninstall the application from the current workspace
yarn uninstall
# Display commands' help
yarn help
yarn app:uninstall
```
## What gets scaffolded
@@ -60,9 +60,9 @@ yarn help
- Example placeholders to help you add entities, actions, and sync logic
## Next steps
- Explore the generated project and add your first entity with `yarn create-entity`.
- Keep your types uptodate using `yarn generate`.
- Use `yarn dev` while you iterate to see changes instantly in your workspace.
- Explore the generated project and add your first entity with `yarn entity:add`.
- Keep your types uptodate using `yarn app:generate`.
- Use `yarn app:dev` while you iterate to see changes instantly in your workspace.
## Publish your application
@@ -90,8 +90,8 @@ git push
Our team reviews contributions for quality, security, and reusability before merging.
## Troubleshooting
- Auth prompts not appearing: run `yarn auth` again and verify the API key permissions.
- Types not generated: ensure `yarn generate` runs without errors, then restart `yarn dev`.
- Auth prompts not appearing: run `yarn auth:login` again and verify the API key permissions.
- Types not generated: ensure `yarn app:generate` runs without errors, then restart `yarn app:dev`.
## Contributing
- See our [GitHub](https://github.com/twentyhq/twenty)
@@ -5,17 +5,41 @@ This is a [Twenty](https://twenty.com) application project bootstrapped with [`c
First, authenticate to your workspace:
```bash
yarn auth
yarn auth:login
```
Then, install this app to your workspace:
Then, start development mode to sync your app and watch for changes:
```bash
yarn sync
yarn app:dev
```
Or run a one-time sync:
```bash
yarn app:sync
```
Open your Twenty instance and go to `/settings/applications` section to see the result.
## Available Commands
```bash
# Authentication
yarn auth:login # Authenticate with Twenty
yarn auth:logout # Remove credentials
yarn auth:status # Check auth status
# Application
yarn app:dev # Start dev mode (sync + watch)
yarn app:sync # One-time sync
yarn entity:add # Add a new entity (function, object, role)
yarn app:generate # Generate typed Twenty client
yarn function:logs # Stream function logs
yarn function:execute # Execute a function with JSON payload
yarn app:uninstall # Uninstall app from workspace
```
## Learn More
To learn more about Twenty applications, take a look at the following resources:
@@ -5,6 +5,7 @@
"declaration": true,
"outDir": "./dist",
"rootDir": ".",
"jsx": "react-jsx",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
@@ -1,12 +1,12 @@
import { copyBaseApplicationProject } from '@/utils/app-template';
import { convertToLabel } from '@/utils/convert-to-label';
import { install } from '@/utils/install';
import { tryGitInit } from '@/utils/try-git-init';
import chalk from 'chalk';
import * as fs from 'fs-extra';
import inquirer from 'inquirer';
import * as path from 'path';
import { copyBaseApplicationProject } from '@/utils/app-template';
import kebabCase from 'lodash.kebabcase';
import { convertToLabel } from '@/utils/convert-to-label';
import { tryGitInit } from '@/utils/try-git-init';
import { install } from '@/utils/install';
import * as path from 'path';
const CURRENT_EXECUTION_DIRECTORY = process.env.INIT_CWD || process.cwd();
@@ -119,10 +119,13 @@ export class CreateAppCommand {
}
private logSuccess(appDirectory: string): void {
const dirName = appDirectory.split('/').reverse()[0] ?? '';
console.log(chalk.green('✅ Application created!'));
console.log('');
console.log(chalk.blue('Next steps:'));
console.log(`cd ${appDirectory.split('/').reverse()[0] ?? ''}`);
console.log('yarn auth');
console.log(chalk.gray(` cd ${dirName}`));
console.log(chalk.gray(' yarn auth:login # Authenticate with Twenty'));
console.log(chalk.gray(' yarn app:dev # Start dev mode'));
}
}
@@ -68,8 +68,8 @@ describe('copyBaseApplicationProject', () => {
expect(packageJson.name).toBe('my-test-app');
expect(packageJson.version).toBe('0.1.0');
expect(packageJson.dependencies['twenty-sdk']).toBe('0.3.1');
expect(packageJson.scripts.sync).toBe('twenty app sync');
expect(packageJson.scripts.dev).toBe('twenty app dev');
expect(packageJson.scripts['app:sync']).toBe('twenty app:sync');
expect(packageJson.scripts['app:dev']).toBe('twenty app:dev');
});
it('should create .gitignore file', async () => {
@@ -32,6 +32,10 @@ export const copyBaseApplicationProject = async ({
appDirectory: appFolderPath,
});
await createDefaultFrontComponent({
appDirectory: appFolderPath,
});
await createApplicationConfig({
displayName: appDisplayName,
description: appDescription,
@@ -115,6 +119,38 @@ export default defineRole({
await fs.writeFile(join(appDirectory, 'default-function.role.ts'), content);
};
const createDefaultFrontComponent = async ({
appDirectory,
}: {
appDirectory: string;
}) => {
const universalIdentifier = v4();
const content = `import { defineFrontComponent } from 'twenty-sdk';
export const HelloWorld = () => {
return (
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
<h1>Hello, World!</h1>
<p>This is your first front component.</p>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: '${universalIdentifier}',
name: 'hello-world',
description: 'A sample front component',
component: HelloWorld,
});
`;
await fs.writeFile(
join(appDirectory, 'hello-world.front-component.tsx'),
content,
);
};
const createApplicationConfig = async ({
displayName,
description,
@@ -156,16 +192,21 @@ const createPackageJson = async ({
},
packageManager: 'yarn@4.9.2',
scripts: {
'create-entity': 'twenty app add',
dev: 'twenty app dev',
generate: 'twenty app generate',
sync: 'twenty app sync',
logs: 'twenty app logs',
uninstall: 'twenty app uninstall',
'auth:login': 'twenty auth:login',
'auth:logout': 'twenty auth:logout',
'auth:status': 'twenty auth:status',
'auth:switch': 'twenty auth:switch',
'auth:list': 'twenty auth:list',
'app:dev': 'twenty app:dev',
'app:sync': 'twenty app:sync',
'entity:add': 'twenty entity:add',
'app:generate': 'twenty app:generate',
'function:logs': 'twenty function:logs',
'function:execute': 'twenty function:execute',
'app:uninstall': 'twenty app:uninstall',
help: 'twenty help',
auth: 'twenty auth login',
lint: 'eslint',
'lint-fix': 'eslint --fix',
'lint:fix': 'eslint --fix',
},
dependencies: {
'twenty-sdk': '0.3.1',
@@ -173,6 +214,8 @@ const createPackageJson = async ({
devDependencies: {
typescript: '^5.9.3',
'@types/node': '^24.7.2',
'@types/react': '^19.0.2',
react: '^19.0.2',
eslint: '^9.32.0',
'typescript-eslint': '^8.50.0',
},
@@ -75,6 +75,7 @@ export default defineConfig(() => {
'path',
'fs',
'child_process',
'util',
],
output: [
{
@@ -1,7 +1,7 @@
import typescriptParser from '@typescript-eslint/parser';
import path from 'path';
import { fileURLToPath } from 'url';
import reactConfig from '../../eslint.config.react.mjs';
import reactConfig from '../../../../twenty-eslint-rules/eslint.config.react.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -3,9 +3,8 @@
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react",
"baseUrl": "src",
"paths": {
"@/*": ["*"]
"@/*": ["./src/*"]
}
}
}
File diff suppressed because one or more lines are too long
+61 -3
View File
@@ -1,15 +1,21 @@
# Makefile for building Twenty CRM docker images.
# Makefile for building and running Twenty CRM docker containers.
# Set the tag and/or target build platform using make command-line variables assignments.
#
# Optional make variables:
# PLATFORM - defaults to 'linux/amd64'
# TAG - defaults to 'latest'
#
# Example: make
# Example: make PLATFORM=linux/aarch64 TAG=my-tag
# Example: make prod-build
# Example: make PLATFORM=linux/aarch64 TAG=my-tag prod-build
# Example: make postgres-on-docker (for local development)
PLATFORM ?= linux/amd64
TAG ?= latest
DOCKER_NETWORK=twenty_network
# =============================================================================
# Production Image Building
# =============================================================================
prod-build:
@cd ../.. && docker build -f ./packages/twenty-docker/twenty/Dockerfile --platform $(PLATFORM) --tag twenty:$(TAG) . && cd -
@@ -25,3 +31,55 @@ prod-website-build:
prod-website-run:
@docker run -d -p 3000:3000 --name twenty-website twenty-website:$(TAG)
# =============================================================================
# Local Development Services
# Run these from the repository root: make -C packages/twenty-docker <target>
# =============================================================================
ensure-docker-network:
docker network inspect $(DOCKER_NETWORK) >/dev/null 2>&1 || docker network create $(DOCKER_NETWORK)
postgres-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) \
--name twenty_pg \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e ALLOW_NOSSL=true \
-v twenty_db_data:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16
@echo "Waiting for PostgreSQL to be ready..."
@until docker exec twenty_pg psql -U postgres -d postgres \
-c 'SELECT pg_is_in_recovery();' 2>/dev/null | grep -q 'f'; do \
sleep 1; \
done
docker exec twenty_pg psql -U postgres -d postgres \
-c "CREATE DATABASE \"default\" WITH OWNER postgres;" \
-c "CREATE DATABASE \"test\" WITH OWNER postgres;"
redis-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
clickhouse-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) --name twenty_clickhouse -p 8123:8123 -p 9000:9000 -e CLICKHOUSE_PASSWORD=devPassword clickhouse/clickhouse-server:latest \
grafana-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) \
--name twenty_grafana \
-p 4000:3000 \
-e GF_SECURITY_ADMIN_USER=admin \
-e GF_SECURITY_ADMIN_PASSWORD=admin \
-e GF_INSTALL_PLUGINS=grafana-clickhouse-datasource \
-v $(PWD)/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources \
grafana/grafana-oss:latest
opentelemetry-collector-on-docker: ensure-docker-network
docker run -d --network $(DOCKER_NETWORK) \
--name twenty_otlp_collector \
-p 4317:4317 \
-p 4318:4318 \
-p 13133:13133 \
-v $(PWD)/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector-contrib:latest \
--config /etc/otel-collector-config.yaml
@@ -8,7 +8,7 @@ COPY ./yarn.lock .
COPY ./.yarnrc.yml .
COPY ./.yarn/releases /app/.yarn/releases
COPY ./.yarn/patches /app/.yarn/patches
COPY ./tools/eslint-rules /app/tools/eslint-rules
COPY ./packages/twenty-eslint-rules /app/packages/twenty-eslint-rules
COPY ./packages/twenty-ui/package.json /app/packages/twenty-ui/
COPY ./packages/twenty-shared/package.json /app/packages/twenty-shared/
COPY ./packages/twenty-website/package.json /app/packages/twenty-website/package.json
@@ -40,4 +40,4 @@ RUN chown -R 1000 /app
# Use non root user with uid 1000
USER 1000
CMD ["/bin/sh", "-c", "npx nx start"]
CMD ["/bin/sh", "-c", "npx nx start"]
-1
View File
@@ -8,7 +8,6 @@ COPY ./package.json ./yarn.lock ./.yarnrc.yml ./tsconfig.base.json ./nx.json /ap
COPY ./.yarn/releases /app/.yarn/releases
COPY ./.yarn/patches /app/.yarn/patches
COPY ./.prettierrc /app/
COPY ./packages/twenty-emails/package.json /app/packages/twenty-emails/
COPY ./packages/twenty-server/package.json /app/packages/twenty-server/
COPY ./packages/twenty-server/patches /app/packages/twenty-server/patches
@@ -112,7 +112,7 @@ You should run all commands in the following steps from the root of the project.
**Option 2:** If you have docker installed:
```bash
make postgres-on-docker
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Mac OS">
@@ -162,7 +162,7 @@ You should run all commands in the following steps from the root of the project.
**Option 2:** If you have docker installed:
```bash
make postgres-on-docker
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
@@ -179,7 +179,7 @@ You should run all commands in the following steps from the root of the project.
Running Docker on WSL adds an extra layer of complexity.
Only use this option if you are comfortable with the extra steps involved, including turning on [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make postgres-on-docker
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
@@ -196,7 +196,7 @@ Twenty requires a redis cache to provide the best performance
**Option 2:** If you have docker installed:
```bash
make redis-on-docker
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Mac OS">
@@ -209,7 +209,7 @@ Twenty requires a redis cache to provide the best performance
**Option 2:** If you have docker installed:
```bash
make redis-on-docker
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
@@ -218,7 +218,7 @@ Twenty requires a redis cache to provide the best performance
**Option 2:** If you have docker installed:
```bash
make redis-on-docker
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
@@ -34,32 +34,35 @@ npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
# Authenticate using your API key (you'll be prompted)
yarn auth
yarn auth:login
# Start dev mode: automatically syncs local changes to your workspace
yarn dev
yarn app:dev
```
From here you can:
```bash filename="Terminal"
# Add a new entity to your application (guided)
yarn create-entity
yarn app:create-entity
# Generate a typed Twenty client and workspace entity types
yarn generate
yarn app:generate
# Run a onetime sync (instead of watch mode)
yarn sync
yarn app:sync
# Watch your application's functions logs
yarn logs
yarn function:logs
# Execute a function by name
yarn function:execute -n my-function -p '{"name": "test"}'
# Uninstall the application from the current workspace
yarn uninstall
yarn app:uninstall
# Display commands' help
yarn help
yarn app:help
```
See also: the CLI reference pages for [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) and [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
@@ -159,29 +162,43 @@ At a high level:
Later commands will add more files and folders:
- `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
- `yarn create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
- `yarn app:generate` will create a `generated/` folder (typed Twenty client + workspace types).
- `yarn app:create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
l
## Authentication
The first time you run `yarn auth`, you'll be prompted for:
The first time you run `yarn auth:login`, you'll be prompted for:
- API URL (defaults to http://localhost:3000 or your current workspace profile)
- API key
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch using `--workspace <name>`.
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch between them.
Examples:
### Managing workspaces
```bash filename="Terminal"
# Login interactively (recommended)
yarn auth
yarn auth:login
# Use a specific workspace profile
yarn auth --workspace my-custom-workspace
# Login to a specific workspace profile
yarn auth:login --workspace my-custom-workspace
# List all configured workspaces
yarn auth:list
# Switch the default workspace (interactive)
yarn auth:switch
# Switch to a specific workspace
yarn auth:switch production
# Check current authentication status
yarn auth:status
```
Once you've switched workspaces with `auth:switch`, all subsequent commands will use that workspace by default. You can still override it temporarily with `--workspace <name>`.
## Use the SDK resources (types & config)
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
@@ -278,7 +295,7 @@ Key points:
- The `universalIdentifier` must be unique and stable across deployments.
- Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
- The `fields` array is optional — you can define objects without custom fields.
- You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
- You can scaffold new objects using `yarn app:create-entity`, which guides you through naming, fields, and relationships.
<Note>
**Base fields are created automatically.** When you define a custom object, Twenty automatically adds standard fields such as `name`, `createdAt`, `updatedAt`, `createdBy`, `position`, and `deletedAt`. You don't need to define these in your `fields` array — only add your custom fields.
@@ -443,18 +460,18 @@ Each function file uses `defineFunction()` to export a configuration with a hand
```typescript
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
const handler = async (
params:
| { name?: string }
| RoutePayload
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
| CronPayload,
) => {
const client = new Twenty(); // generated typed client
const name = 'name' in params
? params.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
const name = 'name' in params.queryStringParameters
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
: 'Hello world';
const result = await client.mutation({
@@ -491,7 +508,8 @@ export default defineFunction({
{
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
eventName: 'person.updated',
updatedFields: ['name'],
},
],
});
@@ -501,21 +519,111 @@ Common trigger types:
- **route**: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
- **cron**: Runs your function on a schedule using a CRON expression.
- **databaseEvent**: Runs on workspace object lifecycle events
> e.g. `person.created`
- **databaseEvent**: Runs on workspace object lifecycle events. When the event operation is `updated`, specific fields to listen to can be specified in the `updatedFields` array. If left undefined or empty, any update will trigger the function.
> e.g. `person.updated`
Notes:
- The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
- You can mix multiple trigger types in a single function.
### Route trigger payload
<Warning>
**Breaking change (v1.16, January 2026):** The route trigger payload format has changed. Prior to v1.16, query parameters, path parameters, and body were sent directly as the payload. Starting with v1.16, they are nested inside a structured `RoutePayload` object.
**Before v1.16:**
```typescript
const handler = async (params) => {
const { param1, param2 } = params; // Direct access
};
```
**After v1.16:**
```typescript
const handler = async (event: RoutePayload) => {
const { param1, param2 } = event.body; // Access via .body
const { queryParam } = event.queryStringParameters;
const { id } = event.pathParameters;
};
```
**To migrate existing functions:** Update your handler to destructure from `event.body`, `event.queryStringParameters`, or `event.pathParameters` instead of directly from the params object.
</Warning>
When a route trigger invokes your function, it receives a `RoutePayload` object that follows the AWS HTTP API v2 format. Import the type from `twenty-sdk`:
```typescript
import { defineFunction, type RoutePayload } from 'twenty-sdk';
const handler = async (event: RoutePayload) => {
// Access request data
const { headers, queryStringParameters, pathParameters, body } = event;
// HTTP method and path are available in requestContext
const { method, path } = event.requestContext.http;
return { message: 'Success' };
};
```
The `RoutePayload` type has the following structure:
| Property | Type | Description |
|----------|------|-------------|
| `headers` | `Record<string, string \| undefined>` | HTTP headers (only those listed in `forwardedRequestHeaders`) |
| `queryStringParameters` | `Record<string, string \| undefined>` | Query string parameters (multiple values joined with commas) |
| `pathParameters` | `Record<string, string \| undefined>` | Path parameters extracted from the route pattern (e.g., `/users/:id` → `{ id: '123' }`) |
| `body` | `object \| null` | Parsed request body (JSON) |
| `isBase64Encoded` | `boolean` | Whether the body is base64 encoded |
| `requestContext.http.method` | `string` | HTTP method (GET, POST, PUT, PATCH, DELETE) |
| `requestContext.http.path` | `string` | Raw request path |
### Forwarding HTTP headers
By default, HTTP headers from incoming requests are **not** passed to your serverless function for security reasons. To access specific headers, explicitly list them in the `forwardedRequestHeaders` array:
```typescript
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'webhook-handler',
handler,
triggers: [
{
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/webhook',
httpMethod: 'POST',
isAuthRequired: false,
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
},
],
});
```
In your handler, you can then access these headers:
```typescript
const handler = async (event: RoutePayload) => {
const signature = event.headers['x-webhook-signature'];
const contentType = event.headers['content-type'];
// Validate webhook signature...
return { received: true };
};
```
<Note>
Header names are normalized to lowercase. Access them using lowercase keys (for example, `event.headers['content-type']`).
</Note>
You can create new functions in two ways:
- **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
- **Scaffolded**: Run `yarn app:create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
- **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, following the same pattern.
### Generated typed client
Run yarn generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
Run yarn app:generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
```typescript
import Twenty from './generated';
@@ -524,7 +632,7 @@ const client = new Twenty();
const { me } = await client.query({ me: { id: true, displayName: true } });
```
The client is re-generated by `yarn generate`. Re-run after changing your objects and `yarn sync` or when onboarding to a new workspace.
The client is re-generated by `yarn app:generate`. Re-run after changing your objects and `yarn app:sync` or when onboarding to a new workspace.
#### Runtime credentials in serverless functions
@@ -568,13 +676,13 @@ Then add scripts like these:
}
```
Now you can run the same commands via Yarn, e.g. `yarn dev`, `yarn sync`, etc.
Now you can run the same commands via Yarn, e.g. `yarn app:dev`, `yarn app:sync`, etc.
## Troubleshooting
- Authentication errors: run `yarn auth` and ensure your API key has the required permissions.
- Authentication errors: run `yarn auth:login` and ensure your API key has the required permissions.
- Cannot connect to server: verify the API URL and that the Twenty server is reachable.
- Types or client missing/outdated: run `yarn generate` and then `yarn dev`.
- Dev mode not syncing: ensure `yarn dev` is running and that changes are not ignored by your environment.
- Types or client missing/outdated: run `yarn app:generate` and then `yarn app:dev`.
- Dev mode not syncing: ensure `yarn app:dev` is running and that changes are not ignored by your environment.
Discord Help Channel: https://discord.com/channels/1130383047699738754/1130386664812982322
@@ -288,3 +288,44 @@ yarn command:prod cron:workflow:automated-cron-trigger
<Warning>
**Environment-only mode:** If you set `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, add these variables to your `.env` file instead.
</Warning>
## Serverless Functions
Twenty supports serverless functions for workflows and custom logic. The execution environment is configured via the `SERVERLESS_TYPE` environment variable.
<Warning>
**Security Notice:** The local serverless driver (`SERVERLESS_TYPE=LOCAL`) runs code directly on the host in a Node.js process with no sandboxing. It should only be used for trusted code in development. For production deployments handling untrusted code, we highly recommend using `SERVERLESS_TYPE=LAMBDA` or `SERVERLESS_TYPE=DISABLED`.
</Warning>
### Available Drivers
| Driver | Environment Variable | Use Case | Security Level |
|--------|---------------------|----------|----------------|
| Disabled | `SERVERLESS_TYPE=DISABLED` | Disable serverless functions entirely | N/A |
| Local | `SERVERLESS_TYPE=LOCAL` | Development and trusted environments | Low (no sandboxing) |
| Lambda | `SERVERLESS_TYPE=LAMBDA` | Production with untrusted code | High (hardware-level isolation) |
### Recommended Configuration
**For development:**
```bash
SERVERLESS_TYPE=LOCAL # default
```
**For production (AWS):**
```bash
SERVERLESS_TYPE=LAMBDA
SERVERLESS_LAMBDA_REGION=us-east-1
SERVERLESS_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role
SERVERLESS_LAMBDA_ACCESS_KEY_ID=your-access-key
SERVERLESS_LAMBDA_SECRET_ACCESS_KEY=your-secret-key
```
**To disable serverless functions:**
```bash
SERVERLESS_TYPE=DISABLED
```
<Note>
When using `SERVERLESS_TYPE=DISABLED`, any attempt to execute a serverless function will return an error. This is useful if you want to run Twenty without serverless function capabilities.
</Note>
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@@ -2,7 +2,7 @@
title: أفضل الممارسات
---
This document outlines the best practices you should follow when working on the backend.
تحدد هذه الوثيقة أفضل الممارسات التي يجب اتباعها عند العمل في الواجهة الخلفية.
## اتبع نهجًا معياريًا
@@ -11,7 +11,7 @@ This document outlines the best practices you should follow when working on the
## إتاحة الخدمات لاستخدامها في الوحدات
قم دائمًا بإنشاء خدمات ذات مسؤولية واضحة ووحيدة، مما يعزز من قابلية قراءة وصيانة الكود. Name the services descriptively and consistently.
قم دائمًا بإنشاء خدمات ذات مسؤولية واضحة ووحيدة، مما يعزز من قابلية قراءة وصيانة الكود. قم بتسمية الخدمات بأسماء وصفية وبطريقة متسقة.
يجب أيضًا إتاحة الخدمات التي تريد استخدامها في وحدات أخرى. إتاحة الخدمات للوحدات الأخرى ممكنة عبر نظام حقن التبعيات القوي في NestJS، كما يعزز ذلك الاقتران الضعيف بين المكونات.
@@ -1,8 +1,8 @@
---
title: Feature Flags
title: أعلام الميزة
---
تُستخدم أعلام الميزات لإخفاء الميزات التجريبية. For Twenty, they are set on workspace level and not on a user level.
تُستخدم أعلام الميزات لإخفاء الميزات التجريبية. بالنسبة إلى Twenty، يتم ضبطها على مستوى مساحة العمل وليس على مستوى المستخدم.
## إضافة علم ميزة جديد
@@ -7,18 +7,18 @@ info: نظرة تفصيلية داخل هيكلية مجلدات الخادم
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
└───قدرات
└───ثوابت
└───نواة
└───قاعدة البيانات
└───زخارف
└───فلاتر
└───حمايات
└───الصحة
└───تكاملات
└───بيانات وصفية
└───مساحة العمل
└───أدوات
```
## قدرات
@@ -56,28 +56,28 @@ server
```
workspace
└───workspace-schema-builder
└───factories
└───graphql-types
└───database
└───interfaces
└───object-definitions
└───services
└───storage
└───utils
└───workspace-resolver-builder
└───factories
└───interfaces
└───workspace-query-builder
└───factories
└───interfaces
└───workspace-query-runner
└───interfaces
└───utils
└───workspace-datasource
└───workspace-manager
└───workspace-migration-runner
└───utils
└───منشئ مساحة العمل
└───مصانع
└───أنواع GraphQL
└───قاعدة البيانات
└───واجهات
└───تعريفات الكائنات
└───خدمات
└───التخزين
└───أدوات
└───منشئ مستعرض مساحة العمل
└───مصانع
└───واجهات
└───منشئ الاستعلامات مساحة العمل
└───مصانع
└───واجهات
└───مشغل استعلامات مساحة العمل
└───واجهات
└───أدوات
└───مصدر بيانات مساحة العمل
└───مدير مساحة العمل
└───مشغل الانتقالات مساحة العمل
└───أدوات
└───workspace.module.ts
└───workspace.factory.spec.ts
└───workspace.factory.ts
@@ -96,7 +96,7 @@ workspace
مصانع متخصصة لتوليد الإنشاءات المتعلقة بـ GraphQL.
* المصنع النوعي يترجم بيانات الحقول الوصفية إلى أنواع GraphQL باستخدام `TypeMapperService`.
* The type-definition.factory creates GraphQL input or output objects derived from `objectMetadata`.
* ينشئ type-definition.factory كائنات إدخال أو إخراج لـ GraphQL مشتقة من `objectMetadata`.
#### أنواع GraphQL
@@ -1,5 +1,5 @@
---
title: Message Queue
title: قائمة انتظار الرسائل
---
تسهل القوائم العمليات غير المتزامنة. يمكن استخدامها لأداء مهام الخلفية مثل إرسال بريد ترحيبي عند التسجيل.
@@ -10,9 +10,9 @@ title: Message Queue
## خطوات إنشاء واستخدام قائمة جديدة
1. أضف اسم قائمة لقائمة جديدة تحت التعداد `MESSAGE_QUEUES`.
2. Provide the factory implementation of the queue with the queue name as the dependency token.
2. وفّر تنفيذ المصنع لقائمة الانتظار مع استخدام اسم قائمة الانتظار بوصفه رمز التبعية.
3. قم بإدراج القائمة التي أنشأتها في الوحدة/الخدمة المطلوبة مع اسم القائمة كرمز تبعية.
4. Add worker class with token based injection just like producer.
4. أضِف صنف عامل مع حقن قائم على الرمز تمامًا مثل المنتِج.
### نموذج للاستخدام
@@ -5,7 +5,7 @@ title: الأوامر الخلفية
## الأوامر المفيدة
يجب تنفيذ هذه الأوامر من مجلد packages/twenty-server.
From any other folder you can run `npx nx {command} twenty-server` (or `npx nx run twenty-server:{command}`).
من أي مجلد آخر يمكنك تشغيل `npx nx {command} twenty-server` (أو `npx nx run twenty-server:{command}`).
### إعداد المرة الأولى
@@ -22,7 +22,7 @@ npx nx run twenty-server:start
### Lint
```
npx nx run twenty-server:lint # pass --fix to fix lint errors
npx nx run twenty-server:lint # مرر --fix لإصلاح أخطاء التدقيق
```
### تجربة
@@ -36,13 +36,13 @@ npx nx run twenty-server:test:integration # تشغيل اختبارات التك
### إعادة تعيين قاعدة البيانات
If you want to reset and seed the database, you can run the following command:
إذا أردت إعادة تعيين قاعدة البيانات وزرع بعض البيانات فيها، يمكنك تشغيل الأمر التالي:
```bash
npx nx run twenty-server:database:reset
```
### Migrations
### عمليات الترحيل
#### للكائنات داخل مخططات Core/Metadata (TypeORM)
@@ -67,13 +67,13 @@ npx nx run twenty-server:command workspace:sync-metadata -f
## "التقنية المستخدمة"
Twenty primarily uses NestJS for the backend.
تستخدم Twenty بشكل أساسي NestJS للواجهة الخلفية.
Prisma كان أول ORM استخدمناه. ولكن للسماح للمستخدمين بإنشاء الحقول والعناصر المخصصة، كان من المنطقي استخدام مستوى أقل حيث نحتاج إلى تحكم دقيق. الآن يستخدم المشروع TypeORM.
إليك شكل العناصر التقنية الآن.
**Core**
**النواة**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
@@ -1,6 +1,6 @@
---
title: Bugs, Requests & Pull Requests
info: Report issues, request features, and contribute code
title: الأخطاء والطلبات وطلبات السحب
info: أبلغ عن المشكلات، واطلب الميزات، وساهم بالشفرة البرمجية
---
## الإبلاغ عن الأخطاء
@@ -9,70 +9,70 @@ info: Report issues, request features, and contribute code
يمكنك أيضًا طلب المساعدة عبر [Discord](https://discord.gg/cx5n4Jzs57).
## Feature Requests
## طلبات الميزات
إذا لم تكن متأكدًا مما إذا كانت مشكلة أو إذا كنت تشعر بأنها مجرد طلب ميزة، فيمكنك على الأرجح [فتح نقاش بدلاً من ذلك](https://github.com/twentyhq/twenty/discussions/new).
## Submit a Pull Request
## أرسل طلب سحب
Contributing code to Twenty starts with a pull request (PR).
تبدأ المساهمة بالشفرة في Twenty بطلب سحب (PR).
### قبل أن تبدأ
1. Check [existing issues](https://github.com/twentyhq/twenty/issues) for related work
2. For new features, open an issue first to discuss
3. Review our [Code of Conduct](https://github.com/twentyhq/twenty/blob/main/CODE_OF_CONDUCT.md)
1. تحقق من [المشكلات القائمة](https://github.com/twentyhq/twenty/issues) للعمل ذي الصلة
2. بالنسبة للميزات الجديدة، افتح مشكلة أولًا للنقاش
3. راجع [مدونة قواعد السلوك](https://github.com/twentyhq/twenty/blob/main/CODE_OF_CONDUCT.md)
### Fork and Clone
### التفريع والاستنساخ
1. Fork the repository on GitHub
2. Clone your fork:
1. فرّع المستودع على GitHub
2. استنسخ تفريعك:
```bash
git clone https://github.com/YOUR_USERNAME/twenty.git
cd twenty
```
3. Add upstream remote:
3. أضف المستودع البعيد upstream:
```bash
git remote add upstream https://github.com/twentyhq/twenty.git
```
### Create a Branch
### إنشاء فرع
```bash
git checkout -b feature/your-feature-name
```
Use descriptive branch names:
استخدم أسماء فروع وصفية:
* `feature/add-export-button`
* `fix/login-redirect-issue`
* `docs/update-api-guide`
### Make Your Changes
### أجرِ تعديلاتك
1. Write clean, well-documented code
2. Follow existing code style
3. Add tests for new functionality
4. Update documentation if needed
1. اكتب شفرة نظيفة وموثقة جيدًا
2. اتبع أسلوب الشفرة المعمول به
3. أضف اختبارات للوظائف الجديدة
4. حدّث الوثائق عند الحاجة
### Submit Your PR
### أرسل طلب السحب الخاص بك
1. Push your branch:
1. ادفع فرعك:
```bash
git push origin feature/your-feature-name
```
2. Open a PR on GitHub
3. Fill in the PR template
4. Link related issues
2. افتح طلب سحب على GitHub
3. املأ قالب طلب السحب
4. اربط المشكلات ذات الصلة
### PR Checklist
### قائمة التحقق لطلب السحب
* [ ] Code follows project style guidelines
* [ ] Tests pass locally
* [ ] Documentation is updated
* [ ] PR description explains the changes
* [ ] الشفرة تتبع إرشادات أسلوب المشروع
* [ ] تجتاز الاختبارات محليًا
* [ ] الوثائق محدّثة
* [ ] وصف طلب السحب يوضح التغييرات
@@ -77,8 +77,8 @@ If you feel like you need to add a `useEffect` in your root component, you shoul
يمكنك تطبيق نفس الشيء على منطق جلب البيانات، مع الخُطافات Apollo.
```tsx
// ❌ Bad, will cause re-renders even if data is not changing,
// because useEffect needs to be re-evaluated
// ❌ سيّئ، سيتسبب في إعادة التصيير حتى إذا لم تتغير البيانات،
// لأن useEffect يحتاج إلى إعادة التقييم
export const PageComponent = () => {
const [data, setData] = useRecoilState(dataState);
const [someDependency] = useRecoilState(someDependencyState);
@@ -100,8 +100,8 @@ export const App = () => (
```
```tsx
// ✅ Good, will not cause re-renders if data is not changing,
// because useEffect is re-evaluated in another sibling component
// ✅ جيّد، لن يتسبب في إعادة التصيير إذا لم تتغير البيانات،
// لأن useEffect يُعاد تقييمه في مكوّن شقيق آخر
export const PageComponent = () => {
const [data, setData] = useRecoilState(dataState);
@@ -168,13 +168,13 @@ export const App = () => (
الأسماء العامة في البرمجة ليست مثالية لأنها تفتقر إلى التحديد، مما يؤدي إلى الغموض وتقليل قابلية قراءة التعليمات البرمجية. مثل هذه الأسماء تفشل في التعبير عن الغرض من المتغير أو الوظيفة، مما يجعل من الصعب على المطورين فهم نية التعليمات البرمجية دون تحقيق أعمق. يمكن أن يؤدي ذلك إلى زيادة وقت إزالة الأخطاء، وزيادة قابلية التعرض للأخطاء، وصعوبات في الصيانة والتعاون. في الوقت نفسه، تجعل التسمية الوصفية التعليمات البرمجية تفسيرية بذاتها وأسهل في التنقل، مما يعزز جودة التعليمات البرمجية وإنتاجية المطور.
```tsx
// ❌ Bad, uses a generic name that doesn't communicate its
// purpose or content clearly
// ❌ سيّئ، يستخدم اسمًا عامًا لا يوضح
// الغرض أو المحتوى بوضوح
const [value, setValue] = useState('');
```
```tsx
// ✅ Good, uses a descriptive name
// ✅ جيّد، يستخدم اسمًا وصفيًا
const [email, setEmail] = useState('');
```
@@ -187,14 +187,14 @@ const [email, setEmail] = useState('');
يجب أن تبدأ أسماء معالجات الأحداث بكلمة `handle`، بينما يعتبر `on` بادئة تستخدم لتسمية الأحداث في خصائص المكونات.
```tsx
// ❌ Bad
// ❌ سيّئ
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
// ✅ جيّد
const handleEmailChange = (val: string) => {
// ...
};
@@ -222,12 +222,12 @@ const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
**الاستخدام**
```tsx
// ❌ Bad, passing in the same value as the default value adds no value
// ❌ سيّئ، تمرير نفس القيمة كقيمة افتراضية لا يضيف أي فائدة
const Form = () => <EmailField value="username@email.com" disabled={false} />;
```
```tsx
// ✅ Good, assumes the default value
// ✅ جيّد، يفترض القيمة الافتراضية
const Form = () => <EmailField value="username@email.com" />;
```
@@ -240,7 +240,7 @@ const Form = () => <EmailField value="username@email.com" />;
```tsx
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
// In MyComponent
// داخل MyComponent
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
const theme = useTheme();
@@ -285,7 +285,7 @@ const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
**الاستخدام**
```tsx
// ❌ Bad, specifies the entire relative path
// ❌ سيّئ، يحدد المسار النسبي بالكامل
import {
CatalogDecorator
} from '../../../../../testing/decorators/CatalogDecorator';
@@ -295,7 +295,7 @@ import {
```
```tsx
// ✅ Good, utilises the designated aliases
// ✅ جيّد، يستخدم الأسماء المستعارة المحددة
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from 'twenty-ui/testing';
```
@@ -310,10 +310,10 @@ const validationSchema = z
exist: z.boolean(),
email: z
.string()
.email('Email must be a valid email'),
.email('يجب أن يكون البريد الإلكتروني صالحًا'),
password: z
.string()
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
.regex(PASSWORD_REGEX, 'يجب أن تحتوي كلمة المرور على 8 أحرف على الأقل'),
})
.required();
@@ -14,9 +14,9 @@ title: مفاتيح الاختصار
لمعالجة هذه المشكلة، لدينا خطاف مخصص يمكن من الاستماع لمفاتيح الاختصار دون أي تعارض.
You place it in a component, and it will listen to the hotkeys only when the component is mounted AND when the specified **hotkey scope** is active.
تضعه داخل مكوّن، ولن يستمع إلى مفاتيح الاختصار إلا عندما يكون المكوّن مُركّبًا وعندما يكون **نطاق مفاتيح الاختصار** المحدّد نشطًا.
## How to listen for hotkeys in practice?
## كيف تستمع إلى مفاتيح الاختصار عمليًا؟
هناك خطوتان متضمنتان في إعداد الاستماع لمفاتيح الاختصار:
@@ -73,9 +73,9 @@ const PageListeningEnter = () => {
### الاستماع لمفاتيح الاختصار في مكون من نوع مودال
For this example we'll use a modal component that listens for the Escape key to tell its parent to close it.
في هذا المثال سنستخدم مكوّن نافذة منبثقة يستمع لمفتاح Escape ليطلب من المكوّن الأب إغلاقه.
Here the user interaction is changing the scope.
هنا يتمثّل تفاعل المستخدم في تغيير النطاق.
```tsx
const ExamplePageWithModal = () => {
@@ -1,8 +1,8 @@
---
title: Storybook
description: Browse Twenty's UI component library
description: تصفح مكتبة مكونات واجهة المستخدم الخاصة بـ Twenty
---
View our complete component library and documentation in Storybook.
اطلع على مكتبة المكونات الكاملة لدينا والتوثيق في Storybook.
[Open Storybook →](https://storybook.twenty.com)
[افتح Storybook →](https://storybook.twenty.com)
@@ -4,7 +4,7 @@ title: دليل الأسلوب
تشمل هذه الوثيقة القواعد التي يجب اتباعها عند كتابة التعليمات البرمجية.
The goal here is to have a consistent codebase, which is easy to read and easy to maintain.
الهدف هنا هو وجود قاعدة شيفرة متسقة يسهل قراءتها وصيانتها.
لهذا، من الأفضل أن تكون تفصيلًا أكثر قليلاً بدلاً من أن تكون موجزًا للغاية.
@@ -67,10 +67,10 @@ const EmailField: React.FC<{
```
```tsx
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
* component's props
* - This method doesn't automatically include the children prop. If
* you want to include it, you have to specify it in OwnProps.
/* ✅ - جيد، نوع منفصل (OwnProps) يُعرَّف صراحةً لخصائص
* المكون
* - هذه الطريقة لا تتضمن تلقائيًا خاصية الأطفال. إذا
* كنت تريد تضمينها، يجب أن تحددها في OwnProps.
*/
type EmailFieldProps = {
value: string;
@@ -81,7 +81,7 @@ const EmailField = ({ value }: EmailFieldProps) => (
);
```
#### No Single Variable Prop Spreading in JSX Elements
#### لا تستخدم انتشار متغير فردي للخصائص في عناصر JSX
تجنب استخدام انتشار متغير فردي للخصائص في عناصر JSX، مثل `{...props}`. غالبًا ما تؤدي هذه الممارسة إلى شكل تعليمي أقل قابلية للقراءة وأصعب في الصيانة لأنه من غير الواضح أي الخصائص يتلقاها المكون.
@@ -94,8 +94,8 @@ const MyComponent = (props: OwnProps) => {
```
```tsx
/* ✅ - Good, Explicitly lists all props
* - Enhances readability and maintainability
/* ✅ - جيد، يسرد جميع الخصائص بوضوح
* - يعزز من قابلية القراءة والصيانة
*/
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
@@ -110,7 +110,7 @@ const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
## JavaScript
### Use nullish-coalescing operator `??`
### استخدم معامل دمج القيم الفارغة `??`
```tsx
// ❌ سيء، قد يعيد 'default' حتى إذا كانت القيمة 0 أو ''
@@ -120,13 +120,13 @@ const value = process.env.MY_VALUE || 'default';
const value = process.env.MY_VALUE ?? 'default';
```
### Use optional chaining `?.`
### استخدم معامل الربط الاختياري `?.`
```tsx
// ❌ Bad
// ❌ سيء
onClick && onClick();
// ✅ Good
// ✅ جيد
onClick?.();
```
@@ -148,7 +148,7 @@ type MyType = {
};
```
### Use string literals instead of enums
### استخدم السلاسل النصية بدلاً من التعدادات
[الحروف المشفوعة](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) هي الطريقة المفضلة للتعامل مع القيم الشبيهة بالأعداد المخصصة في TypeScript. من السهل توسيعها باستخدام Pick و Omit، وتقدم تجربة مطور أفضل، خاصة مع إكمال التعليمات البرمجية.
@@ -190,7 +190,7 @@ setHotkeyScopeAndMemorizePreviousScope(
);
```
## Styling
## التنسيق
### استخدام مكونات منسقة
@@ -224,7 +224,7 @@ const StyledTitle = styled.div`
`;
```
### Theming
### السمات
استخدام السمة لتنسيق معظم المكونات هو النهج المفضل.
@@ -1,6 +1,6 @@
---
title: العمل مع فيجما
info: Learn how you can collaborate with Twenty's Figma
info: تعرّف على كيفية التعاون باستخدام Figma الخاص بـ Twenty
---
فيجما هي أداة تصميم واجهات تعاونية تساعد في سد فجوة التواصل بين المصممين والمطورين.
@@ -18,7 +18,7 @@ info: Learn how you can collaborate with Twenty's Figma
## هيكل فيجما
On the left sidebar, you can access the different pages of Twenty's Figma. هكذا هم مُنظمون:
من الشريط الجانبي الأيسر، يمكنك الوصول إلى الصفحات المختلفة في Figma الخاص بـ Twenty. هكذا هم مُنظمون:
* **صفحة المكونات:** هذه هي الصفحة الأولى. يستخدمها المصمم لإنشاء وتنظيم العناصر التصميمية القابلة لإعادة الاستخدام في ملف التصميم. على سبيل المثال، الأزرار، الأيقونات، الرموز أو أي مكونات أخرى قابلة لإعادة الاستخدام. تعمل على الحفاظ على التناسق عبر التصميم.
* **الصفحة الرئيسية:** الصفحة الثانية هي الصفحة الرئيسية التي تظهر واجهة المستخدم الكاملة للمشروع. يمكنك الضغط على ***تشغيل*** لاستخدام النموذج الأولي الكامل للتطبيق.
@@ -1,9 +1,9 @@
---
title: الإعداد المحلي
description: The guide for contributors (or curious developers) who want to run Twenty locally.
description: الدليل للمساهمين (أو المطورين الفضوليين) الذين يرغبون في تشغيل Twenty محلياً.
---
## Prerequisites
## المتطلبات الأساسية
<Tabs>
<Tab title="Linux و MacOS">
@@ -15,7 +15,7 @@ description: The guide for contributors (or curious developers) who want to run
* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
<Warning>
لن يعمل `npm` ، يجب عليك استخدام `yarn` بدلًا من ذلك. Yarn is now shipped with Node.js, so you don't need to install it separately.
لن يعمل `npm` ، يجب عليك استخدام `yarn` بدلًا من ذلك. يأتي Yarn الآن مع Node.js، لذا لست بحاجة إلى تثبيته بشكل منفصل.
عليك فقط تشغيل `corepack enable` لتفعيل Yarn إذا لم تقم بذلك بعد.
</Warning>
</Tab>
@@ -30,7 +30,7 @@ description: The guide for contributors (or curious developers) who want to run
يجب أن ترى الآن مطالبة لإعادة تشغيل جهاز الكمبيوتر الخاص بك. إذا لم يكن كذلك، فأعد تشغيله يدويًا.
Upon restart, a powershell window will open and install Ubuntu. قد يستغرق هذا وقتًا طويلاً.
عند إعادة التشغيل، ستُفتح نافذة PowerShell وسيتم تثبيت Ubuntu. قد يستغرق هذا وقتًا طويلاً.
سترى مطالبة لإنشاء اسم المستخدم وكلمة المرور لتثبيت Ubuntu الخاص بك.
2. تثبيت وإعداد git
@@ -114,7 +114,7 @@ cd twenty
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make postgres-on-docker
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
@@ -173,7 +173,7 @@ cd twenty
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make postgres-on-docker
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
@@ -194,7 +194,7 @@ cd twenty
استخدم هذا الخيار فقط إذا كنت مرتاحًا مع الخطوات الإضافية المتضمنة، بما في ذلك تشغيل [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make postgres-on-docker
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
@@ -213,7 +213,7 @@ cd twenty
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make redis-on-docker
make -C packages/twenty-docker redis-on-docker
```
</Tab>
@@ -230,7 +230,7 @@ cd twenty
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make redis-on-docker
make -C packages/twenty-docker redis-on-docker
```
</Tab>
@@ -241,7 +241,7 @@ cd twenty
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make redis-on-docker
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
@@ -260,7 +260,7 @@ cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
```
<Info>
**Multi-Workspace Mode:** By default, Twenty runs in single-workspace mode where only one workspace can be created. To enable multi-workspace support (useful for testing subdomain-based features), set `IS_MULTIWORKSPACE_ENABLED=true` in your server `.env` file. See [Multi-Workspace Mode](/l/ar/developers/self-host/capabilities/setup#multi-workspace-mode) for details.
**وضع تعدد مساحات العمل:** بشكل افتراضي، يعمل Twenty في وضع مساحة عمل واحدة حيث يمكن إنشاء مساحة عمل واحدة فقط. لتمكين دعم تعدد مساحات العمل (مفيد لاختبار الميزات المعتمدة على النطاقات الفرعية)، عيّن `IS_MULTIWORKSPACE_ENABLED=true` في ملف الخادم `.env`. راجع [وضع تعدد مساحات العمل](/l/ar/developers/self-host/capabilities/setup#multi-workspace-mode) للحصول على التفاصيل.
</Info>
## الخطوة 6: تثبيت التبعيات
@@ -1,6 +1,6 @@
---
title: Contribute
description: Contribute to Twenty's open-source development.
title: المساهمة
description: ساهم في تطوير Twenty المفتوح المصدر.
---
<Frame>
@@ -9,24 +9,24 @@ description: Contribute to Twenty's open-source development.
## نظرة عامة
Twenty is open-source and welcomes contributions from the community. Whether you're fixing bugs, adding features, or improving documentation, your contributions help make Twenty better for everyone.
Twenty مفتوح المصدر ويرحب بمساهمات المجتمع. سواء كنت تقوم بإصلاح الأخطاء، أو إضافة الميزات، أو تحسين التوثيق، فإن مساهماتك تساعد في جعل Twenty أفضل للجميع.
## Ways to Contribute
## طرق المساهمة
* **Report bugs**: Help identify and document issues
* **Submit features**: Propose and implement new functionality
* **Improve documentation**: Make our docs clearer and more helpful
* **Frontend development**: Work on the React-based UI
* **Backend development**: Contribute to the NestJS server
* **الإبلاغ عن الأخطاء**: ساعد في تحديد المشكلات وتوثيقها
* **تقديم الميزات**: اقترح ونفّذ وظائف جديدة
* **تحسين التوثيق**: اجعل وثائقنا أوضح وأكثر فائدة
* **تطوير الواجهة الأمامية**: اعمل على واجهة المستخدم المبنية على React
* **تطوير الواجهة الخلفية**: ساهم في خادم NestJS
## البدء
<CardGroup cols={٢}>
<Card title="Bug Reports & Requests" icon="bug" href="/l/ar/developers/contribute/capabilities/bug-and-requests">
Report issues or request features
<Card title="تقارير الأخطاء والطلبات" icon="bug" href="/l/ar/developers/contribute/capabilities/bug-and-requests">
أبلغ عن المشكلات أو اطلب ميزات
</Card>
<Card title="تطوير الواجهة الأمامية" icon="browser" href="/l/ar/developers/contribute/capabilities/frontend-development">
Contribute to the UI
ساهم في واجهة المستخدم
</Card>
</CardGroup>
@@ -1,6 +1,6 @@
---
title: واجهات برمجة التطبيقات
description: Query and modify your CRM data programmatically using REST or GraphQL.
description: استعلم وعدّل بيانات إدارة علاقات العملاء (CRM) لديك برمجياً باستخدام REST أو GraphQL.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
@@ -9,7 +9,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## النموذج الأول للمطورين
Twenty generates APIs specifically for your data model:
تقوم Twenty بإنشاء واجهات برمجة التطبيقات خصيصاً لنموذج بياناتك:
* **لا حاجة إلى معرفات طويلة**: استخدم أسماء الكائنات والحقول مباشرة في نقاط النهاية
* **معالجة متساوية للأشياء القياسية والمخصصة**: تحصل أشياؤك المخصصة على نفس معاملة واجهة برمجة التطبيقات كما هو الحال مع الأشياء المضمنة
@@ -17,52 +17,52 @@ Twenty generates APIs specifically for your data model:
* **وثائق مخصصة**: يتم إنشاؤها خصيصًا لنموذج بيانات مساحة عملك
<Note>
Your personalized API documentation is available under **Settings → API & Webhooks** after creating an API key. Since Twenty generates APIs that match your custom data model, the documentation is unique to your workspace.
وثائق واجهة برمجة التطبيقات المخصصة لك متاحة ضمن **الإعدادات → API & Webhooks** بعد إنشاء مفتاح API. نظرًا لأن Twenty تُنشئ واجهات برمجة تطبيقات تتطابق مع نموذج البيانات المخصص لديك، فإن الوثائق فريدة لمساحة عملك.
</Note>
## The Two API Types
## نوعا واجهات برمجة التطبيقات
### واجهة برمجة التطبيقات الأساسية
يتم الوصول إليها عبر `/rest/` أو `/graphql/`
Work with your actual **records** (the data):
تعامَل مع **السجلات** الفعلية لديك (البيانات):
* Create, read, update, delete People, Companies, Opportunities, etc.
* Query and filter data
* إنشاء وقراءة وتحديث وحذف الأشخاص والشركات والفرص، إلخ.
* استعلام وتصفية البيانات
* إدارة العلاقات بين السجلات
### واجهة برمجة البيانات الوصفية
يتم الوصول إليها عبر `/rest/metadata/` أو `/metadata/`
Manage your **workspace and data model**:
إدارة **مساحة العمل ونموذج البيانات** لديك:
* إنشاء أو تعديل أو حذف الكائنات والحقول
* تكوين إعدادات مساحة العمل
* Define relationships between objects
* تعريف العلاقات بين الكائنات
## REST vs GraphQL
## REST مقابل GraphQL
Both Core and Metadata APIs are available in REST and GraphQL formats:
تتوفر واجهات برمجة التطبيقات الأساسية وواجهات البيانات الوصفية بصيغتي REST وGraphQL:
| التنسيق | Available Operations |
| ----------- | ---------------------------------------------------------- |
| **REST** | CRUD, batch operations, upserts |
| **GraphQL** | Same + **batch upserts**, relationship queries in one call |
| التنسيق | العمليات المتاحة |
| ----------- | ----------------------------------------------------------------------------- |
| **REST** | CRUD، عمليات الدفعات، إدراج/تحديث |
| **GraphQL** | نفس الشيء + **عمليات إدراج/تحديث مجمعة**، واستعلامات العلاقات في استدعاء واحد |
Choose based on your needs — both formats access the same data.
اختر بناءً على احتياجاتك — كلا الصيغتين تصلان إلى البيانات نفسها.
## نقاط نهاية API
| Environment | Base URL |
| --------------- | ------------------------- |
| **Cloud** | `https://api.twenty.com/` |
| **Self-Hosted** | `https://{your-domain}/` |
| البيئة | عنوان URL الأساسي |
| --------------------- | ------------------------- |
| **السحابة** | `https://api.twenty.com/` |
| **الاستضافة الذاتية** | `https://{your-domain}/` |
## المصادقة
Every API request requires an API key in the header:
كل طلب API يتطلب تضمين مفتاح API في رأس الطلب:
```
Authorization: Bearer YOUR_API_KEY
@@ -70,78 +70,78 @@ Authorization: Bearer YOUR_API_KEY
### قم بإنشاء مفتاح API
1. Go to **Settings → APIs & Webhooks**
2. Click **+ Create key**
3. Configure:
* **Name**: Descriptive name for the key
* **Expiration Date**: When the key expires
1. انتقل إلى **الإعدادات → APIs & Webhooks**
2. انقر على **+ إنشاء مفتاح**
3. التكوين:
* **الاسم**: اسم وصفي للمفتاح
* **تاريخ الانتهاء**: متى تنتهي صلاحية المفتاح
4. انقر على **حفظ**
5. **Copy immediately** — the key is only shown once
5. **انسخه فوراً** — يظهر المفتاح مرة واحدة فقط
<VimeoEmbed videoId="928786722" title="Creating API key" />
<VimeoEmbed videoId="928786722" title="إنشاء مفتاح API" />
<Warning>
Your API key grants access to sensitive data. Don't share it with untrusted services. If compromised, disable it immediately and generate a new one.
يمنح مفتاح API الخاص بك الوصول إلى بيانات حساسة. لا تشاركه مع خدمات غير موثوقة. إذا تم اختراقه، عطّلْه فوراً وأنشئ مفتاحاً جديداً.
</Warning>
### Assign a Role to an API Key
### تعيين دور لمفتاح API
For better security, assign a specific role to limit access:
لتحسين الأمان، عيّن دوراً محدداً لتقييد الوصول:
1. اذهب إلى **الإعدادات → الأدوار**
2. Click on the role to assign
2. انقر على الدور الذي ترغب في تعيينه
3. افتح علامة التبويب **التعيين**
4. Under **API Keys**, click **+ Assign to API key**
5. Select the API key
4. ضمن **مفاتيح API**، انقر على **+ تعيين إلى مفتاح API**
5. حدد مفتاح API
The key will inherit that role's permissions. See [Permissions](/l/ar/user-guide/permissions-access/capabilities/permissions) for details.
سيرث المفتاح أذونات ذلك الدور. راجع [الأذونات](/l/ar/user-guide/permissions-access/capabilities/permissions) للحصول على التفاصيل.
### إدارة مفاتيح API
**Regenerate**: Settings → APIs & Webhooks → Click key → **Regenerate**
**إعادة التوليد**: الإعدادات → APIs & Webhooks → انقر على المفتاح → **إعادة التوليد**
**Delete**: Settings → APIs & Webhooks → Click key → **Delete**
**حذف**: الإعدادات → APIs & Webhooks → انقر على المفتاح → **حذف**
## API Playground
## ملعب واجهة برمجة التطبيقات
Test your APIs directly in the browser with our built-in playground — available for both **REST** and **GraphQL**.
اختبر واجهات برمجة التطبيقات لديك مباشرة في المتصفح باستخدام الملعب المدمج لدينا — متاح لكلٍ من **REST** و**GraphQL**.
### Access the Playground
### الوصول إلى الملعب
1. Go to **Settings → APIs & Webhooks**
2. Create an API key (required)
3. Click on **REST API** or **GraphQL API** to open the playground
1. انتقل إلى **الإعدادات → APIs & Webhooks**
2. أنشئ مفتاح API (مطلوب)
3. انقر على **REST API** أو **GraphQL API** لفتح الملعب
### What You Get
### ما الذي ستحصل عليه
* **Interactive documentation**: Generated for your specific data model
* **Live testing**: Execute real API calls against your workspace
* **Schema explorer**: Browse available objects, fields, and relationships
* **Request builder**: Construct queries with autocomplete
* **وثائق تفاعلية**: يتم إنشاؤها لنموذج البيانات المحدد لديك
* **اختبارات حيّة**: تنفيذ استدعاءات API فعلية على مساحة عملك
* **مستكشف المخطط**: تصفح الكائنات والحقول والعلاقات المتاحة
* **منشئ الطلبات**: أنشئ الاستعلامات مع الإكمال التلقائي
The playground reflects your custom objects and fields, so documentation is always accurate for your workspace.
يعكس الملعب الكائنات والحقول المخصصة لديك، لذا تكون الوثائق دائماً دقيقة لمساحة عملك.
## عمليات المجموعة
Both REST and GraphQL support batch operations:
كلٌ من REST وGraphQL يدعمان عمليات الدفعات:
* **حجم المجموعة**: حتى 60 سجل لكل طلب
* **Operations**: Create, update, delete multiple records
* **العمليات**: إنشاء وتحديث وحذف سجلات متعددة
**GraphQL-only features:**
**ميزات خاصة بـ GraphQL:**
* **Batch Upsert**: Create or update in one call
* Use plural object names (e.g., `CreateCompanies` instead of `CreateCompany`)
* **إدراج/تحديث دفعي**: إنشاء أو تحديث في استدعاء واحد
* استخدم الأسماء الجمع للكائنات (على سبيل المثال، `CreateCompanies` بدلاً من `CreateCompany`)
## Rate Limits
## حدود المعدل
API requests are throttled to ensure platform stability:
يتم تنظيم طلبات API لضمان استقرار المنصة:
| Limit | القيمة |
| -------------- | -------------------- |
| **Requests** | 100 calls per minute |
| **Batch size** | 60 records per call |
| الحد | القيمة |
| -------------- | ---------------------- |
| **الطلبات** | 100 استدعاء في الدقيقة |
| **حجم الدفعة** | 60 سجل لكل استدعاء |
<Tip>
Use batch operations to maximize throughput — process up to 60 records in a single API call instead of making individual requests.
استخدم عمليات الدفعات لزيادة الإنتاجية — عالج ما يصل إلى 60 سجلًا في استدعاء API واحد بدلاً من إجراء طلبات فردية.
</Tip>
@@ -36,32 +36,35 @@ npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
# Authenticate using your API key (you'll be prompted)
yarn auth
yarn auth:login
# Start dev mode: automatically syncs local changes to your workspace
yarn dev
yarn app:dev
```
من هنا يمكنك:
```bash filename="Terminal"
# Add a new entity to your application (guided)
yarn create-entity
yarn app:create-entity
# Generate a typed Twenty client and workspace entity types
yarn generate
yarn app:generate
# Run a onetime sync (instead of watch mode)
yarn sync
yarn app:sync
# Watch your application's functions logs
yarn logs
yarn function:logs
# Execute a function by name
yarn function:execute -n my-function -p '{"name": "test"}'
# Uninstall the application from the current workspace
yarn uninstall
yarn app:uninstall
# Display commands' help
yarn help
yarn app:help
```
راجع أيضًا: صفحات مرجع CLI لـ [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) و[twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
@@ -164,12 +167,13 @@ src/app/
ستضيف الأوامر اللاحقة مزيدًا من الملفات والمجلدات:
* `yarn generate` سيُنشئ مجلدًا `generated/` (عميل Twenty مضبوط الأنواع + أنواع مساحة العمل).
* `yarn create-entity` سيضيف ملفات تعريف الكيانات تحت `src/app/` لكائناتك المخصصة أو الوظائف أو الأدوار.
* `yarn app:generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn app:create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
l
## المصادقة
في المرة الأولى التي تشغل فيها `yarn auth`، سيُطلب منك إدخال:
The first time you run `yarn auth:login`, you'll be prompted for:
* عنوان URL لواجهة برمجة التطبيقات (الافتراضي http://localhost:3000 أو ملف تعريف مساحة العمل الحالية لديك)
* مفتاح واجهة برمجة التطبيقات
@@ -180,10 +184,10 @@ src/app/
```bash filename="Terminal"
# Login interactively (recommended)
yarn auth
yarn auth:login
# Use a specific workspace profile
yarn auth --workspace my-custom-workspace
yarn auth:login --workspace my-custom-workspace
```
## استخدم موارد SDK (الأنواع والتكوين)
@@ -282,7 +286,7 @@ export default defineObject({
* `universalIdentifier` يجب أن يكون فريدًا وثابتًا عبر عمليات النشر.
* يتطلب كل حقل `name` و`type` و`label` ومعرّف `universalIdentifier` ثابتًا خاصًا به.
* المصفوفة `fields` اختيارية — يمكنك تعريف كائنات بدون حقول مخصصة.
* يمكنك إنشاء كائنات جديدة باستخدام `yarn create-entity`، والذي يرشدك خلال التسمية والحقول والعلاقات.
* You can scaffold new objects using `yarn app:create-entity`, which guides you through naming, fields, and relationships.
<Note>
**يتم إنشاء الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، يضيف Twenty تلقائيًا حقولًا قياسية مثل `name` و`createdAt` و`updatedAt` و`createdBy` و`position` و`deletedAt`. لا تحتاج إلى تعريف هذه في مصفوفة `fields` — أضف فقط حقولك المخصصة.
@@ -448,18 +452,18 @@ export default defineRole({
```typescript
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
const handler = async (
params:
| { name?: string }
| RoutePayload
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
| CronPayload,
) => {
const client = new Twenty(); // generated typed client
const name = 'name' in params
? params.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
const name = 'name' in params.queryStringParameters
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
: 'Hello world';
const result = await client.mutation({
@@ -496,7 +500,8 @@ export default defineFunction({
{
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
eventName: 'person.updated',
updatedFields: ['name'],
},
],
});
@@ -509,23 +514,115 @@ export default defineFunction({
> مثال: `path: '/post-card/create',` -> الاستدعاء على `<APP_URL>/s/post-card/create`
* **cron**: يشغّل وظيفتك على جدول باستخدام تعبير CRON.
* **databaseEvent**: يعمل على أحداث دورة حياة كائنات مساحة العمل
* **databaseEvent**: Runs on workspace object lifecycle events. When the event operation is `updated`, specific fields to listen to can be specified in the `updatedFields` array. If left undefined or empty, any update will trigger the function.
> مثال: `person.created`
> e.g. `person.updated`
الملاحظات:
* المصفوفة `triggers` اختيارية. يمكن استخدام الوظائف بدون مشغلات كوظائف مساعدة تُستدعى بواسطة وظائف أخرى.
* يمكنك مزج أنواع متعددة من المشغلات في وظيفة واحدة.
### Route trigger payload
<Warning>
**Breaking change (v1.16, January 2026):** The route trigger payload format has changed. Prior to v1.16, query parameters, path parameters, and body were sent directly as the payload. Starting with v1.16, they are nested inside a structured `RoutePayload` object.
**Before v1.16:**
```typescript
const handler = async (params) => {
const { param1, param2 } = params; // Direct access
};
```
**After v1.16:**
```typescript
const handler = async (event: RoutePayload) => {
const { param1, param2 } = event.body; // Access via .body
const { queryParam } = event.queryStringParameters;
const { id } = event.pathParameters;
};
```
**To migrate existing functions:** Update your handler to destructure from `event.body`, `event.queryStringParameters`, or `event.pathParameters` instead of directly from the params object.
</Warning>
When a route trigger invokes your function, it receives a `RoutePayload` object that follows the AWS HTTP API v2 format. Import the type from `twenty-sdk`:
```typescript
import { defineFunction, type RoutePayload } from 'twenty-sdk';
const handler = async (event: RoutePayload) => {
// Access request data
const { headers, queryStringParameters, pathParameters, body } = event;
// HTTP method and path are available in requestContext
const { method, path } = event.requestContext.http;
return { message: 'Success' };
};
```
The `RoutePayload` type has the following structure:
| Property | النوع | الوصف |
| ---------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `headers` | `Record<string, string \| undefined>` | HTTP headers (only those listed in `forwardedRequestHeaders`) |
| `queryStringParameters` | `Record<string, string \| undefined>` | Query string parameters (multiple values joined with commas) |
| `pathParameters` | `Record<string, string \| undefined>` | Path parameters extracted from the route pattern (e.g., `/users/:id` → `{ id: '123' }`) |
| `المحتوى` | `object \| null` | Parsed request body (JSON) |
| `isBase64Encoded` | `قيمة منطقية` | Whether the body is base64 encoded |
| `requestContext.http.method` | `string` | HTTP method (GET, POST, PUT, PATCH, DELETE) |
| `requestContext.http.path` | `string` | Raw request path |
### Forwarding HTTP headers
By default, HTTP headers from incoming requests are **not** passed to your serverless function for security reasons. To access specific headers, explicitly list them in the `forwardedRequestHeaders` array:
```typescript
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'webhook-handler',
handler,
triggers: [
{
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/webhook',
httpMethod: 'POST',
isAuthRequired: false,
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
},
],
});
```
In your handler, you can then access these headers:
```typescript
const handler = async (event: RoutePayload) => {
const signature = event.headers['x-webhook-signature'];
const contentType = event.headers['content-type'];
// Validate webhook signature...
return { received: true };
};
```
<Note>
Header names are normalized to lowercase. Access them using lowercase keys (for example, `event.headers['content-type']`).
</Note>
يمكنك إنشاء وظائف جديدة بطريقتين:
* **مُنشأ بالقالب**: شغّل `yarn create-entity` واختر خيار إضافة وظيفة جديدة. يُولّد هذا ملفًا مبدئيًا مع معالج وتكوين.
* **Scaffolded**: Run `yarn app:create-entity` and choose the option to add a new function. يُولّد هذا ملفًا مبدئيًا مع معالج وتكوين.
* **يدوي**: أنشئ ملفًا جديدًا `*.function.ts` واستخدم `defineFunction()` مع اتباع النمط نفسه.
### عميل مُولَّد مضبوط الأنواع
شغّل yarn generate لإنشاء عميل محلي مضبوط الأنواع في generated/ استنادًا إلى مخطط مساحة العمل لديك. استخدمه في وظائفك:
Run yarn app:generate to create a local typed client in generated/ based on your workspace schema. استخدمه في وظائفك:
```typescript
import Twenty from './generated';
@@ -534,7 +631,7 @@ const client = new Twenty();
const { me } = await client.query({ me: { id: true, displayName: true } });
```
يُعاد توليد العميل بواسطة `yarn generate`. أعِد تشغيله بعد تغيير كائناتك وتشغيل `yarn sync` أو عند الانضمام إلى مساحة عمل جديدة.
The client is re-generated by `yarn app:generate`. Re-run after changing your objects and `yarn app:sync` or when onboarding to a new workspace.
#### بيانات الاعتماد في وقت التشغيل في الوظائف بلا خادم
@@ -578,13 +675,13 @@ yarn add -D twenty-sdk
}
```
يمكنك الآن تشغيل الأوامر نفسها عبر Yarn، مثل `yarn dev` و`yarn sync`، إلخ.
Now you can run the same commands via Yarn, e.g. `yarn app:dev`, `yarn app:sync`, etc.
## استكشاف الأخطاء وإصلاحها
* أخطاء المصادقة: شغّل `yarn auth` وتأكد من أن مفتاح واجهة برمجة التطبيقات لديك يمتلك الأذونات المطلوبة.
* Authentication errors: run `yarn auth:login` and ensure your API key has the required permissions.
* يتعذّر الاتصال بالخادم: تحقق من عنوان URL لواجهة البرمجة وأن خادم Twenty قابل للوصول.
* الأنواع أو العميل مفقود/قديم: شغّل `yarn generate` ثم `yarn dev`.
* وضع التطوير لا يزامن: تأكد من أن `yarn dev` قيد التشغيل وأن التغييرات ليست متجاهلة من بيئتك.
* Types or client missing/outdated: run `yarn app:generate` and then `yarn app:dev`.
* Dev mode not syncing: ensure `yarn app:dev` is running and that changes are not ignored by your environment.
قناة المساعدة على Discord: https://discord.com/channels/1130383047699738754/1130386664812982322
@@ -1,44 +1,44 @@
---
title: الويب هوكس
description: Receive real-time notifications when events occur in your CRM.
description: استقبل إشعارات في الوقت الفعلي عند وقوع أحداث في نظام إدارة علاقات العملاء (CRM) الخاص بك.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Webhooks push data to your systems in real-time when events occur in Twenty — no polling required. Use them to keep external systems in sync, trigger automations, or send alerts.
تدفع خطافات الويب البيانات إلى أنظمتك في الوقت الفعلي عند وقوع أحداث في Twenty — دون الحاجة إلى الاستطلاع الدوري. استخدمها للحفاظ على تزامن الأنظمة الخارجية، وتشغيل الأتمتة، أو إرسال التنبيهات.
## إنشاء ربط ويب
1. Go to **Settings → APIs & Webhooks → Webhooks**
1. انتقل إلى **الإعدادات → APIs & Webhooks → Webhooks**
2. انقر على **+ إنشاء ربط ويب**
3. Enter your webhook URL (must be publicly accessible)
3. أدخل عنوان URL لخطاف الويب الخاص بك (يجب أن يكون قابلاً للوصول علنًا)
4. انقر على **حفظ**
The webhook activates immediately and starts sending notifications.
يتم تفعيل خطاف الويب فورًا ويبدأ في إرسال الإشعارات.
<VimeoEmbed videoId="928786708" title="Creating a webhook" />
<VimeoEmbed videoId="928786708" title="إنشاء خطاف ويب" />
### إدارة Webhooks
**Edit**: Click the webhook → Update URL → **Save**
**تحرير**: انقر على خطاف الويب → تحديث عنوان URL → **حفظ**
**Delete**: Click the webhook → **Delete** → Confirm
**حذف**: انقر على خطاف الويب → **حذف** → تأكيد
## الأحداث
Twenty sends webhooks for these event types:
يرسل Twenty خطافات الويب لأنواع الأحداث التالية:
| حدث | مثال |
| ------------------ | ---------------------------------------------------------- |
| **Record Created** | `person.created`, `company.created`, `note.created` |
| **Record Updated** | `person.updated`, `company.updated`, `opportunity.updated` |
| **Record Deleted** | `person.deleted`, `company.deleted` |
| حدث | مثال |
| --------------- | ---------------------------------------------------------- |
| **إنشاء سجل** | `person.created`, `company.created`, `note.created` |
| **تحديث السجل** | `person.updated`, `company.updated`, `opportunity.updated` |
| **حذف السجل** | `person.deleted`, `company.deleted` |
All event types are sent to your webhook URL. Event filtering may be added in future releases.
يتم إرسال جميع أنواع الأحداث إلى عنوان URL لخطاف الويب الخاص بك. قد تتم إضافة تصفية الأحداث في الإصدارات المستقبلية.
## Payload Format
## تنسيق الحمولة
Each webhook sends an HTTP POST with a JSON body:
يرسل كل خطاف ويب طلب HTTP من نوع POST يتضمن جسمًا بصيغة JSON:
```json
{
@@ -55,35 +55,35 @@ Each webhook sends an HTTP POST with a JSON body:
}
```
| الحقل | الوصف |
| --------------- | ------------------------------------------------ |
| `حدث` | What happened (e.g., `person.created`) |
| `بيانات` | The full record that was created/updated/deleted |
| `الطابع الزمني` | When the event occurred (UTC) |
| الحقل | الوصف |
| --------------- | ----------------------------------------------- |
| `حدث` | ما الذي حدث (على سبيل المثال، `person.created`) |
| `بيانات` | السجل الكامل الذي تم إنشاؤه/تحديثه/حذفه |
| `الطابع الزمني` | وقت حدوث الحدث (UTC) |
<Note>
Respond with a **2xx HTTP status** (200-299) to acknowledge receipt. Non-2xx responses are logged as delivery failures.
استجب بحالة **HTTP 2xx** (200-299) لتأكيد الاستلام. تُسجَّل الاستجابات غير 2xx كإخفاقات في التسليم.
</Note>
## Webhook Validation
## التحقق من صحة خطاف الويب
Twenty signs each webhook request for security. Validate signatures to ensure requests are authentic.
يقوم Twenty بتوقيع كل طلب خطاف ويب لأغراض الأمان. تحقّق من التواقيع للتأكد من أن الطلبات أصيلة.
### Headers
### الرؤوس
| رأس الصفحة | الوصف |
| ---------------------------- | --------------------- |
| `X-Twenty-Webhook-Signature` | HMAC SHA256 signature |
| `X-Twenty-Webhook-Timestamp` | Request timestamp |
| رأس الصفحة | الوصف |
| ---------------------------- | ------------------- |
| `X-Twenty-Webhook-Signature` | توقيع HMAC SHA256 |
| `X-Twenty-Webhook-Timestamp` | الطابع الزمني للطلب |
### Validation Steps
### خطوات التحقق
1. Get the timestamp from `X-Twenty-Webhook-Timestamp`
2. Create the string: `{timestamp}:{JSON payload}`
3. Compute HMAC SHA256 using your webhook secret
4. Compare with `X-Twenty-Webhook-Signature`
1. احصل على الطابع الزمني من `X-Twenty-Webhook-Timestamp`
2. أنشئ السلسلة: `{timestamp}:{JSON payload}`
3. احسب HMAC SHA256 باستخدام سر خطاف الويب الخاص بك
4. قارِن مع `X-Twenty-Webhook-Signature`
### Example (Node.js)
### مثال (Node.js)
```javascript
const crypto = require("crypto");
@@ -101,12 +101,12 @@ const expectedSignature = crypto
const isValid = expectedSignature === req.headers["x-twenty-webhook-signature"];
```
## Webhooks vs Workflows
## خطافات الويب مقابل سير العمل
| طريقة | الاتجاه | Use Case |
| ---------------------------- | ------- | ---------------------------------------------------------- |
| **Webhooks** | OUT | Automatically notify external systems of any record change |
| **Workflow + HTTP Request** | OUT | Send data out with custom logic (filters, transformations) |
| **Workflow Webhook Trigger** | IN | Receive data into Twenty from external systems |
| طريقة | الاتجاه | حالة الاستخدام |
| ----------------------------- | ------- | ----------------------------------------------------------- |
| **خطافات الويب** | OUT | إخطار الأنظمة الخارجية تلقائيًا بأي تغيير في السجل |
| **سير العمل + طلب HTTP** | OUT | إرسال البيانات إلى الخارج بمنطق مخصص (عوامل تصفية، تحويلات) |
| **مشغّل خطاف ويب لسير العمل** | IN | استقبال البيانات في Twenty من الأنظمة الخارجية |
For receiving external data, see [Set Up a Webhook Trigger](/l/ar/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
لاستقبال البيانات الخارجية، راجع [إعداد مشغّل خطاف الويب](/l/ar/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
@@ -1,6 +1,6 @@
---
title: Extend
description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
title: التوسيع
description: وسّع وظائف Twenty باستخدام واجهات برمجة التطبيقات، وخطافات الويب، والتطبيقات المخصصة.
---
<Frame>
@@ -9,26 +9,26 @@ description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
## نظرة عامة
Twenty is designed to be extensible. Use our APIs, webhooks, and app framework to integrate with your existing tools and build custom functionality.
تم تصميم Twenty ليكون قابلاً للتوسعة. استخدم واجهات برمجة التطبيقات وخطافات الويب وإطار عمل التطبيقات لدينا للتكامل مع أدواتك الحالية وبناء وظائف مخصصة.
## What You Can Do
## ما الذي يمكنك فعله
* **APIs**: Query and modify your CRM data programmatically using REST or GraphQL
* **Webhooks**: Receive real-time notifications when events occur in Twenty
* **Apps**: Build custom applications that extend Twenty's capabilities - Coming soon!
* **واجهات برمجة التطبيقات**: استعلم وعدّل بيانات إدارة علاقات العملاء (CRM) لديك برمجياً باستخدام REST أو GraphQL
* **خطافات الويب**: استقبل إشعارات في الوقت الفعلي عند وقوع أحداث في Twenty
* **التطبيقات**: أنشئ تطبيقات مخصصة توسّع قدرات Twenty - قريباً!
## البدء
<CardGroup cols={٢}>
<Card title="واجهات برمجة التطبيقات" icon="كود" href="/l/ar/developers/extend/capabilities/apis">
Connect to Twenty programmatically
اتصل بـ Twenty برمجياً
</Card>
<Card title="الويب هوكس" icon="bell" href="/l/ar/developers/extend/capabilities/webhooks">
Get notified of events in real-time
احصل على إشعارات بالأحداث في الوقت الفعلي
</Card>
<Card title="Apps" icon="puzzle-piece" href="/l/ar/developers/extend/capabilities/apps">
Build customizations as code (Alpha)
<Card title="التطبيقات" icon="puzzle-piece" href="/l/ar/developers/extend/capabilities/apps">
أنشئ تخصيصات كرمز برمجي (ألفا)
</Card>
</CardGroup>
@@ -1,23 +1,23 @@
---
title: البدء
description: Welcome to Twenty Developer Documentation, your resources for extending, self-hosting, and contributing to Twenty.
description: مرحبًا بك في وثائق المطوّرين الخاصة بـ Twenty، مرجعك للتوسيع والاستضافة الذاتية والمساهمة في Twenty.
---
import { CardTitle } from "/snippets/card-title.mdx"
<CardGroup cols={٣}>
<Card href="/l/ar/developers/extend/extend" img="/images/user-guide/integrations/plug.png">
<CardTitle>Extend</CardTitle>
Build integrations with APIs, webhooks, and custom apps.
<CardTitle>التوسيع</CardTitle>
أنشئ عمليات تكامل مع واجهات برمجة التطبيقات وخطافات الويب والتطبيقات المخصصة.
</Card>
<Card href="/l/ar/developers/self-host/self-host" img="/images/user-guide/what-is-twenty/20.png">
<CardTitle>Self-Host</CardTitle>
Deploy and manage Twenty on your own infrastructure.
<CardTitle>الاستضافة الذاتية</CardTitle>
قم بنشر Twenty وإدارته على البنية التحتية الخاصة بك.
</Card>
<Card href="/l/ar/developers/contribute/contribute" img="/images/user-guide/github/github-header.png">
<CardTitle>Contribute</CardTitle>
Join our open-source community and contribute to Twenty.
<CardTitle>المساهمة</CardTitle>
انضم إلى مجتمعنا مفتوح المصدر وساهم في Twenty.
</Card>
</CardGroup>
@@ -8,7 +8,7 @@ title: طرق أخرى
## Kubernetes عبر Terraform والمخططات
Community-led documentation for Kubernetes deployment is available [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-docker/k8s)
يتوفر توثيق يقوده المجتمع لعملية نشر Kubernetes [هنا](https://github.com/twentyhq/twenty/tree/main/packages/twenty-docker/k8s)
### Coolify
@@ -42,4 +42,4 @@ Community-led documentation for Kubernetes deployment is available [here](https:
## أخرى
Please feel free to Open a PR to add more Cloud Provider options.
لا تتردد في فتح طلب سحب لإضافة المزيد من خيارات موفّري السحابة.
@@ -1,5 +1,5 @@
---
title: 1-Click w/ Docker Compose
title: بنقرة واحدة مع Docker Compose
---
<Warning>
@@ -44,7 +44,7 @@ VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.
1. **إنشاء ملف .env**
Copy the example environment file to a new .env file in your working directory:
انسخ ملف البيئة النموذجي إلى ملف .env جديد في دليل العمل لديك:
```bash
curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example
@@ -86,7 +86,7 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/ref
### الخطوة 3: إطلاق التطبيق
Start the Docker containers:
بدء حاويات Docker:
```bash
docker compose up -d
@@ -94,9 +94,9 @@ docker compose up -d
### الخطوة 4: الوصول إلى التطبيق
If you host twentyCRM on your own computer, open your browser and navigate to [http://localhost:3000](http://localhost:3000).
إذا كنت تستضيف twentyCRM على جهازك، فافتح المتصفح وانتقل إلى [http://localhost:3000](http://localhost:3000).
If you host it on a server, check that the server is running and that everything is ok with
إذا كنت تستضيفه على خادم، فتحقق من أن الخادم يعمل وأن كل شيء على ما يرام باستخدام
```bash
curl http://localhost:3000
@@ -186,66 +186,66 @@ SSL (HTTPS) مطلوب لعمل ميزات معينة في المتصفح بشك
* **إعدادات الجدار الناري:**
Open necessary ports in your firewall to allow external access.
افتح المنافذ اللازمة في جدار الحماية للسماح بالوصول الخارجي.
* **التناسق:**
يجب أن يتطابق `SERVER_URL` مع طريقة وصول المستخدمين إلى تطبيقك في متصفحاتهم.
#### Persistence
#### استمرارية البيانات
* **أحجام البيانات:**
The Docker Compose configuration uses volumes to persist data for the database and server storage.
يستخدم تكوين Docker Compose وحدات التخزين للاحتفاظ بالبيانات لقاعدة البيانات وتخزين الخادم.
* **البيئات غير المرتبطة بالحالة:**
إذا كنت تقوم بالنشر إلى بيئة غير مرتبطة بالحالة (مثل بعض خدمات السحابة)، فقم بتكوين التخزين الخارجي لحفظ البيانات.
## Backup and Restore
## النسخ الاحتياطي والاستعادة
Regular backups protect your CRM data from loss.
تحمي النسخ الاحتياطية المنتظمة بيانات نظام إدارة علاقات العملاء لديك من الفقدان.
### Create a Database Backup
### إنشاء نسخة احتياطية لقاعدة البيانات
```bash
docker exec twenty-postgres pg_dump -U postgres twenty > backup_$(date +%Y%m%d).sql
```
### Automate Daily Backups
### أتمتة النسخ الاحتياطية اليومية
Add to your crontab (`crontab -e`):
أضف إلى crontab لديك (`crontab -e`):
```bash
0 2 * * * docker exec twenty-postgres pg_dump -U postgres twenty > /backups/twenty_$(date +\%Y\%m\%d).sql
```
### Restore from Backup
### الاستعادة من نسخة احتياطية
1. Stop the application:
1. إيقاف التطبيق:
```bash
docker compose stop twenty-server twenty-front
```
2. Restore the database:
2. استعادة قاعدة البيانات:
```bash
docker exec -i twenty-postgres psql -U postgres twenty < backup_20240115.sql
```
3. Restart services:
3. إعادة تشغيل الخدمات:
```bash
docker compose up -d
```
### Backup Best Practices
### أفضل ممارسات النسخ الاحتياطي
* **Test restores regularly** — verify backups actually work
* **Store backups off-site** — use cloud storage (S3, GCS, etc.)
* **Encrypt sensitive data** — protect backups with encryption
* **Retain multiple copies** — keep daily, weekly, and monthly backups
* **اختبر عمليات الاستعادة بانتظام** — تحقق من أن النسخ الاحتياطية تعمل فعلاً
* **خزّن النسخ الاحتياطية خارج الموقع** — استخدم التخزين السحابي (S3، GCS، إلخ.)
* **شفّر البيانات الحساسة** — قم بحماية النسخ الاحتياطية بالتشفير
* **احتفِظ بنسخ متعددة** — احتفِظ بنسخ احتياطية يومية وأسبوعية وشهرية
## استكشاف الأخطاء وإصلاحها
@@ -60,61 +60,61 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=false
3. أعد تشغيل الحاويات لتصبح التغييرات نافذة
4. ستظهر لوحة الإدارة القيم الحالية لكنها لن تتمكن من تعديلها
## Multi-Workspace Mode
## وضع تعدد مساحات العمل
By default, Twenty runs in **single-workspace mode** — ideal for most self-hosted deployments where you need one CRM instance for your organization.
افتراضيًا، تعمل Twenty في **وضع مساحة عمل واحدة** — وهو مثالي لمعظم عمليات النشر المستضافة ذاتيًا حيث تحتاج إلى مثيل واحد من نظام إدارة علاقات العملاء لمؤسستك.
### Single-Workspace Mode (Default)
### وضع مساحة العمل الواحدة (الافتراضي)
```bash
IS_MULTIWORKSPACE_ENABLED=false # default
```
* One workspace per Twenty instance
* First user automatically becomes admin with full privileges (`canImpersonate` and `canAccessFullAdminPanel`)
* New signups are disabled after the first workspace is created
* Simple URL structure: `https://your-domain.com`
* مساحة عمل واحدة لكل مثيل من Twenty
* أول مستخدم يصبح تلقائيًا مشرفًا مع صلاحيات كاملة (`canImpersonate` و`canAccessFullAdminPanel`)
* يتم تعطيل عمليات التسجيل الجديدة بعد إنشاء مساحة العمل الأولى
* بنية URL بسيطة: `https://your-domain.com`
### Enabling Multi-Workspace Mode
### تمكين وضع تعدد مساحات العمل
```bash
IS_MULTIWORKSPACE_ENABLED=true
DEFAULT_SUBDOMAIN=app # default value
```
Enable multi-workspace mode for SaaS-like deployments where multiple independent teams need their own workspaces on the same Twenty instance.
قم بتمكين وضع تعدد مساحات العمل لعمليات النشر المشابهة لـ SaaS حيث تحتاج عدة فرق مستقلة إلى مساحات عمل خاصة بها على نفس مثيل Twenty.
**Key differences from single-workspace mode:**
**الاختلافات الرئيسية مقارنة بوضع مساحة العمل الواحدة:**
* Multiple workspaces can be created on the same instance
* Each workspace gets its own subdomain (e.g., `sales.your-domain.com`, `marketing.your-domain.com`)
* Users sign up and log in at `{DEFAULT_SUBDOMAIN}.your-domain.com` (e.g., `app.your-domain.com`)
* No automatic admin privileges — first user in each workspace is a regular user
* Workspace-specific settings like subdomain and custom domain become available in workspace settings
* يمكن إنشاء مساحات عمل متعددة على نفس المثيل
* تحصل كل مساحة عمل على نطاق فرعي خاص بها (مثل: `sales.your-domain.com`، `marketing.your-domain.com`)
* يسجل المستخدمون ويقومون بتسجيل الدخول عبر `{DEFAULT_SUBDOMAIN}.your-domain.com` (مثل: `app.your-domain.com`)
* لا توجد صلاحيات مشرف تلقائية — أول مستخدم في كل مساحة عمل يكون مستخدمًا عاديًا
* إعدادات خاصة بمساحة العمل مثل النطاق الفرعي والنطاق المخصص تصبح متاحة ضمن إعدادات مساحة العمل
<Warning>
**Environment-only setting:** `IS_MULTIWORKSPACE_ENABLED` can only be configured via `.env` file and requires a restart. It cannot be changed through the admin panel.
**إعداد خاص بالبيئة فقط:** لا يمكن تكوين `IS_MULTIWORKSPACE_ENABLED` إلا عبر ملف `.env` ويتطلب إعادة تشغيل. لا يمكن تغييره عبر لوحة الإدارة.
</Warning>
### DNS Configuration for Multi-Workspace
### تكوين DNS لوضع تعدد مساحات العمل
When using multi-workspace mode, configure your DNS with a wildcard record to allow dynamic subdomain creation:
عند استخدام وضع تعدد مساحات العمل، قم بتكوين DNS لديك بسجل wildcard للسماح بإنشاء النطاقات الفرعية ديناميكيًا:
```
*.your-domain.com -> your-server-ip
```
This enables automatic subdomain routing for new workspaces without manual DNS configuration.
يُمكّن هذا توجيه النطاقات الفرعية تلقائيًا لمساحات العمل الجديدة دون تكوين DNS يدوي.
### Restricting Workspace Creation
### تقييد إنشاء مساحات العمل
In multi-workspace mode, you may want to limit who can create new workspaces:
في وضع تعدد مساحات العمل، قد ترغب في تقييد من يمكنه إنشاء مساحات عمل جديدة:
```bash
IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
```
When enabled, only users with `canAccessFullAdminPanel` can create additional workspaces. Users can still create their first workspace during initial signup.
عند التمكين، يمكن فقط للمستخدمين الذين لديهم `canAccessFullAdminPanel` إنشاء مساحات عمل إضافية. لا يزال بإمكان المستخدمين إنشاء مساحة العمل الأولى الخاصة بهم أثناء التسجيل الأولي.
## تكامل Gmail و Google Calendar
@@ -133,8 +133,8 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
1. اذهب إلى [بيانات اعتماد](https://console.cloud.google.com/apis/credentials)
2. قم بإنشاء معرف عميل OAuth 2.0
3. أضف هذه الـ URIs لإعادة التوجيه:
* `https://{your-domain}/auth/google/redirect` (for SSO)
* `https://{your-domain}/auth/google-apis/get-access-token` (for integrations)
* `https://{your-domain}/auth/google/redirect` (لتسجيل الدخول الفردي)
* `https://{your-domain}/auth/google-apis/get-access-token` (للتكاملات)
### الإعداد في Twenty
@@ -194,13 +194,13 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
ستحتاج إلى إضافة URIs التالية لإعادة التوجيه إلى مشروعك:
* `https://{your-domain}/auth/microsoft/redirect` if you want to use Microsoft SSO
* `https://{your-domain}/auth/microsoft/redirect` إذا كنت تريد استخدام تسجيل الدخول الفردي في Microsoft
* `https://{your-domain}/auth/microsoft-apis/get-access-token`
### الإعداد في Twenty
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
2. Find the **Microsoft Auth** section
2. ابحث عن قسم **Microsoft Auth**
3. حدد هذه المتغيرات:
* `MESSAGING_PROVIDER_MICROSOFT_ENABLED=true`
* `CALENDAR_PROVIDER_MICROSOFT_ENABLED=true`
@@ -214,7 +214,7 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
</Warning>
### Configure scopes
### تكوين النطاقات
[انظر الشيفرة المصدرية ذات الصلة](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/auth/utils/get-microsoft-apis-oauth-scopes.ts#L2-L9)
@@ -232,7 +232,7 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
أضف مستخدمي الاختبار إلى قسم "المستخدمون والمجموعات".
## Background Jobs for Calendar & Messaging
## مهام الخلفية للتقويم والمراسلة
بعد إعداد تكامل Gmail، أو Google Calendar، أو Microsoft 365، تحتاج إلى بدء وظائف الخلفية التي تقوم بمزامنة البيانات.
@@ -252,7 +252,7 @@ yarn command:prod cron:workflow:automated-cron-trigger
## تكوين البريد الإلكتروني
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
2. Find the **Email** section
2. ابحث عن قسم **Email**
3. قم بضبط إعدادات SMTP الخاصة بك:
<ArticleTabs label1="جيميل" label2="أوفيس 365" label3="Smtp4dev">
@@ -291,3 +291,47 @@ yarn command:prod cron:workflow:automated-cron-trigger
<Warning>
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
</Warning>
## Serverless Functions
Twenty supports serverless functions for workflows and custom logic. The execution environment is configured via the `SERVERLESS_TYPE` environment variable.
<Warning>
**Security Notice:** The local serverless driver (`SERVERLESS_TYPE=LOCAL`) runs code directly on the host in a Node.js process with no sandboxing. It should only be used for trusted code in development. For production deployments handling untrusted code, we highly recommend using `SERVERLESS_TYPE=LAMBDA` or `SERVERLESS_TYPE=DISABLED`.
</Warning>
### Available Drivers
| Driver | Environment Variable | حالة الاستخدام | Security Level |
| -------- | -------------------------- | ------------------------------------- | ------------------------------- |
| Disabled | `SERVERLESS_TYPE=DISABLED` | Disable serverless functions entirely | N/A |
| Local | `SERVERLESS_TYPE=LOCAL` | Development and trusted environments | Low (no sandboxing) |
| Lambda | `SERVERLESS_TYPE=LAMBDA` | Production with untrusted code | High (hardware-level isolation) |
### Recommended Configuration
**For development:**
```bash
SERVERLESS_TYPE=LOCAL # default
```
**For production (AWS):**
```bash
SERVERLESS_TYPE=LAMBDA
SERVERLESS_LAMBDA_REGION=us-east-1
SERVERLESS_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role
SERVERLESS_LAMBDA_ACCESS_KEY_ID=your-access-key
SERVERLESS_LAMBDA_SECRET_ACCESS_KEY=your-secret-key
```
**To disable serverless functions:**
```bash
SERVERLESS_TYPE=DISABLED
```
<Note>
When using `SERVERLESS_TYPE=DISABLED`, any attempt to execute a serverless function will return an error. This is useful if you want to run Twenty without serverless function capabilities.
</Note>
@@ -37,7 +37,7 @@ git config --global core.autocrlf false
ثم قم بحذف المستودع واستنساخه مرة أخرى.
#### Missing metadata schema
#### مخطط البيانات الوصفية مفقود
أثناء تثبيت Twenty، تحتاج إلى توفير قاعدة بيانات بوستجريس الخاصة بك بالمخططات والإضافات والمستخدمين الصحيحة.
إذا نجح تشغيل هذا التخصيص، يجب أن تحتوي قاعدة البيانات لديك على المخططات `default` و`metadata`.
@@ -47,7 +47,7 @@ git config --global core.autocrlf false
عليك بناء حزمة `twenty-emails` قبل تشغيل تهيئة قاعدة البيانات باستخدام `npx nx run twenty-emails:build`.
#### Missing twenty-x package
#### حزمة twenty-x مفقودة
تأكد من تشغيل yarn في الدليل الجذر ثم تشغيل `npx nx server:dev twenty-server`. إذا لم يعمل ذلك، حاول بناء الحزمة المفقودة يدوياً.
@@ -67,11 +67,11 @@ git config --global core.autocrlf false
في `packages/twenty-front/.env` قم بإزالة تعليق على `VITE_DISABLE_TYPESCRIPT_CHECKER=true` و`VITE_DISABLE_ESLINT_CHECKER=true` لتعطيل فحوصات الخلفية مما يقلل من كمية الذاكرة المطلوبة.
**If it does not work:**
Run only the services you need, instead of `npx nx start`. على سبيل المثال، إذا كنت تعمل على الخادم، قم بتشغيل `npx nx worker twenty-server` فقط
**إذا لم يعمل ذلك:**
قم بتشغيل الخدمات التي تحتاجها فقط، بدلاً من `npx nx start`. على سبيل المثال، إذا كنت تعمل على الخادم، قم بتشغيل `npx nx worker twenty-server` فقط
**If it does not work:**
If you tried to run only `npx nx run twenty-server:start` on WSL and it's failing with the below memory error:
**إذا لم يعمل ذلك:**
إذا حاولت تشغيل `npx nx run twenty-server:start` فقط على WSL وفشل مع خطأ الذاكرة أدناه:
`FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`
@@ -82,11 +82,11 @@ If you tried to run only `npx nx run twenty-server:start` on WSL and it's failin
علامة --max-old-space-size=8192 تحدد حداً أقصى للذاكرة الخاصة بـ Node.js بحد أقصى 8GB؛ الاستخدام يتزايد بطلبات التطبيق.
المرجع: https://stackoverflow.com/questions/56982005/where-do-i-set-node-options-max-old-space-size-2048
**If it does not work:**
Investigate which processes are taking you most of your machine RAM. في Twenty، لاحظنا أن بعض إضافات VScode كانت تستهلك الكثير من الذاكرة لذا قمنا بتعطيلها مؤقتًا.
**إذا لم يعمل ذلك:**
تحقق من العمليات التي تستهلك معظم ذاكرة جهازك. في Twenty، لاحظنا أن بعض إضافات VScode كانت تستهلك الكثير من الذاكرة لذا قمنا بتعطيلها مؤقتًا.
**If it does not work:**
Restart your machine helps to clean up ghost processes.
**إذا لم يعمل ذلك:**
إعادة تشغيل جهازك تساعد في تنظيف العمليات الشبحية.
#### أثناء تشغيل `npx nx start` تظهر سجلات غريبة [0] و [1]
@@ -124,7 +124,7 @@ npx nx worker twenty-server
* افتح تطبيق Twenty الخاص بك، انتقل إلى `/settings`، وفعل التبديل المتقدم في الجزء السفلي الأيسر من الشاشة.
* إنشاء ويب هوك جديد.
* Paste **Your Unique Webhook URL** in the **Endpoint Url** field in Twenty. Set the **Filters** to `Companies` and `Created`.
* الصق **عنوان URL الفريد لخطّاف الويب الخاص بك** في الحقل **عنوان URL لنقطة النهاية** في Twenty. اضبط **عوامل التصفية** على `Companies` و`Created`.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/self-hosting/webhook-settings.jpg" alt="إعدادات الويب هوك" />
@@ -4,9 +4,9 @@ title: دليل الترقية
## إرشادات عامة
**Always make sure to back up your database before starting the upgrade process** by running `docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql`.
**تأكد دائماً من عمل نسخة احتياطية لقاعدة بياناتك قبل بدء عملية الترقية** عن طريق تشغيل `docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql`.
To restore backup, run `cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}`.
لاستعادة النسخة الاحتياطية، قم بتشغيل `cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}`.
إذا كنت تستخدم Docker Compose، اتبع الخطوات التالية:
@@ -46,7 +46,7 @@ yarn command:prod cache:flush
لم تعد بحاجة إلى تشغيل أي أمر، الصورة الجديدة ستعتني بتشغيل جميع الترحيلات المطلوبة تلقائيًا.
### `User does not have permission` error
### خطأ: `User does not have permission`
إذا واجهت أخطاء في الأذونات في معظم الطلبات بعد الترقية، فقد تحتاج إلى مسح التخزين المؤقت لإعادة حساب أحدث الأذونات.
@@ -316,8 +316,8 @@ yarn command:prod upgrade-0.31
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.30.0
**Breaking change**:
To enhance performances, Twenty now requires redis cache to be configured. قمنا بتحديث [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml) لتعكس ذلك.
**تغيير كبير**:
لتحسين الأداء، يتطلب Twenty الآن تكوين Redis للتخزين المؤقت. قمنا بتحديث [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml) لتعكس ذلك.
تأكد من تحديث إعدادات التكوين الخاصة بك وتحديث المتغيرات البيئية الخاصة بك وفقًا لذلك:
```
@@ -1,6 +1,6 @@
---
title: Self-Host
description: Deploy and manage Twenty on your own infrastructure.
title: الاستضافة الذاتية
description: قم بنشر Twenty وإدارته على البنية التحتية الخاصة بك.
---
<Frame>
@@ -9,22 +9,22 @@ description: Deploy and manage Twenty on your own infrastructure.
## نظرة عامة
Twenty can be self-hosted on your own infrastructure, giving you full control over your data and deployment.
يمكن استضافة Twenty ذاتيًا على البنية التحتية الخاصة بك، مما يمنحك تحكمًا كاملاً في بياناتك وعمليات النشر.
## Why Self-Host?
## لماذا الاستضافة الذاتية؟
* **Data ownership**: Keep all CRM data on your own servers
* **Compliance**: Meet regulatory requirements for data residency
* **Customization**: Full access to modify and extend the platform
* **ملكية البيانات**: احتفظ بجميع بيانات إدارة علاقات العملاء (CRM) على خوادمك الخاصة
* **الامتثال**: تلبية المتطلبات التنظيمية الخاصة بإقامة البيانات
* **التخصيص**: وصول كامل إلى تعديل المنصة وتوسيعها
## البدء
<CardGroup cols={٢}>
<Card title="Docker Compose" icon="docker" href="/l/ar/developers/self-host/capabilities/docker-compose">
Quick setup with Docker
إعداد سريع باستخدام Docker
</Card>
<Card title="Cloud Providers" icon="cloud" href="/l/ar/developers/self-host/capabilities/cloud-providers">
Deploy on AWS, GCP, or Azure
<Card title="مزودو الخدمات السحابية" icon="cloud" href="/l/ar/developers/self-host/capabilities/cloud-providers">
انشر على AWS أو GCP أو Azure
</Card>
</CardGroup>
+39 -39
View File
@@ -1,16 +1,16 @@
{
"tabs": {
"userGuide": {
"label": "User Guide",
"label": "دليل المستخدم",
"groups": {
"discoverTwenty": {
"label": "Discover Twenty",
"label": "اكتشف Twenty",
"groups": {
"gettingStartedCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"gettingStartedHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
@@ -18,32 +18,32 @@
"label": "نموذج البيانات",
"groups": {
"dataModelCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"dataModelHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
"dataMigration": {
"label": "Data Migration",
"label": "ترحيل البيانات",
"groups": {
"dataMigrationCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"dataMigrationHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
"calendarEmails": {
"label": "Calendar & Emails",
"label": "التقويم والبريد الإلكتروني",
"groups": {
"calendarEmailsCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"calendarEmailsHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
@@ -51,22 +51,22 @@
"label": "سير العمل",
"groups": {
"workflowsCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"workflowsHowTos": {
"label": "How-Tos",
"label": "الإرشادات",
"groups": {
"crmAutomations": {
"label": "CRM Automations"
"label": "أتمتة إدارة علاقات العملاء"
},
"connectToOtherTools": {
"label": "Connect to Other Tools"
"label": "الاتصال بأدوات أخرى"
},
"advancedConfigurations": {
"label": "Advanced Configurations"
"label": "التكوينات المتقدمة"
},
"needMoreHelp": {
"label": "Need More Help"
"label": "المزيد من المساعدة"
}
}
}
@@ -76,21 +76,21 @@
"label": "الذكاء الاصطناعي",
"groups": {
"aiCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"aiHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
"viewsPipelines": {
"label": "Views & Pipelines",
"label": "طرق العرض والمسارات",
"groups": {
"viewsPipelinesCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"viewsPipelinesHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
@@ -98,21 +98,21 @@
"label": "لوحات القيادة",
"groups": {
"dashboardsCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"dashboardsHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
"permissionsAccess": {
"label": "Permissions & Access",
"label": "الصلاحيات والوصول",
"groups": {
"permissionsAccessCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"permissionsAccessHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
@@ -120,10 +120,10 @@
"label": "الفوترة",
"groups": {
"billingCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"billingHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
},
@@ -131,10 +131,10 @@
"label": "\\ا\\ل\\إ\\ع\\د\\ا\\د\\ا\\ت",
"groups": {
"settingsCapabilities": {
"label": "Capabilities"
"label": "القدرات"
},
"settingsHowTos": {
"label": "How-Tos"
"label": "الإرشادات"
}
}
}
@@ -147,26 +147,26 @@
"label": "المطورون"
},
"extend": {
"label": "Extend",
"label": "التوسيع",
"groups": {
"extendCapabilities": {
"label": "Capabilities"
"label": "القدرات"
}
}
},
"selfHost": {
"label": "Self-Host",
"label": "الاستضافة الذاتية",
"groups": {
"selfHostCapabilities": {
"label": "Capabilities"
"label": "القدرات"
}
}
},
"contribute": {
"label": "Contribute",
"label": "المساهمة",
"groups": {
"contributeCapabilities": {
"label": "Capabilities",
"label": "القدرات",
"groups": {
"frontendDevelopment": {
"label": "تطوير الواجهة الأمامية",
@@ -184,7 +184,7 @@
"label": "إدخال"
},
"navigation": {
"label": "Navigation"
"label": "التنقل"
}
}
}
@@ -18,12 +18,12 @@ image: /images/user-guide/tips/light-bulb.png
return (
<>
<p id="hoverText" style={{ display: "inline-block" }}>
Customer Insights
رؤى العملاء
</p>
<AppTooltip
className
anchorSelect="#hoverText"
content="Explore customer behavior and preferences"
content="استكشاف سلوك العملاء وتفضيلاتهم"
delayHide={0}
offset={6}
noArrow={false}
@@ -38,21 +38,21 @@ image: /images/user-guide/tips/light-bulb.png
</Tab>
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
| اختيار الربط | محدد CSS | Selector for the tooltip anchor (the element that triggers the tooltip) |
| المحتوى | نص | The content you want to display within the tooltip |
| تأخير الإخفاء | رقم | The delay in seconds before hiding the tooltip after the cursor leaves the anchor |
| الإزاحة | رقم | The offset in pixels for positioning the tooltip |
| بدون سهم | قيمة منطقية | إذا كانت القيمة `صحيح`, سيتم إخفاء السهم في المربط التنبيهي |
| مفتوح | قيمة منطقية | إذا كانت القيمة `صحيح`, يكون المربط التنبيهي مفتوحًا افتراضيًا |
| المكان | `PlacesType` string from `react-tooltip` | يحدد موضع المربط التنبيهي. Values include `bottom`, `left`, `right`, `top`, `top-start`, `top-end`, `right-start`, `right-end`, `bottom-start`, `bottom-end`, `left-start`, and `left-end` |
| استراتيجية الوضعية | نص `PositionStrategy` من `react-tooltip` | استراتيجية وضعية للمربط التنبيهي. Has two values: `absolute` and `fixed` |
| المحددات | النوع | الوصف |
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
| اختيار الربط | محدد CSS | المحدِّد لمرساة التلميح (العنصر الذي يُفعّل التلميح) |
| المحتوى | نص | المحتوى الذي تريد عرضه داخل التلميح |
| تأخير الإخفاء | رقم | التأخير بالثواني قبل إخفاء التلميح بعد مغادرة المؤشر للمرساة |
| الإزاحة | رقم | الإزاحة بالبكسل لتحديد موضع التلميح |
| بدون سهم | قيمة منطقية | إذا كانت القيمة `صحيح`, سيتم إخفاء السهم في المربط التنبيهي |
| مفتوح | قيمة منطقية | إذا كانت القيمة `صحيح`, يكون المربط التنبيهي مفتوحًا افتراضيًا |
| المكان | نص `PlacesType` من `react-tooltip` | يحدد موضع المربط التنبيهي. تتضمن القيم `bottom`، `left`، `right`، `top`، `top-start`، `top-end`، `right-start`، `right-end`، `bottom-start`، `bottom-end`، `left-start`، و`left-end` |
| استراتيجية الوضعية | نص `PositionStrategy` من `react-tooltip` | استراتيجية وضعية للمربط التنبيهي. له قيمتان: `absolute` و`fixed` |
</Tab>
</Tabs>
## Overflowing Text with Tooltip
## نص متجاوز مع تلميح
يعالج النص الزائد ويعرض مربط تنبيهي عند فيضان النص.
@@ -63,7 +63,7 @@ image: /images/user-guide/tips/light-bulb.png
export const MyComponent = () => {
const crmTaskDescription =
'Follow up with client regarding their recent product inquiry. Discuss pricing options, address any concerns, and provide additional product information. Record the details of the conversation in the CRM for future reference.';
'المتابعة مع العميل بشأن استفساره الأخير عن المنتج. مناقشة خيارات التسعير، ومعالجة أي مخاوف، وتقديم معلومات إضافية عن المنتج. تسجيل تفاصيل المحادثة في نظام إدارة علاقات العملاء (CRM) للرجوع إليها لاحقاً.';
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
};
@@ -71,8 +71,8 @@ image: /images/user-guide/tips/light-bulb.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| ------- | ------ | ------------------------------------------------------------ |
| نص | string | The content you want to display in the overflowing text area |
| الخصائص | النوع | الوصف |
| ------- | ------ | --------------------------------------------- |
| نص | string | المحتوى الذي تريد عرضه في منطقة النص المتجاوز |
</Tab>
</Tabs>
@@ -51,8 +51,8 @@ image: /images/user-guide/tasks/tasks_header.png
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
| ----------- | ----------- | ------------------------------------- | ----------------- |
| isAnimating | قيمة منطقية | يتحكم فيما إذا كانت علامة صحيح متحركة | خاطئ |
| اللون | string | Color of the checkmark | |
| اللون | string | لون علامة الاختيار | |
| المدة | رقم | مدة الحركة بالثواني | 0.5 ثانية |
| الحجم | رقم | The size of the checkmark | 28 بكسل |
| الحجم | رقم | حجم علامة الاختيار | 28 بكسل |
</Tab>
</Tabs>
@@ -9,7 +9,7 @@ image: /images/user-guide/objects/objects.png
قائمة بالأيقونات المستخدمة في جميع أنحاء تطبيقنا.
## Tabler Icons
## أيقونات Tabler
نستخدم أيقونات Tabler لـ React في جميع أنحاء التطبيق.
@@ -1,5 +1,5 @@
---
title: Soon Pill
title: شارة قريبًا
image: /images/user-guide/kanban-views/kanban.png
---
@@ -7,7 +7,7 @@ image: /images/user-guide/table-views/table.png
<img src="/images/user-guide/table-views/table.png" alt="رأس الصفحة" />
</Frame>
Component to visually categorize or label content.
مكوّن لتصنيف المحتوى أو وسمه بصريًا.
<Tabs>
<Tab title="استخدام">
@@ -38,20 +38,20 @@ image: /images/user-guide/views/filter.png
</Tab>
<Tab title="خصائص">
| خصائص | النوع | الوصف |
| --------- | --------------------- | -------------------------------------------------------------------------------------- |
| className | string | اسم فئة اختياري لتنسيقات إضافية |
| أيقونة | `React.ComponentType` | مكون رمز اختياري يُعرض داخل الزر |
| العنوان | string | محتوى نص الزر |
| عرض كامل | قيمة منطقية | يُحدد إذا كان الزر يجب أن يمتد ليغطي العرض الكامل للحاوية الخاصة به |
| التنوع | string | النمط المرئي للزر. Options include `primary`, `secondary`, and `tertiary` |
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
| الموقع | string | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `right`, and `middle` |
| accent | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `default`، `blue`، `danger` |
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
| خصائص | النوع | الوصف |
| --------- | --------------------- | ---------------------------------------------------------------------------------- |
| className | string | اسم فئة اختياري لتنسيقات إضافية |
| أيقونة | `React.ComponentType` | مكون رمز اختياري يُعرض داخل الزر |
| العنوان | string | محتوى نص الزر |
| عرض كامل | قيمة منطقية | يُحدد إذا كان الزر يجب أن يمتد ليغطي العرض الكامل للحاوية الخاصة به |
| التنوع | string | النمط المرئي للزر. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
| accent | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `default`، `blue`، `danger` |
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
</Tab>
</Tabs>
@@ -116,13 +116,13 @@ image: /images/user-guide/views/filter.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| --------- | --------- | ------------------------------------------------------------------------------------------ |
| التنوع | string | النمط المرئي للأزرار داخل المجموعة. Options include `primary`, `secondary`, and `tertiary` |
| الحجم | string | حجم الأزرار داخل المجموعة. Has two options: `medium` and `small` |
| accent | نص | لون تمييز الأزرار داخل المجموعة. Options include `default`, `blue` and `danger` |
| className | string | اسم فئة اختياري لتنسيقات إضافية |
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة |
| الخصائص | النوع | الوصف |
| --------- | --------- | -------------------------------------------------------------------------------------- |
| التنوع | string | النمط المرئي للأزرار داخل المجموعة. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
| الحجم | string | حجم الأزرار داخل المجموعة. يوجد خياران: `medium` و`small`. |
| accent | نص | لون تمييز الأزرار داخل المجموعة. تشمل الخيارات `default`، `blue` و`danger`. |
| className | string | اسم فئة اختياري لتنسيقات إضافية |
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة |
</Tab>
</Tabs>
@@ -153,17 +153,17 @@ image: /images/user-guide/views/filter.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
| className | string | اسم اختياري لتنسيقات إضافية |
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
| العنوان | string | محتوى نص الزر |
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
| الموقع | string | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `middle`, `right` |
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
| الخصائص | النوع | الوصف |
| ------------ | --------------------- | --------------------------------------------------------------------------------- |
| className | string | اسم اختياري لتنسيقات إضافية |
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
| العنوان | string | محتوى نص الزر |
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `middle`، `right`. |
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
</Tab>
</Tabs>
@@ -242,18 +242,18 @@ image: /images/user-guide/views/filter.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| ------------ | --------------------- | -------------------------------------------------------------------------------------- |
| className | نص | اسم اختياري لتنسيقات إضافية |
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
| الموقع | نص | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `right`, and `middle` |
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
| الخصائص | النوع | الوصف |
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
| className | نص | اسم اختياري لتنسيقات إضافية |
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
| الموقع | نص | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
</Tab>
</Tabs>
@@ -299,7 +299,7 @@ image: /images/user-guide/views/filter.png
</Tab>
</Tabs>
## Light Button
## زر خفيف
<Tabs>
<Tab title="الاستخدام">
@@ -31,14 +31,14 @@ image: /images/user-guide/tasks/tasks_header.png
</Tab>
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| ------------------------ | ----------- | ------------------------------------------------------------------------------------------- |
| مختار | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار محددًا |
| indeterminate | قيمة منطقية | Indicates whether the checkbox is in an indeterminate state (neither checked nor unchecked) |
| عند التغيير | دالة | الدالة التي ترغب في تفعيلها عند تغيير حالة مربع الاختيار |
| عند تغيير الحالة المحددة | دالة | The callback function you want to trigger when the `checked` state changes |
| نموذج | نص | النمط البصري للصندوق. تتضمن الخيارات: 'أساسي'، 'ثانوي'، و 'ثالثي' |
| الحجم | نص | حجم مربع الاختيار. Has two options: `small` and `large` |
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
| المحددات | النوع | الوصف |
| ------------------------ | ----------- | ----------------------------------------------------------------------------- |
| مختار | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار محددًا |
| غير محدد | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار في حالة غير محددة (لا هو محدد ولا غير محدد) |
| عند التغيير | دالة | الدالة التي ترغب في تفعيلها عند تغيير حالة مربع الاختيار |
| عند تغيير الحالة المحددة | دالة | الدالة التي ترغب في تفعيلها عند تغيّر حالة `checked` |
| نموذج | نص | النمط البصري للصندوق. تتضمن الخيارات: 'أساسي'، 'ثانوي'، و 'ثالثي' |
| الحجم | نص | حجم مربع الاختيار. له خياران: `small` و `large` |
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
</Tab>
</Tabs>
@@ -55,9 +55,9 @@ image: /images/user-guide/fields/field.png
</Tab>
<Tab title="خصائص">
| خصائص | النوع | الوصف |
| ----------- | ------------------- | ---------------------------------------------------------------------------- |
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
| عند التغيير | function | The callback function you want to trigger when a user selects a color scheme |
| خصائص | النوع | الوصف |
| ----------- | ------------------- | ------------------------------------------------------------- |
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
| عند التغيير | function | الدالة التي ترغب في تفعيلها عندما يختار المستخدم نظام الألوان |
</Tab>
</Tabs>
@@ -47,6 +47,6 @@ image: /images/user-guide/github/github-header.png
| النقر بالخارج | دالة | الدالة الارتجاعية التي تُفعل عندما ينقر المستخدم خارج القائمة المنسدلة |
| عند الإغلاق | دالة | الدالة الارتجاعية التي تُفعل عند إغلاق القائمة المنسدلة |
| عند الفتح | دالة | الدالة الارتجاعية التي تُفعل عند فتح القائمة المنسدلة |
| التنوع | نص | The visual style variant of the clickable icon. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
| التنوع | نص | متغير النمط البصري للأيقونة القابلة للنقر. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
</Tab>
</Tabs>
@@ -21,14 +21,14 @@ image: /images/user-guide/objects/objects.png
</Tab>
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| ------------ | ----------- | ------------------------------------------------------------------------------------------------- |
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
| onUpload | دالة | The function called when a user uploads a new image. It receives the `File` object as a parameter |
| onRemove | دالة | The function called when the user clicks on the remove button |
| onAbort | دالة | The function called when a user clicks on the abort button during image upload |
| isUploading | قيمة منطقية | Indicates whether an image is currently being uploaded |
| errorMessage | نص | An optional error message to display below the image input |
| معطل | قيمة منطقية | If `true`, the entire input is disabled, and the buttons are not clickable |
| المحددات | النوع | الوصف |
| ------------ | ----------- | --------------------------------------------------------------------------------- |
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
| onUpload | دالة | الدالة التي تُستدعى عند قيام المستخدم بتحميل صورة جديدة. تستقبل كائن `File` كوسيط |
| onRemove | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإزالة |
| onAbort | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإلغاء أثناء تحميل الصورة |
| isUploading | قيمة منطقية | يشير إلى ما إذا كان يتم تحميل صورة حاليًا |
| errorMessage | نص | رسالة خطأ اختيارية لعرضها أسفل حقل إدخال الصورة |
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون حقل الإدخال بأكمله معطلاً، ولن تكون الأزرار قابلة للنقر |
</Tab>
</Tabs>
@@ -37,22 +37,22 @@ image: /images/user-guide/create-workspace/workspace-cover.png
/>
);
};
},{
```
</Tab>
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| ------------------------ | ----------------- | ------------------------------------------------------------------------ |
| النمط | خصائص `React.CSS` | أنماط إضافية مضمنة للمكون |
| اسم الفئة | نص | فئة CSS اختيارية لتصميم إضافي |
| مختار | قيمة منطقية | يشير إلى ما إذا كان زر الراديو محددًا |
| القيمة | نص | التسمية أو النص المرتبط بزر الراديو |
| عند التغيير | دالة | The function called when the selected radio button is changed |
| عند تغيير الحالة المحددة | دالة | The function called when the `checked` state of the radio button changes |
| الحجم | نص | حجم زر الراديو. Options include: `large` and `small` |
| معطل | قيمة منطقية | If `true`, the radio button is disabled and not clickable |
| موضع التسمية | نص | موضع نص التسمية بالنسبة لزر الراديو. Has two options: `left` and `right` |
| المحددات | النوع | الوصف |
| ------------------------ | ----------------- | ------------------------------------------------------------------ |
| النمط | خصائص `React.CSS` | أنماط إضافية مضمنة للمكون |
| اسم الفئة | نص | فئة CSS اختيارية لتصميم إضافي |
| مختار | قيمة منطقية | يشير إلى ما إذا كان زر الراديو محددًا |
| القيمة | نص | التسمية أو النص المرتبط بزر الراديو |
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير زر الاختيار المحدد. |
| عند تغيير الحالة المحددة | دالة | الدالة التي تُستدعى عند تغيير حالة `checked` لزر الاختيار. |
| الحجم | نص | حجم زر الراديو. تشمل الخيارات: `large` و `small` |
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون زر الاختيار معطلاً وغير قابل للنقر |
| موضع التسمية | نص | موضع نص التسمية بالنسبة لزر الراديو. يوجد خياران: `left` و `right` |
</Tab>
</Tabs>
@@ -87,11 +87,11 @@ image: /images/user-guide/create-workspace/workspace-cover.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| ------------- | ----------------- | ---------------------------------------------------------------------------------- |
| القيمة | string | قيمة زر الراديو المحدد حاليًا |
| عند التغيير | دالة | The callback function triggered when the radio button is changed |
| onValueChange | دالة | The callback function triggered when the selected value in the group changes. |
| الأبناء | `React.ReactNode` | Allows you to pass React components (such as Radio) as children to the Radio Group |
| الخصائص | النوع | الوصف |
| ------------- | ----------------- | --------------------------------------------------------------------- |
| القيمة | string | قيمة زر الراديو المحدد حاليًا |
| عند التغيير | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير زر الاختيار. |
| onValueChange | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير القيمة المحددة في المجموعة. |
| الأبناء | `React.ReactNode` | يتيح لك تمرير مكوّنات React (مثل Radio) كعناصر فرعية إلى Radio Group |
</Tab>
</Tabs>
@@ -43,8 +43,8 @@ image: /images/user-guide/what-is-twenty/20.png
| ----------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
| معطل | قيمة منطقية | عند ضبطها على `true`، يتم تعطيل تفاعل المستخدم مع المكون |
| التسمية | نص | The label to describe the purpose of the `Select` component |
| عند التغيير | دالة | The function called when the selected values change |
| التسمية | نص | التسمية التي تصف غرض مكوّن `Select` |
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير القيم المحددة |
| خيارات | مصفوفة | تمثل الخيارات المتاحة في مكون `الاختيار`. إنها مصفوفة من الكائنات حيث يحتوي كل كائن على `قيمة` (معرف فريد)، `تسمية` (معرف فريد)، و`أيقونة` اختيارية |
| القيمة | نص | تمثل القيمة المحددة حاليًا. يجب أن تطابق إحدى خصائص `القيمة` في مصفوفة `الخيارات` |
</Tab>
@@ -19,29 +19,29 @@ image: /images/user-guide/notes/notes_header.png
export const MyComponent = () => {
const handleChange = (text) => {
console.log("Input changed:", text);
console.log("تم تغيير الإدخال:", text);
};
const handleKeyDown = (event) => {
console.log("Key pressed:", event.key);
console.log("تم ضغط المفتاح:", event.key);
};
return (
<RecoilRoot>
<TextInput
className
label="Username"
label="اسم المستخدم"
onChange={handleChange}
fullWidth={false}
disableHotkeys={false}
error="Invalid username"
error="اسم مستخدم غير صالح"
onKeyDown={handleKeyDown}
RightIcon={null}
/>
</RecoilRoot>
);
};
},{
```
</Tab>
@@ -75,17 +75,17 @@ image: /images/user-guide/notes/notes_header.png
return (
<RecoilRoot>
<AutosizeTextInput
onValidate={() => console.log("onValidate function fired")}
onValidate={() => console.log("تم تشغيل الدالة onValidate")}
minRows={1}
placeholder="Write a comment"
onFocus={() => console.log("onFocus function fired")}
placeholder="اكتب تعليقًا"
onFocus={() => console.log("تم تشغيل الدالة onFocus")}
variant="icon"
buttonTitle
value="Task: "
value="المهمة: "
/>
</RecoilRoot>
);
};
};},{
```
</Tab>
@@ -116,8 +116,8 @@ image: /images/user-guide/notes/notes_header.png
<TextArea
disabled={false}
minRows={4}
onChange={()=>console.log('On change function fired')}
placeholder="Enter text here"
onChange={()=>console.log('تم تشغيل الدالة onChange')}
placeholder="أدخل النص هنا"
value=""
/>
);
@@ -16,7 +16,7 @@ image: /images/user-guide/table-views/table.png
return (
<Toggle
value = {true}
onChange = {()=>console.log('On Change event')}
onChange = {()=>console.log('تم تشغيل حدث onChange')}
color="green"
toggleSize = "medium"
/>
@@ -28,8 +28,8 @@ image: /images/user-guide/table-views/table.png
<Tab title="الخصائص">
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
| ----------- | ----------- | --------------------------------------------------------------------------- | ----------------- |
| القيمة | قيمة منطقية | The current state of the toggle | `خاطئ` |
| عند التغيير | دالة | Callback function triggered when the toggle state changes | |
| القيمة | قيمة منطقية | الحالة الحالية لمفتاح التبديل | `خاطئ` |
| عند التغيير | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل | |
| اللون | string | لون التبديل عند كونه | لون أزرق |
| حجم التبديل | نص | حجم التبديل الذي يؤثر على كل من الطول والوزن. لديها خياران: `صغير` و`متوسط` | متوسط |
</Tab>
@@ -9,22 +9,22 @@ import { CardTitle } from "/snippets/card-title.mdx"
<CardGroup cols={٢}>
<Card href="/l/ar/twenty-ui/display" img="/images/user-guide/views/filter.png">
<CardTitle>Display</CardTitle>
Display components for showing information visually
<CardTitle>العرض</CardTitle>
مكونات العرض لعرض المعلومات بصريًا
</Card>
<Card href="/l/ar/twenty-ui/progress-bar" img="/images/user-guide/fields/field.png">
<CardTitle>Feedback</CardTitle>
Feedback components for user notifications
<CardTitle>التغذية الراجعة</CardTitle>
مكونات التغذية الراجعة لإبلاغ المستخدمين
</Card>
<Card href="/l/ar/twenty-ui/input" img="/images/user-guide/tips/light-bulb.png">
<CardTitle>Input</CardTitle>
Input components for user interaction
<CardTitle>الإدخال</CardTitle>
مكونات الإدخال لتفاعل المستخدم
</Card>
<Card href="/l/ar/twenty-ui/navigation" img="/images/user-guide/tasks/tasks_header.png">
<CardTitle>Navigation</CardTitle>
Navigation components for user interface
<CardTitle>التنقل</CardTitle>
مكونات التنقل لواجهة المستخدم
</Card>
</CardGroup>
@@ -1,5 +1,5 @@
---
title: Navigation
title: التنقل
image: /images/user-guide/tasks/tasks_header.png
---
@@ -1,5 +1,5 @@
---
title: Breadcrumb
title: مسار التنقّل
image: /images/user-guide/fields/field.png
---
@@ -7,7 +7,7 @@ image: /images/user-guide/fields/field.png
<img src="/images/user-guide/fields/field.png" alt="رأس الصفحة" />
</Frame>
Renders a breadcrumb navigation bar.
يعرض شريط مسار التنقّل.
<Tabs>
<Tab title="استخدام">
@@ -33,9 +33,9 @@ Renders a breadcrumb navigation bar.
</Tab>
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| --------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
| روابط | مصفوفة | An array of objects, each representing a breadcrumb link. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
| المحددات | النوع | الوصف |
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
| روابط | مصفوفة | مصفوفة من الكائنات، يمثّل كلٌّ منها رابطًا في مسار التنقّل. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
</Tab>
</Tabs>
@@ -20,7 +20,7 @@ image: /images/user-guide/what-is-twenty/20.png
export const MyComponent = () => {
const handleLinkClick = (event) => {
console.log('Contact link clicked!', event);
console.log('تم النقر على رابط الاتصال!', event);
};
return (
@@ -34,7 +34,7 @@ image: /images/user-guide/what-is-twenty/20.png
</ContactLink>
</Router>
);
};
};},{
```
</Tab>
@@ -42,16 +42,16 @@ image: /images/user-guide/kanban-views/kanban.png
</Tab>
<Tab title="المحددات">
| المحددات | النوع | الوصف |
| -------------- | ------------- | ----------------------------------------------------------------------------------------- |
| أيقونة اليسار | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
| accent | نص | Specifies the accent color of the menu item. تشمل الخيارات: `افتراضي`, `خطر`, `موضع مؤقت` |
| نص | نص | المحتوى النصي لعنصر القائمة |
| أزرار الأيقونة | array | مجموعة من الكائنات التي تمثل أيقونات إضافية مرتبطة بعنصر القائمة |
| isTooltipOpen | قيمة منطقية | Controls the visibility of the tooltip associated with the menu item |
| معرف الفحص | نص | السمة data-testid لأغراض الاختبار |
| عند النقر | function | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
| المحددات | النوع | الوصف |
| -------------- | ------------- | ---------------------------------------------------------------------------- |
| أيقونة اليسار | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
| accent | نص | يحدد لون الإبراز لعنصر القائمة. تشمل الخيارات: `افتراضي`, `خطر`, `موضع مؤقت` |
| نص | نص | المحتوى النصي لعنصر القائمة |
| أزرار الأيقونة | array | مجموعة من الكائنات التي تمثل أيقونات إضافية مرتبطة بعنصر القائمة |
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
| معرف الفحص | نص | السمة data-testid لأغراض الاختبار |
| عند النقر | function | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
</Tab>
</Tabs>
@@ -90,15 +90,15 @@ image: /images/user-guide/kanban-views/kanban.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| --------------- | ------------- | -------------------------------------------------------- |
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
| نص | نص | محتوى النص لعنصر القائمة |
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
| secondHotKey | string | The second keyboard shortcut associated with the command |
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
| الخصائص | النوع | الوصف |
| --------------- | ------------- | ----------------------------------------------------- |
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
| نص | نص | محتوى النص لعنصر القائمة |
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
| secondHotKey | string | اختصار لوحة المفاتيح الثاني المرتبط بالأمر |
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
</Tab>
</Tabs>
@@ -135,16 +135,16 @@ image: /images/user-guide/kanban-views/kanban.png
</Tab>
<Tab title="الخصائص">
| الخصائص | النوع | الوصف |
| --------------- | ------------- | -------------------------------------------------------------------------------- |
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
| accent | نص | لون العنصر لهجة القائمة. It can either be `default`, `placeholder`, and `danger` |
| أزرار الأيقونات | array | مصفوفة الكائنات التي تمثل أزرار الأيقونات الإضافية المرتبطة بعنصر القائمة |
| isTooltipOpen | قيمة منطقية | Controls the visibility of the tooltip associated with the menu item |
| عند_النقر | دالة | وظيفة استدعاء ليتم تشغيلها عند النقر فوق الرابط |
| نص | نص | محتوى النص لعنصر القائمة |
| isDragDisabled | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
| الخصائص | النوع | الوصف |
| --------------- | ------------- | ------------------------------------------------------------------------------------- |
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
| accent | نص | لون العنصر لهجة القائمة. يمكن أن يكون إحدى القيم: `default`، `placeholder`، و`danger` |
| أزرار الأيقونات | array | مصفوفة الكائنات التي تمثل أزرار الأيقونات الإضافية المرتبطة بعنصر القائمة |
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
| عند_النقر | دالة | وظيفة استدعاء ليتم تشغيلها عند النقر فوق الرابط |
| نص | نص | محتوى النص لعنصر القائمة |
| isDragDisabled | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
</Tab>
</Tabs>
@@ -183,7 +183,7 @@ image: /images/user-guide/kanban-views/kanban.png
</Tab>
</Tabs>
### Multi Select Avatar
### التحديد المتعدد للصورة الرمزية
عنصر قائمة متعدد الخيارات مع صورة رمزية، ومربع اختيار للتحديد، ومحتوى نصي.
@@ -221,7 +221,7 @@ image: /images/user-guide/kanban-views/kanban.png
### التنقل
A menu item featuring an optional left icon, textual content, and a right-chevron icon.
عنصر قائمة يتضمن أيقونة يسارية اختيارية، محتوى نصي، وأيقونة سهم إلى اليمين.
<Tabs>
<Tab title="الاستخدام">
@@ -299,9 +299,9 @@ A menu item featuring an optional left icon, textual content, and a right-chevro
</Tab>
</Tabs>
### Select Avatar
### اختيار الصورة الرمزية
A selectable menu item with an avatar, featuring optional left content (avatar and text) and an indicator (check icon) for the selected state.
عنصر قائمة قابل للتحديد مع صورة رمزية، يتضمن محتوى يسارياً اختيارياً (صورة رمزية ونصاً) ومؤشراً (أيقونة التحقق) لحالة التحديد.
<Tabs>
<Tab title="الاستخدام">
@@ -310,16 +310,16 @@ A selectable menu item with an avatar, featuring optional left content (avatar a
export const MyComponent = () => {
const imageUrl =
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMACwgICggHCwoJCg0MCw0RHBIRDw8RIhkaFBwpJCsqKCQnJy0yQDctMD0wJyc4TDk9Q0VISUgrNk9VTkZUQEdIRf/bAEMBDA0NEQ8RIRISIUUuJy5FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRf/dAAQAAv/aAAwDAQACEQMRAD8Ava1q728otYY98joSCTgZrnbXWdTtrhrfVZXWLafmcAEkdgR/hVltQku9Q8+OIEBcGOT+ID0PY1ka1KH2u8ToqnPLbmIqG7u6LtbQ7RXBRec4Uck9eKXcPWsKDWVnhWSL5kYcFelSf2m3901POh8jP//QoyIAnTuKpXsY82NsksUyWPU5q/L9z8RVK++/F/uCsVsaEURwgA4HtT9x9TUcf3KfUGh//9k=";
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6OnqvLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMACwgICggHCwoJCg0MCw0RHBIRDw8RIhkaFBwpJCsqKCQnJy0yQDctMD0wJyc4TDk9Q0VISUgrNk9VTkZUQEdIRf/bAEMBDA0NEQ8RIRISIUUuJy5FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRf/dAAQAAv/aAAwDAQACEQMRAD8Ava1q728otYY98joSCTgZrnbXWdTtrhrfVZXWLafmcAEkdgR/hVltQku9Q8+OIEBcGOT+ID0PY1ka1KH2u8ToqnPLbmIqG7u6LtbQ7RXBRec4Uck9eKXcPWsKDWVnhWSL5kYcFelSf2m3901POh8jP//QoyIAnTuKpXsY82NsksUyWPU5q/L9z8RVK++/F/uCsVsaEURwgA4HtT9x9TUcf3KfUGh//9k=";
const handleSelection = () => {
console.log("Menu item selected");
console.log("تم تحديد عنصر القائمة");
};
return (
<MenuItemSelectAvatar
avatar={<img src={imageUrl} alt="Avatar" />}
text="First Option"
avatar={<img src={imageUrl} alt="الصورة الرمزية" />}
text="الخيار الأول"
selected={true}
disabled={false}
hovered={false}
@@ -349,7 +349,7 @@ A selectable menu item with an avatar, featuring optional left content (avatar a
### اختيار اللون
A selectable menu item with a color sample for scenarios where you want users to choose a color from a menu.
عنصر قائمة قابل للتحديد يتضمن عينة لون للسيناريوهات التي تريد فيها من المستخدمين اختيار لون من القائمة.
<Tabs>
<Tab title="الاستخدام">
@@ -383,7 +383,7 @@ A selectable menu item with a color sample for scenarios where you want users to
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
| البديل | نص | The variant of the color sample. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
| البديل | نص | المتغير لعينة اللون. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
</Tab>

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