i18n - docs translations (#18541)
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
committed by
GitHub
parent
621962e049
commit
e7fe435f60
@@ -321,11 +321,11 @@ export default defineObject({
|
||||
但不建议这样做。
|
||||
</Note>
|
||||
|
||||
### Defining fields on existing objects
|
||||
### 在现有对象上定义字段
|
||||
|
||||
Use `defineField()` to add custom fields to existing objects — both standard objects (like `company`, `person`, `opportunity`) and custom objects defined by other apps. Each field lives in its own file and references the target object by its `universalIdentifier`.
|
||||
使用 `defineField()` 向现有对象添加自定义字段——包括标准对象(如 `company`、`person`、`opportunity`)以及由其他应用定义的自定义对象。 每个字段位于其各自的文件中,并通过其 `universalIdentifier` 引用目标对象。
|
||||
|
||||
To reference standard objects, import `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` from `twenty-sdk`. This constant provides stable identifiers for all built-in objects and their fields:
|
||||
要引用标准对象,请从 `twenty-sdk` 导入 `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`。 该常量为所有内置对象及其字段提供稳定的标识符:
|
||||
|
||||
```typescript
|
||||
// src/fields/apollo-total-funding.field.ts
|
||||
@@ -349,22 +349,22 @@ export default defineField({
|
||||
|
||||
关键点:
|
||||
|
||||
* `objectUniversalIdentifier` tells Twenty which object to attach the field to. Use `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.<objectName>.universalIdentifier` for standard objects.
|
||||
* Each field requires its own stable `universalIdentifier`, a `name`, `type`, `label`, and the target `objectUniversalIdentifier`.
|
||||
* You can scaffold new fields using `yarn twenty entity:add` and choosing the field option.
|
||||
* `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` is also exported as `STANDARD_OBJECT` for convenience — both refer to the same constant.
|
||||
* `objectUniversalIdentifier` 告诉 Twenty 将该字段附加到哪个对象。 使用 `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.<objectName>.universalIdentifier` 用于标准对象。
|
||||
* 每个字段都需要其自身稳定的 `universalIdentifier`、`name`、`type`、`label`,以及目标 `objectUniversalIdentifier`。
|
||||
* 你可以使用 `yarn twenty entity:add` 脚手架创建新字段,并选择字段选项。
|
||||
* `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` 也以 `STANDARD_OBJECT` 的名称导出以方便使用——二者指向同一常量。
|
||||
|
||||
Available standard objects include: `attachment`, `blocklist`, `calendarChannel`, `calendarEvent`, `calendarEventParticipant`, `company`, `connectedAccount`, `dashboard`, `favorite`, `favoriteFolder`, `message`, `messageChannel`, `messageParticipant`, `messageThread`, `note`, `noteTarget`, `opportunity`, `person`, `task`, `taskTarget`, `timelineActivity`, `workflow`, `workflowAutomatedTrigger`, `workflowRun`, `workflowVersion`, and `workspaceMember`.
|
||||
可用的标准对象包括:`attachment`、`blocklist`、`calendarChannel`、`calendarEvent`、`calendarEventParticipant`、`company`、`connectedAccount`、`dashboard`、`favorite`、`favoriteFolder`、`message`、`messageChannel`、`messageParticipant`、`messageThread`、`note`、`noteTarget`、`opportunity`、`person`、`task`、`taskTarget`、`timelineActivity`、`workflow`、`workflowAutomatedTrigger`、`workflowRun`、`workflowVersion` 和 `workspaceMember`。
|
||||
|
||||
Each standard object also exposes its field identifiers. For example, to reference a specific field on a standard object in role permissions:
|
||||
每个标准对象还会暴露其字段标识符。 例如,要在角色权限中引用标准对象上的特定字段:
|
||||
|
||||
```typescript
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name.universalIdentifier
|
||||
```
|
||||
|
||||
#### Relation fields on existing objects
|
||||
#### 现有对象上的关联字段
|
||||
|
||||
You can also define relation fields that link existing objects to your custom objects:
|
||||
你还可以定义关联字段,将现有对象链接到你的自定义对象:
|
||||
|
||||
```typescript
|
||||
// src/fields/people-on-call-recording.field.ts
|
||||
@@ -837,6 +837,255 @@ export default defineFrontComponent({
|
||||
* **脚手架生成**:运行 `yarn twenty entity:add` 并选择添加新前端组件的选项。
|
||||
* **手动**:创建一个新的 `.tsx` 文件,并使用 `defineFrontComponent()`,遵循相同的模式。
|
||||
|
||||
#### Where front components can be used
|
||||
|
||||
Front components can render in two locations within Twenty:
|
||||
|
||||
* **Side panel** — Non-headless front components open in the right-hand side panel. This is the default behavior when a front component is triggered from the command menu.
|
||||
* **Widgets (dashboards and record pages)** — Front components can be embedded as widgets inside page layouts. When configuring a dashboard or a record page layout, users can add a front component widget.
|
||||
|
||||
#### Headless vs non-headless
|
||||
|
||||
Front components come in two rendering modes controlled by the `isHeadless` option:
|
||||
|
||||
**Non-headless (default)** — The component renders a visible UI. When triggered from the command menu it opens in the side panel. This is the default behavior when `isHeadless` is `false` or omitted.
|
||||
|
||||
**Headless** — The component mounts invisibly in the background. It does not open the side panel. Headless components are designed for actions that execute logic and then unmount themselves — for example, running an async task, navigating to a page, or showing a confirmation modal. They pair naturally with the SDK Command components described below.
|
||||
|
||||
```typescript
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
name: 'my-action',
|
||||
description: 'Runs an action without opening the side panel',
|
||||
component: MyAction,
|
||||
isHeadless: true,
|
||||
command: {
|
||||
universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
|
||||
label: 'Run my action',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Adding command menu items
|
||||
|
||||
To make a front component appear as an item in Twenty's command menu, add the `command` property to `defineFrontComponent()`. When users open the command menu (Cmd+K / Ctrl+K), the item shows up and triggers the front component on click.
|
||||
|
||||
The `command` object accepts the following fields:
|
||||
|
||||
| 字段 | 类型 | 描述 |
|
||||
| --------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | `string` (required) | Unique ID for the command menu item |
|
||||
| `标签` | `string` (required) | Display label shown in the command menu |
|
||||
| `图标` | `string` (optional) | Icon name (e.g., `'IconSparkles'`) |
|
||||
| `isPinned` | `boolean` (optional) | Whether the command is pinned at the top of the menu |
|
||||
| `availabilityType` | `'GLOBAL' \| 'RECORD_SELECTION'` (optional) | `GLOBAL` shows the command everywhere; `RECORD_SELECTION` shows it only in record contexts |
|
||||
| `availabilityObjectUniversalIdentifier` | `string` (optional) | Restrict the command to a specific object type (e.g., Person) |
|
||||
|
||||
Here is an example from the call-recording app that adds a command scoped to Person records:
|
||||
|
||||
```typescript
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
|
||||
name: 'Summarize Person Call Recordings',
|
||||
description: 'Generates a summary of call recordings for a person',
|
||||
component: SummarizePersonRecordings,
|
||||
command: {
|
||||
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-234567890123',
|
||||
label: 'Summarize call recordings',
|
||||
icon: 'IconSparkles',
|
||||
isPinned: false,
|
||||
availabilityType: 'RECORD_SELECTION',
|
||||
availabilityObjectUniversalIdentifier:
|
||||
'20202020-e674-48e5-a542-72570eee7213',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When the command is synced, it appears in the command menu. If the front component is non-headless the side panel opens with the component rendered inside. If it is headless the component mounts in the background and executes its logic.
|
||||
|
||||
#### SDK Command components
|
||||
|
||||
The `twenty-sdk` package provides four Command helper components designed for headless front components. Each component executes an action on mount, handles errors by showing a snackbar notification, and automatically unmounts the front component when done.
|
||||
|
||||
Import them from `twenty-sdk/command`:
|
||||
|
||||
* **`Command`** — Runs an async callback via the `execute` prop.
|
||||
* **`CommandLink`** — Navigates to an app path. Props: `to`, `params`, `queryParams`, `options`.
|
||||
* **`CommandModal`** — Opens a confirmation modal. If the user confirms, executes the `execute` callback. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||||
* **`CommandOpenSidePanelPage`** — Opens a specific side panel page. Props: `page`, `pageTitle`, `pageIcon`.
|
||||
|
||||
Here is a full example of a headless front component using `Command` to run an action from the command menu:
|
||||
|
||||
```typescript
|
||||
// src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
import { Command } from 'twenty-sdk/command';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
|
||||
const RunAction = () => {
|
||||
const execute = async () => {
|
||||
const client = new CoreApiClient();
|
||||
|
||||
await client.mutation({
|
||||
createTask: {
|
||||
__args: { data: { title: 'Created by my app' } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return <Command execute={execute} />;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||||
name: 'run-action',
|
||||
description: 'Creates a task from the command menu',
|
||||
component: RunAction,
|
||||
isHeadless: true,
|
||||
command: {
|
||||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||||
label: 'Run my action',
|
||||
icon: 'IconPlayerPlay',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
And an example using `CommandModal` to ask for confirmation before executing:
|
||||
|
||||
```typescript
|
||||
// src/front-components/delete-draft.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
import { CommandModal } from 'twenty-sdk/command';
|
||||
|
||||
const DeleteDraft = () => {
|
||||
const execute = async () => {
|
||||
// perform the deletion
|
||||
};
|
||||
|
||||
return (
|
||||
<CommandModal
|
||||
title="Delete draft?"
|
||||
subtitle="This action cannot be undone."
|
||||
execute={execute}
|
||||
confirmButtonText="Delete"
|
||||
confirmButtonAccent="danger"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456',
|
||||
name: 'delete-draft',
|
||||
description: 'Deletes a draft with confirmation',
|
||||
component: DeleteDraft,
|
||||
isHeadless: true,
|
||||
command: {
|
||||
universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567',
|
||||
label: 'Delete draft',
|
||||
icon: 'IconTrash',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Execution context
|
||||
|
||||
Every front component receives an execution context that provides information about where and how it is running. Access context values using hooks from `twenty-sdk`:
|
||||
|
||||
| Hook | Return type | 描述 |
|
||||
| ----------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `useFrontComponentId()` | `string` | The unique ID of the current front component instance |
|
||||
| `useRecordId()` | `string \| null` | The ID of the current record, when the component runs in a record context (e.g., a record page widget or a command scoped to a record). Returns `null` otherwise. |
|
||||
| `useUserId()` | `string \| null` | The ID of the current user |
|
||||
|
||||
```typescript
|
||||
import { useRecordId, useUserId } from 'twenty-sdk';
|
||||
|
||||
const MyWidget = () => {
|
||||
const recordId = useRecordId();
|
||||
const userId = useUserId();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Record: {recordId ?? 'none'}</p>
|
||||
<p>User: {userId ?? 'anonymous'}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
The context is reactive — if the surrounding record changes, hooks automatically return the updated values.
|
||||
|
||||
#### Host API functions
|
||||
|
||||
Front components run in an isolated sandbox but can interact with Twenty's UI through a set of functions provided by the host. Import them directly from `twenty-sdk`:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
navigate,
|
||||
closeSidePanel,
|
||||
enqueueSnackbar,
|
||||
unmountFrontComponent,
|
||||
openSidePanelPage,
|
||||
openCommandConfirmationModal,
|
||||
} from 'twenty-sdk';
|
||||
```
|
||||
|
||||
| 函数 | Signature | 描述 |
|
||||
| ------------------------------ | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `导航` | `(to, params?, queryParams?, options?) => Promise<void>` | Navigate to a typed app path within Twenty |
|
||||
| `closeSidePanel` | `() => Promise<void>` | Close the side panel |
|
||||
| `enqueueSnackbar` | `(params) => Promise<void>` | Show a snackbar notification. Params: `message`, `variant` (`'error'`, `'success'`, `'info'`, `'warning'`), optional `duration`, `detailedMessage`, `dedupeKey` |
|
||||
| `unmountFrontComponent` | `() => Promise<void>` | Unmount the current front component (used by headless components to clean up after execution) |
|
||||
| `openSidePanelPage` | `(params) => Promise<void>` | Open a page in the side panel. Params: `page`, `pageTitle`, `pageIcon`, `shouldResetSearchState` |
|
||||
| `openCommandConfirmationModal` | `(params) => Promise<'confirm' \| 'cancel'>` | Show a confirmation modal and wait for the user's response. Params: `title`, `subtitle`, `confirmButtonText`, `confirmButtonAccent` (`'default'`, `'blue'`, `'danger'`) |
|
||||
|
||||
Here is an example that uses the host API to show a snackbar and close the side panel after an action completes:
|
||||
|
||||
```typescript
|
||||
import { defineFrontComponent, useRecordId } from 'twenty-sdk';
|
||||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
|
||||
const ArchiveRecord = () => {
|
||||
const recordId = useRecordId();
|
||||
|
||||
const handleArchive = async () => {
|
||||
const client = new CoreApiClient();
|
||||
|
||||
await client.mutation({
|
||||
updateTask: {
|
||||
__args: { id: recordId, data: { status: 'ARCHIVED' } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
await enqueueSnackbar({
|
||||
message: 'Record archived',
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
await closeSidePanel();
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<p>Archive this record?</p>
|
||||
<button onClick={handleArchive}>Archive</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678',
|
||||
name: 'archive-record',
|
||||
description: 'Archives the current record',
|
||||
component: ArchiveRecord,
|
||||
});
|
||||
```
|
||||
|
||||
### 技能
|
||||
|
||||
Skills define reusable instructions and capabilities that AI agents can use within your workspace. Use `defineSkill()` to define skills with built-in validation:
|
||||
|
||||
Reference in New Issue
Block a user