i18n - docs translations (#22276)
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
committed by
GitHub
parent
7b17cac772
commit
a0493dbc4b
+124
-18
@@ -3073,11 +3073,37 @@
|
||||
{
|
||||
"language": "ja",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "始めに",
|
||||
"groups": [
|
||||
{
|
||||
"group": "ようこそ",
|
||||
"pages": [
|
||||
"l/ja/getting-started/introduction",
|
||||
"l/ja/getting-started/key-features",
|
||||
"l/ja/getting-started/quickstart"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "基本概念",
|
||||
"pages": [
|
||||
"l/ja/getting-started/core-concepts/data-model",
|
||||
"l/ja/getting-started/core-concepts/layout",
|
||||
"l/ja/getting-started/core-concepts/workflows",
|
||||
"l/ja/getting-started/core-concepts/calendar-and-email",
|
||||
"l/ja/getting-started/core-concepts/ai",
|
||||
"l/ja/getting-started/core-concepts/apps",
|
||||
"l/ja/getting-started/core-concepts/dashboards",
|
||||
"l/ja/getting-started/core-concepts/glossary"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "ユーザーガイド",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Overview",
|
||||
"group": "概要",
|
||||
"pages": [
|
||||
"l/ja/user-guide/introduction"
|
||||
]
|
||||
@@ -3088,7 +3114,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/data-model/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/data-model/capabilities/objects",
|
||||
"l/ja/user-guide/data-model/capabilities/fields",
|
||||
@@ -3114,7 +3140,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/data-migration/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/data-migration/capabilities/file-formats",
|
||||
"l/ja/user-guide/data-migration/capabilities/field-mapping",
|
||||
@@ -3146,7 +3172,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/calendar-emails/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/calendar-emails/capabilities/mailbox",
|
||||
"l/ja/user-guide/calendar-emails/capabilities/calendar"
|
||||
@@ -3171,7 +3197,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/workflows/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/workflows/capabilities/workflow-triggers",
|
||||
"l/ja/user-guide/workflows/capabilities/workflow-actions",
|
||||
@@ -3196,7 +3222,8 @@
|
||||
"l/ja/user-guide/workflows/how-tos/crm-automations/formula-fields",
|
||||
"l/ja/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
|
||||
"l/ja/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
|
||||
"l/ja/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
|
||||
"l/ja/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
|
||||
"l/ja/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3233,7 +3260,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/ai/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/ai/capabilities/ai-chatbot",
|
||||
"l/ja/user-guide/ai/capabilities/ai-agents",
|
||||
@@ -3249,14 +3276,16 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Layout",
|
||||
"group": "レイアウト",
|
||||
"icon": "table-columns",
|
||||
"pages": [
|
||||
"l/ja/user-guide/layout/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/layout/capabilities/navigation",
|
||||
{
|
||||
"group": "Views",
|
||||
"group": "ビュー",
|
||||
"pages": [
|
||||
"l/ja/user-guide/views-pipelines/capabilities/table-views",
|
||||
"l/ja/user-guide/views-pipelines/capabilities/kanban-views",
|
||||
@@ -3265,11 +3294,12 @@
|
||||
"l/ja/user-guide/views-pipelines/capabilities/fields-and-columns",
|
||||
"l/ja/user-guide/views-pipelines/capabilities/view-settings"
|
||||
]
|
||||
}
|
||||
},
|
||||
"l/ja/user-guide/layout/capabilities/record-pages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "How-Tos",
|
||||
"group": "ハウツー",
|
||||
"pages": [
|
||||
"l/ja/user-guide/views-pipelines/how-tos/create-a-table-view-with-grouping",
|
||||
"l/ja/user-guide/views-pipelines/how-tos/create-a-kanban-view-for-projects",
|
||||
@@ -3288,7 +3318,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/dashboards/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/dashboards/capabilities/dashboards",
|
||||
"l/ja/user-guide/dashboards/capabilities/widgets",
|
||||
@@ -3310,7 +3340,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/permissions-access/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/permissions-access/capabilities/permissions",
|
||||
"l/ja/user-guide/permissions-access/capabilities/sso-configuration"
|
||||
@@ -3330,7 +3360,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/billing/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/billing/capabilities/pricing-plans",
|
||||
"l/ja/user-guide/billing/capabilities/credits"
|
||||
@@ -3350,7 +3380,7 @@
|
||||
"pages": [
|
||||
"l/ja/user-guide/settings/overview",
|
||||
{
|
||||
"group": "Reference",
|
||||
"group": "リファレンス",
|
||||
"pages": [
|
||||
"l/ja/user-guide/settings/capabilities/workspace-settings",
|
||||
"l/ja/user-guide/settings/capabilities/member-management",
|
||||
@@ -3374,11 +3404,85 @@
|
||||
"tab": "開発者",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Overview",
|
||||
"group": "概要",
|
||||
"pages": [
|
||||
"l/ja/developers/introduction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "アプリ",
|
||||
"pages": [
|
||||
{
|
||||
"group": "始めに",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/apps/getting-started/quick-start",
|
||||
"l/ja/developers/extend/apps/getting-started/concepts",
|
||||
"l/ja/developers/extend/apps/getting-started/project-structure",
|
||||
"l/ja/developers/extend/apps/getting-started/local-server",
|
||||
"l/ja/developers/extend/apps/getting-started/scaffolding",
|
||||
"l/ja/developers/extend/apps/getting-started/troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "設定",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/apps/config/overview",
|
||||
"l/ja/developers/extend/apps/config/application",
|
||||
"l/ja/developers/extend/apps/config/roles",
|
||||
"l/ja/developers/extend/apps/config/install-hooks",
|
||||
"l/ja/developers/extend/apps/config/public-assets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "データ",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/apps/data/overview",
|
||||
"l/ja/developers/extend/apps/data/objects",
|
||||
"l/ja/developers/extend/apps/data/extending-objects",
|
||||
"l/ja/developers/extend/apps/data/relations"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "ロジック",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/apps/logic/overview",
|
||||
"l/ja/developers/extend/apps/logic/logic-functions",
|
||||
"l/ja/developers/extend/apps/logic/key-value-store",
|
||||
"l/ja/developers/extend/apps/logic/skills-and-agents",
|
||||
"l/ja/developers/extend/apps/logic/connections"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "レイアウト",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/apps/layout/overview",
|
||||
"l/ja/developers/extend/apps/layout/views",
|
||||
"l/ja/developers/extend/apps/layout/navigation-menu-items",
|
||||
"l/ja/developers/extend/apps/layout/page-layouts",
|
||||
"l/ja/developers/extend/apps/layout/front-components",
|
||||
"l/ja/developers/extend/apps/layout/command-menu-items"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "オペレーション",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/apps/operations/overview",
|
||||
"l/ja/developers/extend/apps/operations/cli",
|
||||
"l/ja/developers/extend/apps/operations/sync-and-recovery",
|
||||
"l/ja/developers/extend/apps/operations/testing",
|
||||
"l/ja/developers/extend/apps/operations/publishing"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "API",
|
||||
"pages": [
|
||||
"l/ja/developers/extend/api",
|
||||
"l/ja/developers/extend/webhooks",
|
||||
"l/ja/developers/extend/oauth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "セルフホスト",
|
||||
"pages": [
|
||||
@@ -3391,7 +3495,9 @@
|
||||
{
|
||||
"group": "貢献",
|
||||
"pages": [
|
||||
"l/ja/developers/contribute/capabilities/local-setup"
|
||||
"l/ja/developers/contribute/capabilities/local-setup",
|
||||
"l/ja/developers/contribute/commands",
|
||||
"l/ja/developers/contribute/style-guide"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ title: カスタムオブジェクト
|
||||
## 上位スキーマ
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="上位スキーマ" />
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="上位スキーマ" />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
@@ -27,7 +27,7 @@ title: カスタムオブジェクト
|
||||
カスタムオブジェクトを追加するには、workspaceMemberが/metadata APIをクエリします。 これにより、メタデータが適切に更新され、メタデータに基づいてGraphQLスキーマが計算され、後で使用するためにGQLキャッシュに保存されます。 これにより、メタデータが適切に更新され、メタデータに基づいてGraphQLスキーマが計算され、後で使用するためにGQLキャッシュに保存されます。 これにより、メタデータが適切に更新され、メタデータに基づいてGraphQLスキーマが計算され、後で使用するためにGQLキャッシュに保存されます。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/server/add-custom-objects.jpeg" alt="カスタムオブジェクトを追加するために/metadata APIをクエリ" />
|
||||
<img src="/images/docs/server/add-custom-objects.jpeg" alt="カスタムオブジェクトを追加するために/metadata APIをクエリ" />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
@@ -35,5 +35,5 @@ title: カスタムオブジェクト
|
||||
データを取得するには、/graphqlエンドポイントを介してクエリを行い、Query Resolverを通過させるプロセスが必要です。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="データを取得するために/graphqlエンドポイントをクエリします" />
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="データを取得するために/graphqlエンドポイントをクエリします" />
|
||||
</div>
|
||||
|
||||
+8
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: バックエンドコマンド
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
## 便利なコマンド
|
||||
@@ -48,6 +49,13 @@ npx nx run twenty-server:database:reset
|
||||
```
|
||||
|
||||
### マイグレーション
|
||||
|
||||
#### Core/Metadata スキーマ (TypeORM) のオブジェクトに対して
|
||||
|
||||
```bash
|
||||
npx nx run twenty-server:database:migrate:generate
|
||||
```
|
||||
|
||||
## 技術スタック
|
||||
|
||||
Twenty は主にバックエンドに NestJS を使用しています。
|
||||
|
||||
+3
-1
@@ -43,7 +43,9 @@ cp .env.example .env
|
||||
## 開発
|
||||
|
||||
<Warning>
|
||||
`zapier`コマンドを実行する前に`yarn build`を実行してください。
|
||||
|
||||
`zapier`コマンドを実行する前に`yarn build`を実行してください。
|
||||
|
||||
</Warning>
|
||||
|
||||
### テスト
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: バグ報告、要望、プルリクエスト
|
||||
icon: bug
|
||||
info: Issue を報告し、機能を要望し、コードで貢献する
|
||||
---
|
||||
|
||||
|
||||
+10
-7
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: ベストプラクティス',
|
||||
icon: star
|
||||
---
|
||||
|
||||
このドキュメントでは、フロントエンド作業におけるベストプラクティスを概要化しています。
|
||||
@@ -8,12 +9,14 @@ title: ベストプラクティス',
|
||||
|
||||
React と Jotai はコードベース内の状態管理を行います。
|
||||
|
||||
### 状態を保存するために `useAtomState` を使用する
|
||||
### Jotai の atom を使用して状態を保存する
|
||||
|
||||
状態を保存するために必要なだけ多くのアトムを作成するのがよいです。
|
||||
|
||||
<Warning>
|
||||
プロップドリリングを必要以上に控えるより、追加のアトムを使用する方が良いです。
|
||||
|
||||
プロップドリリングを必要以上に控えるより、追加のアトムを使用する方が良いです。
|
||||
|
||||
</Warning>
|
||||
|
||||
```tsx
|
||||
@@ -43,7 +46,7 @@ export const MyComponent = () => {
|
||||
|
||||
状態の保存に `useRef` を使用するのは避けてください。
|
||||
|
||||
If you want to store state, you should use `useState` or `useAtomState`.
|
||||
状態を保存したい場合は、`useState` または `useAtomState` を用いた Jotai の atom を使用してください。
|
||||
|
||||
いくつかの再レンダリングを防ぐために `useRef` が必要だと感じた場合は、[再レンダリングの管理方法](#managing-re-renders)を参照してください。
|
||||
|
||||
@@ -80,7 +83,7 @@ If you feel like you need to add a `useEffect` in your root component, you shoul
|
||||
Apollo フックを使用してデータ取得ロジックにも同じことを適用できます。
|
||||
|
||||
```tsx
|
||||
// ❌ 悪い例: データが変化していなくても再レンダーを引き起こす
|
||||
// ❌ 悪い例: データが変化していなくても再レンダリングを引き起こす
|
||||
// useEffect を再評価する必要があるため
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
@@ -101,7 +104,7 @@ export const App = () => (
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ 良い例: データが変化していなければ再レンダーは発生しない
|
||||
// ✅ 良い例: データが変化していなければ再レンダリングは発生しない
|
||||
// useEffect が別の兄弟コンポーネントで再評価されるため
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
@@ -130,9 +133,9 @@ export const App = () => (
|
||||
);
|
||||
```
|
||||
|
||||
### Jotaiファミリー状態とファミリーセレクターを使用する
|
||||
### atom ファミリー状態とセレクターを使用する
|
||||
|
||||
Jotai ファミリー状態とセレクターは、再レンダリングを回避するための優れた方法です。
|
||||
atom ファミリー状態とセレクターは、再レンダリングを回避するための優れた方法です。
|
||||
|
||||
アイテムのリストを保存する必要があるときに有用です。
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: フォルダ構成
|
||||
icon: folder-tree
|
||||
info: 当社のフォルダ構造の詳細な解説
|
||||
---
|
||||
|
||||
@@ -84,7 +85,7 @@ module1
|
||||
|
||||
ステート管理のロジックを含みます。 [Jotai](https://jotai.org) がこれを管理します。
|
||||
|
||||
* セレクター: 派生アトム(`createAtomSelector`を使用)は他のアトムから値を計算し、自動的にメモ化されます。
|
||||
* セレクター: 派生アトム(`createAtomSelector` を使用)は、他のアトムから値を計算し、自動的にメモ化されます。
|
||||
|
||||
Reactの組み込みステート管理は依然としてコンポーネント内のステートを処理します。
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: フロントエンドコマンド
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
## 便利なコマンド
|
||||
@@ -77,7 +78,7 @@ To avoid unnecessary [re-renders](/l/ja/developers/contribute/capabilities/front
|
||||
|
||||
### 状態管理
|
||||
|
||||
[Jotai](https://jotai.org/)は状態管理を処理します。
|
||||
[Jotai](https://jotai.org/)は状態管理を行います。
|
||||
|
||||
状態管理に関する詳細な情報は[ベストプラクティス](/l/ja/developers/contribute/capabilities/frontend-development/best-practices-front#state-management)を参照してください。
|
||||
|
||||
|
||||
+3
-3
@@ -160,7 +160,7 @@ export enum PageHotkeyScope {
|
||||
}
|
||||
```
|
||||
|
||||
内部的には、現在選択されているスコープはアプリケーション全体で共有されるJotaiステートに格納されています:
|
||||
内部的には、現在選択されているスコープはアプリケーション全体で共有されるJotai atomに格納されています:
|
||||
|
||||
```tsx
|
||||
export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
@@ -169,10 +169,10 @@ export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
});
|
||||
```
|
||||
|
||||
しかし、このJotaiステートは手動で処理しないでください! 次のセクションでその使用方法を見ていきます。 次のセクションでその使用方法を見ていきます。 次のセクションでその使用方法を見ていきます。
|
||||
しかし、このatomは手動で扱ってはいけません! 次のセクションでその使用方法を見ていきます。
|
||||
|
||||
## 内部的にはどう機能しているのか?
|
||||
|
||||
[react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro)の上に薄いラッパーを作成し、より効率的にし、不必要な再レンダリングを避けます。
|
||||
|
||||
また、ホットキースコープの状態を処理し、アプリケーション全体で利用できるJotaiステートを作成しました。
|
||||
また、ホットキーのスコープの状態を処理し、アプリケーション全体で利用できるようにするためのJotai atomも作成します。
|
||||
|
||||
+5
-4
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: スタイルガイド
|
||||
icon: paintbrush
|
||||
---
|
||||
|
||||
このドキュメントには、コードを書く際に従うべきルールが含まれています。
|
||||
@@ -8,7 +9,7 @@ title: スタイルガイド
|
||||
|
||||
そのためには、簡潔すぎるよりも少し冗長な方が良いです。
|
||||
|
||||
常に念頭に置いておくべきは、コードは書くより読む方が多いということ、特にオープンソースプロジェクトでは、誰でも貢献できるためです。
|
||||
常に念頭に置いておくべきなのは、コードは書くよりも読む機会の方が多いということです。特に、誰でも貢献できるオープンソースプロジェクトでは、その傾向が強くなります。
|
||||
|
||||
ここでは定義されていない、多くの規則がありますが、リンターにより自動的にチェックされます。
|
||||
|
||||
@@ -150,7 +151,7 @@ type MyType = {
|
||||
|
||||
### 列挙型の代わりに文字列リテラルを使う
|
||||
|
||||
[文字列リテラル](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) は、TypeScriptで列挙型のような値を扱うための推奨方法です。 それらはPickやOmitで拡張するのが簡単で、特にコード補完を伴う開発者の体験を向上させます。 それらはPickやOmitで拡張するのが簡単で、特にコード補完を伴う開発者の体験を向上させます。 それらはPickやOmitで拡張するのが簡単で、特にコード補完を伴う開発者の体験を向上させます。
|
||||
[文字列リテラル](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) は、TypeScriptで列挙型のような値を扱うための推奨方法です。 それらはPickやOmitで拡張するのが簡単で、特にコード補完を伴う開発者の体験を向上させます。 それらはPickやOmitで拡張するのが簡単で、特にコード補完を伴う開発者の体験を向上させます。 Pick や Omit で拡張しやすく、特にコード補完の面で、より良い開発者体験を提供します。
|
||||
|
||||
なぜTypeScriptが列挙型を避けることを推奨しているかは[ここ](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums)をご覧ください。
|
||||
|
||||
@@ -260,7 +261,7 @@ const StyledButton = styled.button`
|
||||
|
||||
## タイプ無しのインポートの強制
|
||||
|
||||
タイプインポートを避けてください。 タイプインポートを避けてください。 この標準を強制するために、Oxlintルールがどのタイプのインポートもチェックして報告します。 これにより、TypeScriptコードの一貫性と可読性が維持されます。 これにより、TypeScriptコードの一貫性と可読性が維持されます。
|
||||
タイプインポートを避けてください。 この標準を強制するために、Oxlint のルールがあらゆる型インポートをチェックして報告します。 これにより、TypeScriptコードの一貫性と可読性が維持されます。
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
@@ -283,7 +284,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
### Oxlintルール
|
||||
|
||||
Oxlint のルール `typescript/consistent-type-imports` は、型専用インポートの標準を強制します。 このルールは、タイプインポート違反のエラーや警告を生成します。
|
||||
Oxlint のルール `typescript/consistent-type-imports` は、型インポート禁止の標準を強制します。 このルールは、タイプインポート違反のエラーや警告を生成します。
|
||||
|
||||
このルールは、意図せず型をインポートしてしまう稀なエッジケースに特化して対処することに留意してください。 TypeScript自体、[TypeScript 3.8 リリースノート](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html)でこのプラクティスを避けています。 ほとんどの状況で、タイプのみのインポートを使用する必要はありません。 ほとんどの状況で、タイプのみのインポートを使用する必要はありません。 ほとんどの状況で、タイプのみのインポートを使用する必要はありません。
|
||||
|
||||
|
||||
+3
-1
@@ -34,7 +34,9 @@ Figma は、デザイナーと開発者の間のコミュニケーションの
|
||||
開発者モードや専用のフレーム選択など、キー機能はサインインしたユーザーのみが利用できます。
|
||||
|
||||
<Warning>
|
||||
アカウントがないと効果的に協力することはできません。
|
||||
|
||||
アカウントがないと効果的に協力することはできません。
|
||||
|
||||
</Warning>
|
||||
|
||||
## Figma 構造
|
||||
|
||||
@@ -1,78 +1,77 @@
|
||||
---
|
||||
title: ローカルセットアップ
|
||||
icon: laptop-code
|
||||
description: 寄稿者(または好奇心旺盛な開発者)のためのガイドで、ローカルにTwentyを実行したい人向けです。
|
||||
---
|
||||
|
||||
## 前提条件
|
||||
|
||||
<Tabs>
|
||||
<Tab title="LinuxとMacOS">
|
||||
Twentyをインストールして使用する前に、以下をコンピュータにインストールしてください。
|
||||
<Tab title="LinuxとmacOS">
|
||||
|
||||
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||
* [Node v24.5.0](https://nodejs.org/en/download)
|
||||
* [yarn v4](https://yarnpkg.com/getting-started/install)
|
||||
* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
|
||||
Twentyをインストールして使用する前に、以下をコンピュータにインストールしてください。
|
||||
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||
* [Node v24.5.0](https://nodejs.org/en/download)
|
||||
* [yarn v4](https://yarnpkg.com/getting-started/install)
|
||||
* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
|
||||
|
||||
<Warning>
|
||||
`npm`は使えません。代わりに`yarn`を使用してください。
|
||||
`npm`は使えません。代わりに`yarn`を使用してください。 Yarnは今Node.jsに同梱されているので、別途インストールする必要はありません。
|
||||
Yarnを有効にするには、まだしていない場合は`corepack enable`を実行するだけです。
|
||||
`npm`は使えません。代わりに`yarn`を使用してください。 Yarnは今Node.jsに同梱されているので、別途インストールする必要はありません。
|
||||
Yarnを有効にするには、まだしていない場合は`corepack enable`を実行するだけです。Yarnを有効にするには、まだしていない場合は`corepack enable`を実行するだけです。
|
||||
</Warning>
|
||||
</Tab>
|
||||
<Warning>
|
||||
`npm`は使えません。代わりに`yarn`を使用してください。
|
||||
`npm`は使えません。代わりに`yarn`を使用してください。 Yarnは今Node.jsに同梱されているので、別途インストールする必要はありません。
|
||||
Yarnを有効にするには、まだしていない場合は`corepack enable`を実行するだけです。
|
||||
`npm`は使えません。代わりに`yarn`を使用してください。 Yarnは今Node.jsに同梱されているので、別途インストールする必要はありません。
|
||||
Yarnを有効にするには、まだしていない場合は`corepack enable`を実行するだけです。Yarnを有効にするには、まだしていない場合は`corepack enable`を実行するだけです。
|
||||
</Warning>
|
||||
|
||||
<Tab title="Windows(WSL)">
|
||||
1. WSLをインストール
|
||||
管理者としてPowerShellを開き、次のコマンドを実行します。
|
||||
</Tab>
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
<Tab title="Windows(WSL)">
|
||||
|
||||
コンピュータを再起動するプロンプトが表示されるはずです。 表示されなければ、手動で再起動してください。 表示されなければ、手動で再起動してください。 表示されなければ、手動で再起動してください。
|
||||
1. WSLをインストール
|
||||
管理者としてPowerShellを開き、次のコマンドを実行します。
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
コンピュータを再起動するプロンプトが表示されるはずです。 表示されなければ、手動で再起動してください。 表示されなければ、手動で再起動してください。 表示されなければ、手動で再起動してください。
|
||||
|
||||
再起動後、PowerShellウィンドウが開き、Ubuntuをインストールします。 これには少し時間がかかるかもしれません。
|
||||
再起動後、PowerShellウィンドウが開き、Ubuntuをインストールします。 これには少し時間がかかるかもしれません。
|
||||
Ubuntuインストールのためにユーザー名とパスワードを設定するプロンプトが表示されます。 これには少し時間がかかるかもしれません。
|
||||
再起動後、PowerShellウィンドウが開き、Ubuntuをインストールします。 これには少し時間がかかるかもしれません。
|
||||
Ubuntuインストールのためにユーザー名とパスワードを設定するプロンプトが表示されます。
|
||||
再起動後、PowerShellウィンドウが開き、Ubuntuをインストールします。 これには少し時間がかかるかもしれません。
|
||||
再起動後、PowerShellウィンドウが開き、Ubuntuをインストールします。 これには少し時間がかかるかもしれません。
|
||||
Ubuntuインストールのためにユーザー名とパスワードを設定するプロンプトが表示されます。
|
||||
|
||||
2. gitをインストールして設定する
|
||||
2. gitをインストールして設定する
|
||||
|
||||
```bash
|
||||
sudo apt-get install git
|
||||
```bash
|
||||
sudo apt-get install git
|
||||
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.name "Your Name"
|
||||
|
||||
git config --global user.email "youremail@domain.com"
|
||||
```
|
||||
git config --global user.email "youremail@domain.com"
|
||||
```
|
||||
|
||||
3. nvm、node.js、yarnをインストールする
|
||||
3. nvm、node.js、yarnをインストールする
|
||||
|
||||
<Warning>
|
||||
`nvm`を使用して正しい`node`バージョンをインストールします。 `.nvmrc`は、すべての寄稿者が同じバージョンを使用することを保証します。
|
||||
`.nvmrc`は、すべての寄稿者が同じバージョンを使用することを保証します。
|
||||
</Warning>
|
||||
<Warning>
|
||||
`nvm`を使用して正しい`node`バージョンをインストールします。 `.nvmrc`は、すべての寄稿者が同じバージョンを使用することを保証します。
|
||||
`.nvmrc`は、すべての寄稿者が同じバージョンを使用することを保証します。
|
||||
</Warning>
|
||||
|
||||
```bash
|
||||
sudo apt-get install curl
|
||||
```bash
|
||||
sudo apt-get install curl
|
||||
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
|
||||
```
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
|
||||
```
|
||||
nvmを使用できるようにするには、ターミナルを閉じて再度開いてください。 次のコマンドを実行します。 次のコマンドを実行します。 次のコマンドを実行します。
|
||||
|
||||
nvmを使用できるようにするには、ターミナルを閉じて再度開いてください。 次のコマンドを実行します。 次のコマンドを実行します。 次のコマンドを実行します。
|
||||
```bash
|
||||
|
||||
```bash
|
||||
nvm install # installs recommended node version
|
||||
|
||||
nvm install # installs recommended node version
|
||||
nvm use # use recommended node version
|
||||
|
||||
nvm use # use recommended node version
|
||||
corepack enable
|
||||
```
|
||||
|
||||
corepack enable
|
||||
```
|
||||
</Tab>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
@@ -82,21 +81,21 @@ description: 寄稿者(または好奇心旺盛な開発者)のためのガ
|
||||
ターミナルで次のコマンドを実行します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="SSH(推奨)">
|
||||
SSHキーの設定をまだ行っていない場合は、[こちら](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh)で学べます。```bash
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
<Tab title="SSH(推奨)">
|
||||
SSHキーの設定をまだ行っていない場合は、[こちら](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh)で学べます。```bash
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
```bash
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="HTTPS">
|
||||
|
||||
```bash
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
```bash
|
||||
git clone https://github.com/twentyhq/twenty.git
|
||||
```
|
||||
|
||||
<Tab title="HTTPS">
|
||||
```bash
|
||||
git clone https://github.com/twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ステップ 2: ルートディレクトリに移動する
|
||||
@@ -112,21 +111,17 @@ cd twenty
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
**オプション1(推奨):** データベースをローカルにプロビジョニングするには:
|
||||
LinuxマシンにPostgresqlをインストールするには、次のリンクを使用してください:[Postgresqlインストール](https://www.postgresql.org/download/linux/)
|
||||
|
||||
LinuxマシンにPostgreSQLをインストールするには、次のリンクを使用してください:[PostgreSQL のインストール](https://www.postgresql.org/download/linux/)
|
||||
```bash
|
||||
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
|
||||
```
|
||||
|
||||
注意:`psql`の前に`sudo -u postgres`を追加して、パーミッションエラーを避ける必要があるかもしれません。
|
||||
|
||||
**オプション2:** dockerをインストールしている場合:
|
||||
|
||||
```bash
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Mac OS">
|
||||
**オプション1(推奨):** `brew`でローカルにデータベースをプロビジョニングするには:
|
||||
|
||||
@@ -138,14 +133,13 @@ cd twenty
|
||||
```
|
||||
|
||||
PostgreSQLサーバーが稼働しているかどうかを確認するには、以下を実行してください。
|
||||
|
||||
```bash
|
||||
brew services list
|
||||
```
|
||||
|
||||
インストーラーは、デフォルトでHomebrew経由でMacOSにインストールする際に`postgres`ユーザーを作成しないかもしれません。 代わりに、macOSのユーザー名(例:「john」)と一致するPostgreSQLロールを作成します。
|
||||
macOSでHomebrew経由でインストールする場合、
|
||||
インストーラーがデフォルトで`postgres`ユーザーを作成しないことがあります。 代わりに、macOSのユーザー名(例:「john」)と一致するPostgreSQLロールを作成します。
|
||||
必要に応じて、`postgres`ユーザーを確認して作成するには、次の手順を実行してください。
|
||||
|
||||
```bash
|
||||
# Connect to PostgreSQL
|
||||
psql postgres
|
||||
@@ -154,70 +148,61 @@ cd twenty
|
||||
```
|
||||
|
||||
psqlプロンプト(postgres=#)で次を実行します。
|
||||
|
||||
```bash
|
||||
# List existing PostgreSQL roles
|
||||
\du
|
||||
```
|
||||
|
||||
```bash
|
||||
# List existing PostgreSQL roles
|
||||
\du
|
||||
```
|
||||
以下のような出力が表示されます。
|
||||
|
||||
```bash
|
||||
Role name | Attributes | Member of
|
||||
-----------+-------------+-----------
|
||||
john | Superuser | {}
|
||||
```
|
||||
```bash
|
||||
Role name | Attributes | Member of
|
||||
-----------+-------------+-----------
|
||||
john | Superuser | {}
|
||||
```
|
||||
|
||||
`postgres`ロールがリストに表示されない場合は、次のステップに進んでください。
|
||||
`postgres`ロールを手動で作成します。
|
||||
|
||||
```bash
|
||||
CREATE ROLE postgres WITH SUPERUSER LOGIN;
|
||||
```
|
||||
|
||||
```bash
|
||||
CREATE ROLE postgres WITH SUPERUSER LOGIN;
|
||||
```
|
||||
これにより、ログイン権限を持つ `postgres` というスーパーユーザーロールが作成されます。
|
||||
|
||||
```bash
|
||||
Role name | Attributes | Member of
|
||||
-----------+-------------+-----------
|
||||
postgres | Superuser | {}
|
||||
john | Superuser | {}
|
||||
```
|
||||
```bash
|
||||
Role name | Attributes | Member of
|
||||
-----------+-------------+-----------
|
||||
postgres | Superuser | {}
|
||||
john | Superuser | {}
|
||||
```
|
||||
|
||||
**オプション2:** dockerをインストールしている場合:
|
||||
|
||||
```bash
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows(WSL)">
|
||||
以下のすべてのステップは、WSLターミナル(仮想マシン内)で実行されます。
|
||||
|
||||
**オプション1:** PostgreSQLをローカルでプロビジョニングするには:
|
||||
Linux仮想マシンにPostgresqlをインストールするには、次のリンクを使用してください:[Postgresqlインストール](https://www.postgresql.org/download/linux/)
|
||||
|
||||
Linux仮想マシンにPostgreSQLをインストールするには、次のリンクを使用してください:[PostgreSQL のインストール](https://www.postgresql.org/download/linux/)
|
||||
```bash
|
||||
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
|
||||
```
|
||||
|
||||
注意:`psql`の前に`sudo -u postgres`を追加して、パーミッションエラーを避ける必要があるかもしれません。
|
||||
|
||||
**オプション2:** dockerをインストールしている場合:
|
||||
WSLでDockerを実行すると、手順が少し複雑になります。
|
||||
追加の手順を含む[Docker Desktop WSL2](https://docs.docker.com/desktop/wsl)の有効化などの追加の手順に精通している場合にのみこのオプションを使用してください。
|
||||
|
||||
```bash
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
データベースに[localhost:5432](localhost:5432)でアクセスできるようになりました。ユーザーは`postgres`、パスワードは`postgres`です。
|
||||
これで、`localhost:5432` でデータベースにアクセスできます。
|
||||
|
||||
上記の Docker オプションを使用した場合、デフォルトの認証情報は、ユーザー `postgres` とパスワード `postgres` です。 ネイティブの PostgreSQL インストールの場合は、マシン上で設定された認証情報とロールを使用してください。
|
||||
|
||||
## ステップ4:Redisデータベース(キャッシュ)をセットアップ
|
||||
|
||||
Twentyは、最良のパフォーマンスを提供するためにredisキャッシュを必要とします。
|
||||
Twenty は、最高のパフォーマンスを提供するために Redis キャッシュを必要とします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
@@ -225,42 +210,37 @@ Twentyは、最良のパフォーマンスを提供するためにredisキャッ
|
||||
LinuxマシンにRedisをインストールするには、次のリンクを使用してください:[Redisインストール](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
|
||||
|
||||
**オプション2:** dockerをインストールしている場合:
|
||||
|
||||
```bash
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Mac OS">
|
||||
**オプション1(推奨):** `brew`でredisをローカルプロビジョニングするには:
|
||||
|
||||
```bash
|
||||
brew install redis
|
||||
```
|
||||
|
||||
Redisサーバーを開始します:
|
||||
`brew services start redis`
|
||||
Redis サーバーを起動する:
|
||||
```bash
|
||||
brew services start redis
|
||||
```
|
||||
|
||||
**オプション2:** dockerをインストールしている場合:
|
||||
|
||||
```bash
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows(WSL)">
|
||||
**オプション1:** Redisをローカルでプロビジョニングするには:
|
||||
Linux仮想マシンにRedisをインストールするには、次のリンクを使用してください:[Redisインストール](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
|
||||
|
||||
**オプション2:** dockerをインストールしている場合:
|
||||
|
||||
```bash
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
クライアントGUIが必要な場合は、[redis insight](https://redis.io/insight/)(無料版利用可能)をお勧めします。
|
||||
クライアント GUI が必要な場合は、[Redis Insight](https://redis.io/insight/)(無料版あり)をお勧めします。
|
||||
|
||||
## ステップ5:環境変数をセットアップします
|
||||
|
||||
@@ -274,7 +254,7 @@ cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
|
||||
```
|
||||
|
||||
<Info>
|
||||
**マルチワークスペースモード:** デフォルトでは、Twenty は単一ワークスペースモードで動作し、ワークスペースは 1 つだけ作成できます。 マルチワークスペース対応を有効にするには(サブドメインベースの機能のテストに有用)、サーバーの `.env` ファイルで `IS_MULTIWORKSPACE_ENABLED=true` を設定してください。 詳細は[マルチワークスペースモード](/l/ja/developers/self-host/capabilities/setup#multi-workspace-mode)を参照してください。
|
||||
**マルチワークスペースモード:** デフォルトでは、Twenty は単一ワークスペースモードで動作し、ワークスペースは 1 つだけ作成できます。 マルチワークスペース対応を有効にするには(サブドメインベースの機能のテストに有用)、サーバーの `.env` ファイルで `IS_MULTIWORKSPACE_ENABLED=true` を設定してください。 詳細は[マルチワークスペースモード](/l/ja/developers/self-host/capabilities/setup#multi-workspace-mode)を参照してください。
|
||||
</Info>
|
||||
|
||||
## ステップ6:依存関係をインストールします
|
||||
@@ -294,15 +274,12 @@ yarn
|
||||
Linuxディストリビューションによっては、Redisサーバーが自動的に開始されるかもしれません。
|
||||
そうでない場合は、[Redisインストールガイド](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/)を参照し、お使いのディストリビューションに合わせて確認してください。
|
||||
</Tab>
|
||||
|
||||
<Tab title="Mac OS">
|
||||
Redisはすでに稼働しているはずです。 稼働していなかった場合は、以下を実行してください:
|
||||
|
||||
Redisはすでに稼働しているはずです。 稼働していなかった場合は、以下を実行してください:
|
||||
```bash
|
||||
brew services start redis
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Windows(WSL)">
|
||||
Linuxディストリビューションによっては、Redisサーバーが自動的に開始されるかもしれません。
|
||||
そうでない場合は、[Redisインストールガイド](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/)を参照し、お使いのディストリビューションに合わせて確認してください。
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: コマンド
|
||||
icon: terminal
|
||||
description: Twenty の開発に役立つコマンド。
|
||||
---
|
||||
|
||||
コマンドはリポジトリのルートから `npx nx` を使用して実行できます。 ターゲットを明示的に指定するには、`npx nx run {project}:{command}` を使用します。
|
||||
|
||||
## アプリの開始
|
||||
|
||||
```bash
|
||||
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
|
||||
npx nx start twenty-server # Backend server (http://localhost:3000)
|
||||
npx nx run twenty-server:worker # Background worker
|
||||
```
|
||||
|
||||
## データベース
|
||||
|
||||
```bash
|
||||
npx nx database:reset twenty-server # Reset and seed database
|
||||
npx nx run twenty-server:database:migrate:prod # Run migrations
|
||||
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
|
||||
```
|
||||
|
||||
## Lint(リント)
|
||||
|
||||
```bash
|
||||
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
|
||||
npx nx lint:diff-with-main twenty-server
|
||||
npx nx lint twenty-front --configuration=fix # Auto-fix
|
||||
```
|
||||
|
||||
## 型チェック
|
||||
|
||||
```bash
|
||||
npx nx typecheck twenty-front
|
||||
npx nx typecheck twenty-server
|
||||
```
|
||||
|
||||
## テスト
|
||||
|
||||
```bash
|
||||
# Frontend
|
||||
npx nx test twenty-front # Jest unit tests
|
||||
npx nx storybook:build twenty-front # Build Storybook
|
||||
npx nx storybook:test twenty-front # Storybook tests
|
||||
|
||||
# Backend
|
||||
npx nx run twenty-server:test:unit # Unit tests
|
||||
npx nx run twenty-server:test:integration # Integration tests
|
||||
npx nx run twenty-server:test:integration:with-db-reset # Integration with DB reset
|
||||
|
||||
# Single file (fastest)
|
||||
npx jest path/to/test.test.ts --config=packages/{project}/jest.config.mjs
|
||||
```
|
||||
|
||||
## GraphQL
|
||||
|
||||
```bash
|
||||
npx nx run twenty-front:graphql:generate # Regenerate types
|
||||
npx nx run twenty-front:graphql:generate --configuration=metadata # Metadata schema
|
||||
```
|
||||
|
||||
## 翻訳
|
||||
|
||||
```bash
|
||||
npx nx run twenty-front:lingui:extract # Extract strings
|
||||
npx nx run twenty-front:lingui:compile # Compile translations
|
||||
```
|
||||
|
||||
## ビルド
|
||||
|
||||
```bash
|
||||
npx nx build twenty-shared # Must be built first
|
||||
npx nx build twenty-front
|
||||
npx nx build twenty-server
|
||||
```
|
||||
@@ -25,7 +25,6 @@ Twentyはオープンソースであり、コミュニティからの貢献を
|
||||
<Card title="バグ報告と要望" icon="bug" href="/l/ja/developers/contribute/capabilities/bug-and-requests">
|
||||
問題を報告する、または機能を要望する
|
||||
</Card>
|
||||
|
||||
<Card title="フロントエンド開発" icon="browser" href="/l/ja/developers/contribute/capabilities/frontend-development">
|
||||
UIに貢献する
|
||||
</Card>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
title: スタイルガイド
|
||||
icon: paintbrush
|
||||
description: Twentyに貢献するためのコード規約とベストプラクティス。
|
||||
---
|
||||
|
||||
## React
|
||||
|
||||
### 関数コンポーネントのみ
|
||||
|
||||
常に名前付きエクスポートの TSX 関数コンポーネントを使用してください。
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
const MyComponent = () => {
|
||||
return <div>Hello World</div>;
|
||||
};
|
||||
export default MyComponent;
|
||||
|
||||
// ✅ Good
|
||||
export function MyComponent() {
|
||||
return <div>Hello World</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### プロパティ
|
||||
|
||||
`{ComponentName}Props` という名前の型を作成する。 分割代入を使用する。 `React.FC` を使用しない。
|
||||
|
||||
```tsx
|
||||
type MyComponentProps = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
|
||||
```
|
||||
|
||||
### 単一変数によるプロップスのスプレッドは禁止
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
const MyComponent = (props: MyComponentProps) => <Other {...props} />;
|
||||
|
||||
// ✅ Good
|
||||
const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1, prop2 }} />;
|
||||
```
|
||||
|
||||
## 状態管理
|
||||
|
||||
### グローバル状態には Jotai の atom を使用
|
||||
|
||||
```tsx
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
|
||||
export const myAtomState = createAtomState<string>({
|
||||
key: 'myAtomState',
|
||||
defaultValue: 'default value',
|
||||
});
|
||||
```
|
||||
|
||||
* prop drilling よりも atom を優先する
|
||||
* 状態管理に `useRef` を使用しない — `useState` または atom を使用する
|
||||
* リストには atom ファミリーとセレクターを使用する
|
||||
|
||||
### 不要な再レンダーを避ける
|
||||
|
||||
* `useEffect` とデータ取得は同一階層のサイドカーコンポーネントに切り出す
|
||||
* `useEffect` よりもイベントハンドラー(`handleClick`、`handleChange`)を優先する
|
||||
* `React.memo()` は使用しない — 代わりに根本原因を修正する
|
||||
* `useCallback` および `useMemo` の使用を制限する
|
||||
|
||||
```tsx
|
||||
// ❌ Bad — useEffect in the same component causes re-renders
|
||||
export const Page = () => {
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
const [dep] = useAtomState(depState);
|
||||
useEffect(() => { setData(dep); }, [dep]);
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
// ✅ Good — extract into sibling
|
||||
export const PageData = () => {
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
const [dep] = useAtomState(depState);
|
||||
useEffect(() => { setData(dep); }, [dep]);
|
||||
return <></>;
|
||||
};
|
||||
export const Page = () => {
|
||||
const [data] = useAtomState(dataState);
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
* **`interface` より `type`** — より柔軟で、合成しやすい
|
||||
* **enum より文字列リテラル** — ただし GraphQL コード生成の enum と内部ライブラリ API は除く
|
||||
* **`any` は禁止** — 厳格な TypeScript を適用
|
||||
* **型インポートは禁止** — 通常のインポートを使用(Oxlint の `typescript/consistent-type-imports` で強制)
|
||||
* **[Zod](https://github.com/colinhacks/zod) を使用**して、型未定義オブジェクトを実行時に検証する
|
||||
|
||||
## JavaScript
|
||||
|
||||
```tsx
|
||||
// Use nullish-coalescing (??) instead of ||
|
||||
const value = process.env.MY_VALUE ?? 'default';
|
||||
|
||||
// Use optional chaining
|
||||
onClick?.();
|
||||
```
|
||||
|
||||
## 命名について
|
||||
|
||||
* **変数**: camelCase、意味がわかる名前(`value` ではなく `email`、`fm` ではなく `fieldMetadata`)
|
||||
* **定数**: SCREAMING_SNAKE_CASE
|
||||
* **型/クラス**: PascalCase
|
||||
* **ファイル/ディレクトリ**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
|
||||
* **イベントハンドラー**: `handleClick` (ハンドラー関数として `onClick` は使用しない)
|
||||
* **コンポーネントのプロップス**: コンポーネント名を接頭辞に付ける(`ButtonProps`)
|
||||
* **スタイル付きコンポーネント**: `Styled` を接頭辞に付ける(`StyledTitle`)
|
||||
|
||||
## スタイリング
|
||||
|
||||
[Linaria](https://github.com/callstack/linaria) の styled components を使用する。 テーマの値を使用し、`px`、`rem`、色のハードコードは避ける。
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
const StyledButton = styled.button`
|
||||
color: #333333;
|
||||
font-size: 1rem;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
|
||||
// ✅ Good
|
||||
const StyledButton = styled.button`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
```
|
||||
|
||||
## インポート
|
||||
|
||||
相対パスの代わりにエイリアスを使用する:
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
import { Foo } from '../../../../../testing/decorators/Foo';
|
||||
|
||||
// ✅ Good
|
||||
import { Foo } from '~/testing/decorators/Foo';
|
||||
import { Bar } from '@/modules/bar/components/Bar';
|
||||
```
|
||||
|
||||
## フォルダ構成
|
||||
|
||||
```
|
||||
front
|
||||
└── modules/ # Feature modules
|
||||
│ └── module1/
|
||||
│ ├── components/
|
||||
│ ├── constants/
|
||||
│ ├── contexts/
|
||||
│ ├── graphql/ (fragments, queries, mutations)
|
||||
│ ├── hooks/
|
||||
│ ├── states/ (atoms, selectors)
|
||||
│ ├── types/
|
||||
│ └── utils/
|
||||
└── pages/ # Route-level components
|
||||
└── ui/ # Reusable UI components (display, input, feedback, ...)
|
||||
```
|
||||
|
||||
* モジュールは他のモジュールからインポートしてよいが、`ui/` は依存関係を持たない状態を保つこと。
|
||||
* モジュール専用コードには `internal/` サブフォルダを使用する。
|
||||
* コンポーネントは 300 行未満、サービスは 500 行未満
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: API
|
||||
icon: plug
|
||||
description: ワークスペースのスキーマから生成される REST と GraphQL の API。
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## テナントごとのスキーマ API
|
||||
|
||||
Twenty に静的な API リファレンスはありません。 各ワークスペースには独自のスキーマがあり、カスタムオブジェクト(たとえば `Invoice`)を追加すると、`Company` や `Person` といった組み込みオブジェクトと同一の REST および GraphQL エンドポイントが即座に提供されます。 API はスキーマから生成されるため、エンドポイントはオブジェクト名やフィールド名を直接使用します — 不透明な ID はありません。
|
||||
|
||||
API キー作成後、**Settings → API & Webhooks** でワークスペース固有の API ドキュメントを利用できます。 実データに対して実際の呼び出しを実行できるインタラクティブなプレイグラウンドが含まれています。
|
||||
|
||||
## 2 種類の API
|
||||
|
||||
**コア API** — `/rest/` と `/graphql/`
|
||||
|
||||
レコードに対する CRUD:People、Companies、Opportunities、およびカスタムオブジェクト。 クエリ、フィルター、リレーションのトラバース。
|
||||
|
||||
**メタデータ API** — `/rest/metadata/` と `/metadata/`
|
||||
|
||||
スキーマ管理:オブジェクト、フィールド、リレーションの作成/変更/削除。 これは、プログラム的にデータモデルを変更する方法です。
|
||||
|
||||
どちらも REST と GraphQL で利用できます。 GraphQL では、バッチアップサートや、単一のクエリでリレーションをトラバースする機能が追加されています。 いずれの場合も、基盤となるデータは同じです。
|
||||
|
||||
## ベース URL
|
||||
|
||||
| 環境 | ベース URL |
|
||||
| ------ | ------------------------- |
|
||||
| クラウド | `https://api.twenty.com/` |
|
||||
| セルフホスト | `https://{your-domain}/` |
|
||||
|
||||
## 認証
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
API キーは **Settings → API & Webhooks → + Create key** で作成します。 すぐにコピーしてください — 表示は一度きりです。 アクセス可能範囲を制限するため、キーは **Settings → Members → Roles → Assignment tab** で特定のロールにスコープできます。
|
||||
|
||||
<VimeoEmbed videoId="928786722" title="API キーの作成" />
|
||||
|
||||
OAuth ベースのアクセス(ユーザーに代わって動作する外部アプリ)については、[OAuth](/l/ja/developers/extend/oauth) を参照してください。
|
||||
|
||||
## バッチ操作
|
||||
|
||||
REST と GraphQL はどちらも、1 リクエストあたり最大 60 レコードのバッチ処理(作成、更新、削除)に対応しています。 GraphQL は、`CreateCompanies` のような複数形の名前を使用して、バッチアップサート(1 回の呼び出しで作成または更新)もサポートします。
|
||||
|
||||
## API レートリミット
|
||||
|
||||
| 制限 | 値 |
|
||||
| ------ | ------------------- |
|
||||
| リクエスト | 1 分あたり 100 回の呼び出し |
|
||||
| バッチサイズ | 1 回の呼び出しあたり 60 レコード |
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: アプリケーション設定
|
||||
description: "`defineApplication` を使用して、アプリの識別情報、デフォルトのロール、変数、およびマーケットプレイスのメタデータを宣言します。"
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
すべてのアプリは、`defineApplication` の呼び出しを厳密に 1 つ持つ必要があります。 ここでは次の内容を宣言します。
|
||||
|
||||
* **Identity** — ユニバーサル識別子、表示名、説明。
|
||||
* **Permissions** — ロジック関数およびフロントコンポーネントがどのロールで実行されるか。
|
||||
* **Variables** *(optional)* — コードから環境変数として利用できるキーと値のペア。
|
||||
* **Pre-install / post-install hooks** *(optional)* — [Logic Functions](/l/ja/developers/extend/apps/logic/logic-functions) を参照してください。
|
||||
|
||||
```ts src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d',
|
||||
displayName: 'My Twenty App',
|
||||
description: 'My first Twenty app',
|
||||
applicationVariables: {
|
||||
DEFAULT_RECIPIENT_NAME: {
|
||||
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
|
||||
description: 'Default recipient name for postcards',
|
||||
value: 'Jane Doe',
|
||||
isSecret: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
注記:
|
||||
|
||||
* `universalIdentifier` フィールドは、あなたが所有する安定した ID です。 一度生成し、その後の同期でも安定して維持してください。
|
||||
* `applicationVariables` は関数やフロントコンポーネントの環境変数になります。 ロジック関数(サーバーサイド)では、`process.env.VARIABLE_NAME` として利用できます。 フロントコンポーネントでは、`twenty-sdk/front-component` の `getApplicationVariable('VARIABLE_NAME')` を使用します。 `isSecret: true` が指定された変数は、ロジック関数にのみインジェクトされます。 フロントコンポーネントには、秘密ではない変数のみが渡されます。
|
||||
* デフォルトのロールは、[`defineApplicationRole()`](/l/ja/developers/extend/apps/config/roles) でマークされたロールファイルから自動的に検出されます。`defineApplication()` から参照する必要はありません。
|
||||
* プレインストール関数とポストインストール関数は、マニフェストのビルド中に自動検出されます—`defineApplication()` で参照する必要はありません。
|
||||
* 後方互換性のために `defaultRoleUniversalIdentifier` を明示的に渡すことも依然としてサポートされていますが、`defineApplicationRole()` が推奨されるため、非推奨となっています。
|
||||
|
||||
## デフォルトの関数ロール
|
||||
|
||||
[`defineApplicationRole()`](/l/ja/developers/extend/apps/config/roles) で宣言されたロールは、アプリのロジック関数とフロントコンポーネントがアクセスできる内容を制御します。
|
||||
|
||||
* `TWENTY_APP_ACCESS_TOKEN` として注入される実行時トークンは、このロールに基づいて生成されます。
|
||||
* 型付き API クライアントは、そのロールに付与された権限に制限されます。
|
||||
* 最小権限の原則に従い、関数に必要な権限のみを宣言してください。
|
||||
|
||||
新しいアプリをスキャフォルドすると、CLI は `src/roles/default-role.ts` にスターターロールファイルを作成します。 詳細は [Roles & Permissions](/l/ja/developers/extend/apps/config/roles) を参照してください。
|
||||
|
||||
## マーケットプレイスのメタデータ
|
||||
|
||||
アプリを[公開](/l/ja/developers/extend/apps/operations/publishing)する予定がある場合、これらの任意フィールドでマーケットプレイスでの表示方法を制御できます:
|
||||
|
||||
| フィールド | 説明 |
|
||||
| ------------------ | -------------------------------------------------------------------------------- |
|
||||
| `author` | 作成者または会社名 |
|
||||
| `category` | マーケットプレイスのフィルタリング用のアプリカテゴリ |
|
||||
| `logoUrl` | アプリのロゴへのパス(例:`public/logo.png`) |
|
||||
| `screenshots` | スクリーンショットのパス配列(例:`public/screenshot-1.png`) |
|
||||
| `aboutDescription` | 「About」タブ向けの詳細な Markdown 説明。 省略した場合、マーケットプレイスは npm のパッケージにある `README.md` を使用します。 |
|
||||
| `websiteUrl` | 自社ウェブサイトへのリンク |
|
||||
| `termsUrl` | 利用規約へのリンク |
|
||||
| `emailSupport` | サポート用メールアドレス |
|
||||
| `issueReportUrl` | 課題トラッカーへのリンク |
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
title: インストールフック
|
||||
description: インストールの前後にロジックを実行して、シードデータの投入、レコードのバックアップ、アップグレードの検証を行います。
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
インストールフックは、インストールまたはアップグレードのライフサイクル中に実行される特別なロジック関数です。 これらは通常の[ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions)と同じハンドラーランタイムを共有し、`InstallPayload` を受け取りますが、`definePostInstallLogicFunction()` と `definePreInstallLogicFunction()` という独自の define 関数で宣言され、通常のトリガーモデル (HTTP、cron、データベースイベント) の外側で動作します。
|
||||
|
||||
各アプリは、**プレインストール関数は最大 1 つ**、**ポストインストール関数も最大 1 つ**まで定義できます。 どちらかが複数検出された場合、マニフェストのビルドはエラーになります。
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ install flow │
|
||||
│ │
|
||||
│ upload package → [pre-install] → metadata migration → │
|
||||
│ generate SDK → [post-install] │
|
||||
│ │
|
||||
│ old schema visible new schema visible │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="definePostInstallLogicFunction" description="ワークスペースのメタデータマイグレーションが適用された後に実行されます">
|
||||
|
||||
ポストインストール関数は、アプリのワークスペースへのインストールが完了した後に自動的に実行されます。 サーバーは、アプリのメタデータが同期され、SDK クライアントが生成された**後に**これを実行します。そのため、ワークスペースは完全に利用できる状態となり、新しいスキーマが適用されています。 代表的なユースケースには、デフォルトデータの投入、初期レコードの作成、ワークスペース設定の構成、またはサードパーティのサービスでのリソースのプロビジョニングが含まれます。
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
|
||||
const handler = async (payload: InstallPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: false,
|
||||
shouldRunSynchronously: false,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
CLI を使用して、いつでもポストインストール関数を手動で実行することもできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec --postInstall
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
* ポストインストール関数は `definePostInstallLogicFunction()` を使用します — トリガー設定(`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`)を省いた専用のバリアントです。
|
||||
* ハンドラーは `InstallPayload`(`{ previousVersion?: string; newVersion: string }`)を受け取ります。`newVersion` は現在インストール中のバージョン、`previousVersion` は以前にインストールされていたバージョン(新規インストール時は `undefined`)です。 これらの値を使用して新規インストールとアップグレードを区別し、バージョン固有のマイグレーションロジックを実行します。
|
||||
* **フックが実行されるタイミング**: 既定では新規インストール時のみ。 アプリを以前のバージョンからアップグレードする際にも実行したい場合は、`shouldRunOnVersionUpgrade: true` を指定してください。 省略した場合、このフラグは既定で `false` となり、アップグレード時にはフックはスキップされます。
|
||||
* **実行モデル — 既定は非同期、同期はオプトイン**: `shouldRunSynchronously` フラグは、ポストインストールが実行される*方法*を制御します。
|
||||
* `shouldRunSynchronously: false` *(既定)* — フックは `retryLimit: 3` で**メッセージキューに投入**され、ワーカー内で非同期に実行されます。 ジョブがキューに投入されるとすぐにインストールのレスポンスが返るため、処理が遅い、または失敗するハンドラーでも呼び出し元をブロックしません。 ワーカーは最大 3 回まで再試行します。 **長時間実行のジョブに使用** — 大規模データセットのシーディング、低速なサードパーティ API の呼び出し、外部リソースのプロビジョニングなど、妥当な HTTP 応答時間枠を超える可能性のある処理。
|
||||
* `shouldRunSynchronously: true` — フックは**インストールフロー内でインライン実行**されます(プレインストールと同じエグゼキューター)。 ハンドラーが完了するまでインストールリクエストはブロックされ、スローした場合はインストールの呼び出し元が `POST_INSTALL_ERROR` を受け取ります。 自動再試行はありません。 **応答前に完了必須の高速な処理に使用** — 例: ユーザーへのバリデーションエラーの表示、インストール呼び出し直後にクライアントが依存するクイックセットアップ。 ポストインストールが実行される時点ではメタデータのマイグレーションはすでに適用済みである点に注意してください。そのため、同期モードで失敗してもスキーマ変更は**ロールバックされません** — エラーが表出するだけです。
|
||||
* ハンドラーが冪等であることを必ず確認してください。 非同期モードではキューが最大 3 回まで再試行する場合があります。いずれのモードでも、`shouldRunOnVersionUpgrade: true` の場合はアップグレード時にフックが再度実行されることがあります。
|
||||
* ハンドラー内では(他のロジック関数と同様に)環境変数 `APPLICATION_ID`、`APP_ACCESS_TOKEN`、`API_URL` が利用できます。そのため、アプリにスコープされたアプリケーションアクセストークンで Twenty API を呼び出せます。
|
||||
* アプリケーションごとにポストインストール関数は 1 つのみ許可されます。 複数検出された場合、マニフェストのビルドはエラーになります。
|
||||
* ビルド時に、関数の `universalIdentifier`、`shouldRunOnVersionUpgrade`、`shouldRunSynchronously` はアプリケーションマニフェストの `postInstallLogicFunction` フィールドに自動的に付与されます。[`defineApplication()`](/l/ja/developers/extend/apps/config/application) でそれらを参照する必要はありません。
|
||||
* デフォルトのタイムアウトは 300 秒(5 分)に設定されており、データシーディングのような長めのセットアップ作業を許容します。
|
||||
* **dev モードでは実行されません**: アプリがローカル登録(`yarn twenty dev`)された場合、サーバーはインストールフローを完全にスキップし、CLI ウォッチャー経由でファイルを直接同期します。したがって、`shouldRunSynchronously` に関わらず、dev モードではポストインストールは実行されません。 稼働中のワークスペースに対して手動でトリガーするには、`yarn twenty dev:function:exec --postInstall` を使用します。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="definePreInstallLogicFunction" description="ワークスペースのメタデータマイグレーションが適用される前に実行されます">
|
||||
|
||||
プレインストール関数は、インストール中に自動的に実行されるロジック関数で、**ワークスペースのメタデータマイグレーションが適用される前**に実行されます。 ポストインストール(`InstallPayload`)と同じペイロード型を共有しますが、インストールフローの早い段階に位置するため、これから行われるマイグレーションが依存する状態を準備できます。典型的な用途には、データのバックアップ、新しいスキーマとの互換性の検証、再構成または削除予定のレコードのアーカイブなどがあります。
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
|
||||
const handler = async (payload: InstallPayload): Promise<void> => {
|
||||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: true,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
CLI を使用して、いつでもプレインストール関数を手動で実行することもできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec --preInstall
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
* プレインストール関数は `definePreInstallLogicFunction()` を使用します — ポストインストールと同じ特化設定ですが、異なるライフサイクルスロットに割り当てられます。
|
||||
* プレインストールとポストインストールの両ハンドラーは同じ `InstallPayload` 型(`{ previousVersion?: string; newVersion: string }`)を受け取ります。 一度インポートして、両方のフックで再利用してください。
|
||||
* **フックが実行されるタイミング**: ワークスペースのメタデータマイグレーション(`synchronizeFromManifest`)の直前に配置されます。 実行前に、サーバーは純粋に追加のみの「簡易同期」を実行し、ワークスペースのメタデータに**新しい**バージョンのプレインストール関数を登録します — それ以外には一切手を触れません — その後に実行されます。 この同期は追加のみのため、ハンドラーが実行される時点でも前バージョンのオブジェクト、フィールド、データはそのまま残っています。マイグレーション前の状態を安全に読み取り、バックアップできます。
|
||||
* **実行モデル**: プレインストールは**同期的**に実行され、**インストールをブロック**します。 ハンドラーがスローした場合、スキーマ変更が適用される前にインストールは中止され、ワークスペースは一貫した状態のまま前のバージョンに留まります。 これは意図的な設計です。プレインストールは、リスクの高いアップグレードを拒否できる最後の機会です。
|
||||
* ポストインストールと同様に、アプリケーションごとにプレインストール関数は 1 つのみ許可されます。 ビルド時に、アプリケーションマニフェストの `preInstallLogicFunction` に自動的に追加されます。
|
||||
* **dev モードでは実行されません**: ポストインストールと同様に、ローカル登録されたアプリではインストールフローが完全にスキップされるため、`yarn twenty dev` 下ではプレインストールは実行されません。 手動でトリガーするには、`yarn twenty dev:function:exec --preInstall` を使用します。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="プレインストール vs ポストインストール: どちらをいつ使うか" description="適切なインストールフックの選択">
|
||||
|
||||
両方のフックは同じインストールフローの一部で、同じ `InstallPayload` を受け取ります。 違いは、ワークスペースのメタデータマイグレーションとの相対的な実行タイミング(**いつ**実行されるか)であり、それによって安全に扱えるデータが変わります。
|
||||
|
||||
プレインストールは常に**同期的**です(インストールをブロックでき、中止することも可能)。 ポストインストールは**既定で非同期**(ワーカーにエンキューされ自動再試行あり)ですが、`shouldRunSynchronously: true` で同期実行にオプトインできます。 各モードの使い分けは、上の `definePostInstallLogicFunction` のアコーディオンを参照してください。
|
||||
|
||||
**新しいスキーマの存在を前提とする処理には `post-install` を使用してください。** これは一般的なケースです:
|
||||
|
||||
* 新規に追加されたオブジェクトやフィールドに対するデフォルトデータのシーディング(初期レコード、デフォルトビュー、デモコンテンツの作成)。
|
||||
* アプリにクレデンシャルが付与された後に、サードパーティサービスにウェブフックを登録すること。
|
||||
* 同期済みメタデータに依存するセットアップを完了させるために自前の API を呼び出すこと。
|
||||
* あらゆるアップグレード時に状態を調整すべき、冪等な「存在を保証する」ロジック — `shouldRunOnVersionUpgrade: true` と組み合わせます。
|
||||
|
||||
例 — インストール後に既定の `PostCard` レコードをシードする:
|
||||
|
||||
```ts src/logic-functions/post-install.ts
|
||||
import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
import { createClient } from './generated/client';
|
||||
|
||||
const handler = async ({ previousVersion }: InstallPayload): Promise<void> => {
|
||||
if (previousVersion) return; // fresh installs only
|
||||
|
||||
const client = createClient();
|
||||
await client.postCard.create({
|
||||
data: { title: 'Welcome to Postcard', content: 'Your first card!' },
|
||||
});
|
||||
};
|
||||
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210',
|
||||
name: 'post-install',
|
||||
description: 'Seeds a welcome post card after install.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: false,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
**`pre-install` を、マイグレーションが既存データを破壊または破損しかねない場合に使用してください。** プレインストールは*以前の*スキーマに対して実行され、失敗するとアップグレードをロールバックするため、リスクのある処理に最適です:
|
||||
|
||||
* **削除または再構成される予定のデータのバックアップ** — 例: v2 でフィールドを削除するため、マイグレーション実行前にその値を別のフィールドへコピーする、またはストレージへエクスポートする必要がある場合。
|
||||
* **新しい制約により無効化されるレコードのアーカイブ** — 例: フィールドが `NOT NULL` になるため、先に null 値の行を削除または修正する必要がある場合。
|
||||
* 互換性を**検証し、現在のデータをクリーンに移行できない場合はアップグレードを拒否** — ハンドラーからスローすれば、変更が適用されないままインストールが中止されます。 これは、マイグレーションの途中で非互換性に気付くよりも安全です。
|
||||
* 関連付けが失われるスキーマ変更に先立って、**データの名称変更やキーの再割り当て**を行う。
|
||||
|
||||
例 — 破壊的なマイグレーションの前にレコードをアーカイブする:
|
||||
|
||||
```ts src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define';
|
||||
import { createClient } from './generated/client';
|
||||
|
||||
const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise<void> => {
|
||||
// Only the 1.x → 2.x upgrade drops the legacy `notes` field.
|
||||
if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = createClient();
|
||||
const legacyRecords = await client.postCard.findMany({
|
||||
where: { notes: { isNotNull: true } },
|
||||
});
|
||||
|
||||
if (legacyRecords.length === 0) return;
|
||||
|
||||
// Copy legacy `notes` into the new `description` field before the migration
|
||||
// drops the `notes` column. If this fails, the upgrade is aborted and the
|
||||
// workspace stays on v1 with all data intact.
|
||||
await Promise.all(
|
||||
legacyRecords.map((record) =>
|
||||
client.postCard.update({
|
||||
where: { id: record.id },
|
||||
data: { description: record.notes },
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
|
||||
name: 'pre-install',
|
||||
description: 'Backs up legacy notes into description before the v2 migration.',
|
||||
timeoutSeconds: 300,
|
||||
shouldRunOnVersionUpgrade: true,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
**経験則:**
|
||||
|
||||
| やりたいこと… | 使用 |
|
||||
| ----------------------------------------------- | --------------------------------------------------------------- |
|
||||
| デフォルトデータのシーディング、ワークスペースの構成、外部リソースの登録 | `post-install` |
|
||||
| インストールの応答をブロックすべきでない長時間のシーディングやサードパーティ呼び出しを実行する | `post-install`(既定 — `shouldRunSynchronously: false`、ワーカーの再試行あり) |
|
||||
| インストール呼び出しが返った直後に呼び出し元が依存する高速なセットアップを実行する | `post-install`(`shouldRunSynchronously: true` を指定) |
|
||||
| 次のマイグレーションで失われるデータを読み取る、またはバックアップする | `pre-install` |
|
||||
| 既存データを破損させる恐れのあるアップグレードを拒否する | `pre-install`(ハンドラーからスロー) |
|
||||
| すべてのアップグレードで調整処理を実行する | `post-install`(`shouldRunOnVersionUpgrade: true` を指定) |
|
||||
| 初回インストール時のみの一度限りのセットアップを行う | `post-install`(`shouldRunOnVersionUpgrade: false` を指定、既定) |
|
||||
|
||||
<Note>
|
||||
迷ったら、既定は**post-install**にしましょう。 マイグレーション自体が破壊的で、消える前の状態を先に扱う必要がある場合にのみ、pre-install を使ってください。
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: 概要
|
||||
description: アプリ自体の設定 — アプリのアイデンティティ、デフォルト権限、およびインストール時に実行される内容を構成します。
|
||||
icon: screwdriver-wrench
|
||||
---
|
||||
|
||||
Twenty アプリの **config layer** は、アプリのアイデンティティ、保持する権限、インストールやアップグレード時に実行されるコードなど、アプリを *プラットフォームに対して* 説明するものです。 これらの宣言は新しいデータ構造や実行時の動作を追加するものではなく、アプリが *何者であるか*、そして *どのようにセットアップするか* を Twenty に伝える役割を持ちます。
|
||||
|
||||
```text
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Application — identity, default role, variables, │
|
||||
│ marketplace metadata │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────┐ │
|
||||
│ │ Role — what the app's logic functions can read │ │
|
||||
│ │ and write (referenced by Application) │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ (at install / upgrade time)
|
||||
┌──────────────────────────────────┐
|
||||
│ Pre-install hook │ before metadata migration
|
||||
└──────────────────────────────────┘
|
||||
┌──────────────────────────────────┐
|
||||
│ Post-install hook │ after metadata migration
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
## このセクションについて
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="アプリケーション設定" icon="rocket" href="/l/ja/developers/extend/apps/config/application">
|
||||
`defineApplication` — アイデンティティ、デフォルトロール、変数、マーケットプレイスのメタデータ。
|
||||
</Card>
|
||||
<Card title="ロールと権限" icon="shield-halved" href="/l/ja/developers/extend/apps/config/roles">
|
||||
`defineRole` — アプリのロジック関数が読み書きできる内容を宣言します。
|
||||
</Card>
|
||||
<Card title="インストールフック" icon="wrench" href="/l/ja/developers/extend/apps/config/install-hooks">
|
||||
`definePreInstallLogicFunction` と `definePostInstallLogicFunction` — データのバックアップ、デフォルト値の投入、アップグレードの検証を行います。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## 各要素同士の関係
|
||||
|
||||
* **アプリケーション** がエントリポイントです。 すべてのアプリには `defineApplication()` 呼び出しが 1 つだけ存在し、その呼び出しは 1 つの **ロール** をデフォルトとして指します。
|
||||
* **ロール** は、アプリのロジック関数とフロントコンポーネントが読み書きできる内容を制御します。 最小権限の原則に従い、コードが実際に必要とする権限だけを付与してください。
|
||||
* **インストールフック** はインストールまたはアップグレード時に実行されます。メタデータのマイグレーション前にプレインストールが実行されるため(リスクの高いアップグレードを拒否できます)、マイグレーション後にポストインストールが実行され(新しいスキーマに対してデフォルトデータを投入できます)。
|
||||
|
||||
<Note>
|
||||
インストールフックは [logic function](/l/ja/developers/extend/apps/logic/logic-functions) ランタイムを共有します。つまり、同じハンドラーシグネチャ、同じ環境変数、同じ型付き API クライアントを使用します。ただし、独自の define 関数で宣言され、通常のトリガーモデル(HTTP、cron、データベースイベント)とは別に存在します。
|
||||
</Note>
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: 公開アセット
|
||||
description: 静的ファイル(画像、アイコン、フォントなど)を、public/ フォルダーを通じてアプリと一緒に配信します。
|
||||
icon: folder-open
|
||||
---
|
||||
|
||||
アプリのルートにある `public/` フォルダーには、実行時に必要な画像、アイコン、フォントなどの静的ファイルが格納されます。 これらのファイルはビルドに自動的に含まれ、開発モード中に同期され、サーバーにアップロードされます。
|
||||
|
||||
`public/` に配置されたファイルは次のとおりです:
|
||||
|
||||
* **公開アクセス可能** — サーバーに同期されると、アセットはパブリック URL で配信されます。 アクセスには認証は不要です。
|
||||
* **フロントコンポーネントで利用可能** — アセットの URL を使用して、React コンポーネント内に画像、アイコン、その他のメディアを表示します。
|
||||
* **ロジック関数で利用可能** — メール、API レスポンス、その他のサーバーサイドロジックでアセットの URL を参照します。
|
||||
* **マーケットプレイスのメタデータに使用** — `defineApplication()` の `logoUrl` と `screenshots` フィールドは、このフォルダー内のファイルを参照します(例:`public/logo.png`)。 アプリを公開すると、これらがマーケットプレイスに表示されます。
|
||||
* **開発モードで自動同期** — `public/` のファイルを追加、更新、削除すると、自動的にサーバーへ同期されます。 再起動は不要です。
|
||||
* **ビルドに含まれる** — `yarn twenty dev:build` はすべての公開アセットを配布物にバンドルします。
|
||||
|
||||
## `getPublicAssetUrl` で公開アセットにアクセスする
|
||||
|
||||
`twenty-sdk` の `getPublicAssetUrl` ヘルパーを使用して、`public/` ディレクトリ内のファイルのフル URL を取得します。 これはロジック関数とフロントコンポーネントの両方で機能します。
|
||||
|
||||
**ロジック関数内:**
|
||||
|
||||
```ts src/logic-functions/send-invoice.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import { getPublicAssetUrl } from 'twenty-sdk/utils';
|
||||
|
||||
const handler = async (): Promise<any> => {
|
||||
const logoUrl = getPublicAssetUrl('logo.png');
|
||||
const invoiceUrl = getPublicAssetUrl('templates/invoice.png');
|
||||
|
||||
// Fetch the file content (no auth required — public endpoint)
|
||||
const response = await fetch(invoiceUrl);
|
||||
const buffer = await response.arrayBuffer();
|
||||
|
||||
return { logoUrl, size: buffer.byteLength };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'a1b2c3d4-...',
|
||||
name: 'send-invoice',
|
||||
description: 'Sends an invoice with the app logo',
|
||||
timeoutSeconds: 10,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
**フロントコンポーネント内:**
|
||||
|
||||
```tsx src/front-components/company-card.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { getPublicAssetUrl } from 'twenty-sdk/utils';
|
||||
|
||||
const CompanyCard = () => {
|
||||
const logoUrl = getPublicAssetUrl('logo.png');
|
||||
|
||||
return <img src={logoUrl} alt="App logo" />;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '...',
|
||||
name: 'company-card',
|
||||
component: CompanyCard,
|
||||
});
|
||||
```
|
||||
|
||||
`path` 引数はアプリの `public/` フォルダーからの相対パスです。 `getPublicAssetUrl('logo.png')` と `getPublicAssetUrl('public/logo.png')` のどちらも同じ URL に解決されます—`public/` プレフィックスが付いている場合は自動的に取り除かれます。
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
title: 役割と権限
|
||||
description: アプリのロジック関数とフロントコンポーネントが読み取りおよび書き込みできるオブジェクトとフィールドを宣言します。
|
||||
icon: shield-halved
|
||||
---
|
||||
|
||||
**ロール**とは権限セットのことであり、アプリが読み取りまたは書き込みできるオブジェクト、参照できるフィールド、および使用できるプラットフォームレベルの機能を定義します。 すべてのアプリのロジック関数とフロントコンポーネントは、`defineApplicationRole()` でマークされたロールの権限を継承します(下記の[The default function role](#the-default-function-role)を参照してください)。
|
||||
|
||||
```ts src/roles/restricted-company-role.ts
|
||||
import {
|
||||
defineRole,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-sdk/define';
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: '2c80f640-2083-4803-bb49-003e38279de6',
|
||||
label: 'My new role',
|
||||
description: 'A role that can be used in your workspace',
|
||||
canReadAllObjectRecords: false,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canUpdateAllSettings: false,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToUsers: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
objectPermissions: [
|
||||
{
|
||||
objectUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: true,
|
||||
canSoftDeleteObjectRecords: false,
|
||||
canDestroyObjectRecords: false,
|
||||
},
|
||||
],
|
||||
fieldPermissions: [
|
||||
{
|
||||
objectUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||||
fieldUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name
|
||||
.universalIdentifier,
|
||||
canReadFieldValue: false,
|
||||
canUpdateFieldValue: false,
|
||||
},
|
||||
],
|
||||
permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS],
|
||||
});
|
||||
```
|
||||
|
||||
## 行レベルセキュリティ
|
||||
|
||||
オブジェクトおよびフィールド権限は、ロールが操作できる *オブジェクトとフィールド* を決定します。 **行レベル
|
||||
権限述語** はさらに進んで、ロールが閲覧および操作できる *レコード* を決定します。たとえば、各外部ユーザーが自分自身のレコードだけを閲覧できるセルフサービスロールのようなケースです。
|
||||
|
||||
ロールに対して `rowLevelPermissionPredicates` を使って述語を宣言します。 マニフェストの他の要素と同様に、各述語はそれ自身の `universalIdentifier` を持ち、オブジェクトおよびフィールドをそれぞれの `universalIdentifier`、`operand`、そして(オプションで)クエリ時に値が注入される workspaceMember フィールドによって参照します。これにより、「レコードの所有者リレーションが現在のワークスペースメンバー **である**」ことを表現できます。
|
||||
|
||||
```ts src/roles/partner-role.ts
|
||||
import {
|
||||
defineRole,
|
||||
RowLevelPermissionPredicateOperand,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk/define';
|
||||
|
||||
import { ACCOUNT_OWNER_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/account-owner.field';
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: 'c3c1dc2e-1a08-4de5-abb7-2139b3d99343',
|
||||
label: 'Partner',
|
||||
description: 'External partner — sees only its own records',
|
||||
canBeAssignedToUsers: true,
|
||||
objectPermissions: [
|
||||
{
|
||||
objectUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: true,
|
||||
},
|
||||
],
|
||||
rowLevelPermissionPredicates: [
|
||||
{
|
||||
universalIdentifier: 'd0f0c1a2-3b4c-4d5e-8f60-111111111111',
|
||||
objectUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||||
fieldUniversalIdentifier: ACCOUNT_OWNER_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
operand: RowLevelPermissionPredicateOperand.IS,
|
||||
workspaceMemberFieldUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workspaceMember.fields.id
|
||||
.universalIdentifier,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
述語はマニフェスト内に含まれて出荷されるため、インストールやアップグレードのたびに、ロールと一緒にまとめて作成・更新・削除されます。同期を保つための、別個のポストインストール手順は不要です。
|
||||
|
||||
### 述語とグループの組み合わせ
|
||||
|
||||
デフォルトでは、ロールの述語は `AND` で組み合わされます。 それらの一部を `OR` で組み合わせたり(あるいはロジックをネストしたり)するには、`rowLevelPermissionPredicateGroups` エントリを宣言し、各述語から
|
||||
`predicateGroupUniversalIdentifier` を通じてそのエントリを参照させます。 このロールでは、パートナーが、自分が**所有している**か、あるいは**連絡窓口**となっている Opportunity を閲覧できます:
|
||||
|
||||
```ts src/roles/partner-opportunities-role.ts
|
||||
import {
|
||||
defineRole,
|
||||
RowLevelPermissionPredicateGroupLogicalOperator,
|
||||
RowLevelPermissionPredicateOperand,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk/define';
|
||||
|
||||
const OPPORTUNITY = STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity;
|
||||
const CURRENT_MEMBER =
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workspaceMember.fields.id
|
||||
.universalIdentifier;
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: 'b2a1c0d9-8e7f-4a6b-9c5d-222222222222',
|
||||
label: 'Partner (opportunities)',
|
||||
canBeAssignedToUsers: true,
|
||||
objectPermissions: [
|
||||
{
|
||||
objectUniversalIdentifier: OPPORTUNITY.universalIdentifier,
|
||||
canReadObjectRecords: true,
|
||||
},
|
||||
],
|
||||
rowLevelPermissionPredicateGroups: [
|
||||
{
|
||||
universalIdentifier: 'c3b2a1d0-9f8e-4b7a-8d6c-333333333333',
|
||||
objectUniversalIdentifier: OPPORTUNITY.universalIdentifier,
|
||||
logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.OR,
|
||||
},
|
||||
],
|
||||
rowLevelPermissionPredicates: [
|
||||
{
|
||||
universalIdentifier: 'd4c3b2a1-0e9f-4c8b-9e7d-444444444444',
|
||||
objectUniversalIdentifier: OPPORTUNITY.universalIdentifier,
|
||||
fieldUniversalIdentifier: OPPORTUNITY.fields.owner.universalIdentifier,
|
||||
operand: RowLevelPermissionPredicateOperand.IS,
|
||||
workspaceMemberFieldUniversalIdentifier: CURRENT_MEMBER,
|
||||
predicateGroupUniversalIdentifier: 'c3b2a1d0-9f8e-4b7a-8d6c-333333333333',
|
||||
},
|
||||
{
|
||||
universalIdentifier: 'e5d4c3b2-1f0e-4d9c-8f8e-555555555555',
|
||||
objectUniversalIdentifier: OPPORTUNITY.universalIdentifier,
|
||||
fieldUniversalIdentifier:
|
||||
OPPORTUNITY.fields.pointOfContact.universalIdentifier,
|
||||
operand: RowLevelPermissionPredicateOperand.IS,
|
||||
workspaceMemberFieldUniversalIdentifier: CURRENT_MEMBER,
|
||||
predicateGroupUniversalIdentifier: 'c3b2a1d0-9f8e-4b7a-8d6c-333333333333',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
注記:
|
||||
|
||||
* すべての述語とグループに安定した `universalIdentifier`(任意の uuid)を付与してください。これはアップグレードにまたがってエンティティを特定するキーとなり、述語はこの識別子でグループを参照します。
|
||||
* 述語は、あなたのアプリが所有するオブジェクトやフィールド、または Twenty の標準オブジェクトを参照できます。
|
||||
* 行レベルセキュリティは、それを含むプランのワークスペースに対して適用されます。その他のプランでも述語は引き続き同期されますが、単に適用されないだけです。
|
||||
|
||||
## デフォルトの関数ロール
|
||||
|
||||
新しいアプリをスキャフォルドすると、CLI は `defineApplicationRole()` で宣言されたデフォルトのロールファイルを作成します。
|
||||
|
||||
```ts src/roles/default-role.ts
|
||||
import { defineApplicationRole } from 'twenty-sdk/define';
|
||||
|
||||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
||||
'b648f87b-1d26-4961-b974-0908fd991061';
|
||||
|
||||
export default defineApplicationRole({
|
||||
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
label: 'Default function role',
|
||||
description: 'Default role for function Twenty client',
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canUpdateAllSettings: false,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToUsers: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
objectPermissions: [],
|
||||
fieldPermissions: [],
|
||||
permissionFlagUniversalIdentifiers: [],
|
||||
});
|
||||
```
|
||||
|
||||
`defineApplicationRole()` は `defineRole()` の軽量なラッパーであり、インストール時にアプリケーションのデフォルトとして使用されるロールをフラグ付けします。 検証は `defineRole` と同一ですが、ビルドパイプラインがその `universalIdentifier` をアプリケーションマニフェストの `defaultRoleUniversalIdentifier` に自動的に関連付けるため、[`defineApplication`](/l/ja/developers/extend/apps/config/application) から参照する必要はありません。
|
||||
|
||||
注記:
|
||||
|
||||
* 各アプリにつき `defineApplicationRole(...)` は **1 つだけ** 許可されます。2 つ以上見つかった場合、マニフェストのビルドは失敗します。
|
||||
* アプリが提供する**追加**ロールには、`defineApplicationRole()` ではなく `defineRole()` を使用してください。
|
||||
* `defineApplication()` で `defaultRoleUniversalIdentifier` を明示的に設定することは後方互換性のため引き続きサポートされていますが、非推奨であり、代わりに `defineApplicationRole()` の使用が推奨されます。
|
||||
|
||||
## ベストプラクティス
|
||||
|
||||
* スキャフォールドされたロールから開始し、徐々に制限を強めてください。デフォルトでは幅広い読み取りアクセスが付与されていますが、本番環境でこれをそのまま使いたいケースはほとんどありません。
|
||||
* `objectPermissions` と `fieldPermissions` を、関数が実際に必要とするオブジェクトとフィールドだけに置き換えてください。
|
||||
* `permissionFlagUniversalIdentifiers` はプラットフォームレベルの機能へのアクセスを制御します。 できるだけ最小限に保ってください。
|
||||
* 動作例: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts)。
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: オブジェクトの拡張
|
||||
description: 標準の Twenty オブジェクト(Person、Company など)にフィールドを追加します。 または、`defineField` を使用して他のアプリのオブジェクトにフィールドを追加します。
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
`defineField()` を使用すると、あなたが所有していないオブジェクト ― Person や Company のような標準の Twenty オブジェクトや、他のインストール済みアプリから提供されるオブジェクト ― にフィールドを追加できます。 [`defineObject`](/l/ja/developers/extend/apps/data/objects) 内で宣言されるインラインフィールドと異なり、スタンドアロンフィールドでは、どのオブジェクトを拡張するかを指定するために `objectUniversalIdentifier` が必要です。
|
||||
|
||||
```ts src/fields/company-loyalty-tier.field.ts
|
||||
import { defineField, FieldType } from 'twenty-sdk/define';
|
||||
|
||||
export default defineField({
|
||||
universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890',
|
||||
objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object
|
||||
name: 'loyaltyTier',
|
||||
type: FieldType.SELECT,
|
||||
label: 'Loyalty Tier',
|
||||
icon: 'IconStar',
|
||||
options: [
|
||||
{ value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' },
|
||||
{ value: 'SILVER', label: 'Silver', position: 1, color: 'gray' },
|
||||
{ value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 主なポイント
|
||||
|
||||
* `objectUniversalIdentifier` は対象オブジェクトを識別します。 標準の Twenty オブジェクトの場合は、`twenty-sdk` から定数を import します:
|
||||
|
||||
```ts
|
||||
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||||
|
||||
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier
|
||||
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier
|
||||
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier
|
||||
// …
|
||||
```
|
||||
|
||||
* フィールドを **`defineObject()` 内でインライン定義する** 場合、`objectUniversalIdentifier` は **不要** です — 親オブジェクトから継承されます。
|
||||
|
||||
* `defineField()` は、`defineObject()` で作成していないオブジェクトにフィールドを追加する唯一の方法です。
|
||||
|
||||
* ファイルの配置場所は任意です。 慣例としては `src/fields/\<name>.field.ts` ですが、SDK は `src/` 内の任意の場所にあるフィールドを検出します。
|
||||
|
||||
* 標準ページレイアウト(例:Task や Company の詳細ページ)にタブを追加するには、`twenty-sdk/define` の `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS` とともに [`definePageLayoutTab`](/l/ja/developers/extend/apps/layout/page-layouts#definepagelayouttab) を使用します。
|
||||
|
||||
## 既存のオブジェクトへのリレーションの追加
|
||||
|
||||
リレーションフィールド(例: カスタムオブジェクトを標準の `Person` にリンクする)を追加するには、`FieldType.RELATION` を指定して `defineField()` を使用します。 パターンはインラインのリレーションと同じですが、`objectUniversalIdentifier` を明示的に設定します。 双方向のパターンについては、[Relations](/l/ja/developers/extend/apps/data/relations) を参照してください。
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: オブジェクト
|
||||
description: defineObject を使用して、新しいレコードタイプ(独自のフィールドを持つカスタムテーブル)を宣言します。
|
||||
icon: table
|
||||
---
|
||||
|
||||
カスタム**オブジェクト**は、アプリがワークスペースに追加する新しいレコードタイプです。ポストカード、請求書、サブスクリプションなど、あなたのドメインに特化したあらゆるものを定義できます。 各オブジェクトは、そのスキーマ(フィールド、リレーション、デフォルト値)と、同期やデプロイをまたいで維持される安定したユニバーサル識別子を宣言します。
|
||||
|
||||
```ts src/objects/post-card.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
|
||||
enum PostCardStatus {
|
||||
DRAFT = 'DRAFT',
|
||||
SENT = 'SENT',
|
||||
DELIVERED = 'DELIVERED',
|
||||
RETURNED = 'RETURNED',
|
||||
}
|
||||
|
||||
export default defineObject({
|
||||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||||
nameSingular: 'postCard',
|
||||
namePlural: 'postCards',
|
||||
labelSingular: 'Post Card',
|
||||
labelPlural: 'Post Cards',
|
||||
description: 'A post card object',
|
||||
icon: 'IconMail',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
|
||||
name: 'content',
|
||||
type: FieldType.TEXT,
|
||||
label: 'Content',
|
||||
description: "Postcard's content",
|
||||
icon: 'IconAbc',
|
||||
},
|
||||
{
|
||||
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
|
||||
name: 'recipientName',
|
||||
type: FieldType.FULL_NAME,
|
||||
label: 'Recipient name',
|
||||
icon: 'IconUser',
|
||||
},
|
||||
{
|
||||
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
|
||||
name: 'recipientAddress',
|
||||
type: FieldType.ADDRESS,
|
||||
label: 'Recipient address',
|
||||
icon: 'IconHome',
|
||||
},
|
||||
{
|
||||
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
|
||||
name: 'status',
|
||||
type: FieldType.SELECT,
|
||||
label: 'Status',
|
||||
icon: 'IconSend',
|
||||
defaultValue: `'${PostCardStatus.DRAFT}'`,
|
||||
options: [
|
||||
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
|
||||
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
|
||||
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
|
||||
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
|
||||
],
|
||||
},
|
||||
{
|
||||
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
|
||||
name: 'deliveredAt',
|
||||
type: FieldType.DATE_TIME,
|
||||
label: 'Delivered at',
|
||||
icon: 'IconCheck',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 主なポイント
|
||||
|
||||
* `universalIdentifier` は、デプロイをまたいで一意かつ安定している必要があります。
|
||||
* 各フィールドには、`name`、`type`、`label`、および自身の安定した `universalIdentifier` が必要です。
|
||||
* `fields` 配列は任意です。カスタムフィールドなしでオブジェクトを定義できます。
|
||||
* ここで定義されたインラインフィールドには `objectUniversalIdentifier` は**不要**です — 親オブジェクトから継承されます。 所有していないオブジェクトにフィールドを追加するには、[`defineField()`](/l/ja/developers/extend/apps/data/extending-objects) を使用します。
|
||||
* `yarn twenty dev:add object` を使用すれば、新しいオブジェクトをスキャフォルドできます。名前、フィールド、リレーションシップの設定をガイドしてくれます。 [アーキテクチャ → エンティティのスキャフォールディング](/l/ja/developers/extend/apps/getting-started/scaffolding) を参照してください。
|
||||
|
||||
<Note>
|
||||
**ベースフィールドは自動的に追加されます。** カスタムオブジェクトを定義すると、Twenty は `id`、`name`、`createdAt`、`updatedAt`、`createdBy`、`updatedBy`、`deletedAt` などの標準フィールドを自動的に作成します。 これらを `fields` 配列で宣言する必要はありません。カスタムフィールドのみを追加してください。 同じ名前でフィールドを宣言することでデフォルトフィールドを上書きすることもできますが、これはほとんどの場合お勧めできません。
|
||||
</Note>
|
||||
|
||||
## デフォルト値
|
||||
|
||||
文字列リテラルのデフォルト値は、文字列**の内部で**シングルクォートで囲む必要があります。つまり、`defaultValue: "'Draft'"` のように書き、`defaultValue: "Draft"` のようには書きません。 そのため上記の `status` フィールドでは、`` `'${PostCardStatus.DRAFT}'` `` を使用しています。
|
||||
|
||||
引用符で囲まれていない文字列は、レコード作成時に評価される計算済みデフォルト値用に予約されています。
|
||||
|
||||
* `'uuid'` — `UUID` フィールド用に UUID を生成します。
|
||||
* `'now'` — 現在のタイムスタンプ(`DATE_TIME` フィールド用)を表します。
|
||||
|
||||
同じ規則が、複合デフォルトの文字列サブフィールド(例: `ACTOR` フィールド上の `{ source: "'MANUAL'" }`)および `SELECT` / `MULTI_SELECT` の値にも適用されます。 引用符で囲まれていないリテラル文字列のデフォルト値がある場合、アプリのビルド時に警告が発生します。
|
||||
|
||||
## 次のステップ
|
||||
|
||||
* **このオブジェクトを他のオブジェクトに接続する** — 双方向リレーションパターンについては [Relations](/l/ja/developers/extend/apps/data/relations) を参照してください。
|
||||
* **他のアプリのオブジェクトにフィールドを追加する** — `defineField()` については [Extending Objects](/l/ja/developers/extend/apps/data/extending-objects) を参照してください。
|
||||
* **このオブジェクトを UI に表示する** — サイドバーに表示するには、[Views](/l/ja/developers/extend/apps/layout/views) および [Navigation Menu Items](/l/ja/developers/extend/apps/layout/navigation-menu-items) を参照してください。
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: 概要
|
||||
description: ワークスペースにアプリが追加するデータ ― オブジェクト、フィールド、リレーション ― の形を整えます。
|
||||
icon: database
|
||||
---
|
||||
|
||||
Twenty アプリの**データレイヤー**とは、アプリがワークスペースに*追加*するデータのことです。宣言する新しいレコードタイプ、既存オブジェクトに追加するカラム、およびそれらのレコード同士がどのように接続されるかを指します。
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Object — a record type, e.g. PostCard │
|
||||
│ ├─ Field (name, type, label) │
|
||||
│ ├─ Field │
|
||||
│ └─ Relation (link to another object) │
|
||||
└──────────────────────────────────────────────────┘
|
||||
│
|
||||
├── lives in your app, OR
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Standard / other apps' objects │
|
||||
│ └─ Field added by your app via defineField │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## このセクションについて
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="オブジェクト" icon="table" href="/l/ja/developers/extend/apps/data/objects">
|
||||
`defineObject` — 固有のフィールドを持つ新しいレコードタイプを宣言します。
|
||||
</Card>
|
||||
<Card title="オブジェクトの拡張" icon="wand-magic-sparkles" href="/l/ja/developers/extend/apps/data/extending-objects">
|
||||
`defineField` — 標準オブジェクトや他のアプリのオブジェクトにフィールドを追加します。
|
||||
</Card>
|
||||
<Card title="関連" icon="diagram-project" href="/l/ja/developers/extend/apps/data/relations">
|
||||
オブジェクト間の双方向 `MANY_TO_ONE` / `ONE_TO_MANY` 接続。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## エンティティの概要
|
||||
|
||||
| エンティティ | 目的 | 定義方法 |
|
||||
| ---------- | ------------------------------------------------------------------------------- | -------------------------------------- |
|
||||
| **オブジェクト** | 固有のフィールドを持つ新しいカスタムレコードタイプ(例: PostCard、Invoice) | `defineObject()` |
|
||||
| **フィールド** | オブジェクト上のカラム。 スタンドアロンフィールドは、作成していないオブジェクトも拡張できます(例: Company に `loyaltyTier` を追加) | `defineField()` |
|
||||
| **リレーション** | 2 つのオブジェクト間の双方向リンク ― 両側がフィールドとして宣言されます | `defineField()` と `FieldType.RELATION` |
|
||||
| **インデックス** | オブジェクトの 1 つに対する定期的なクエリを高速化するためのデータベースインデックス | `defineIndex()` |
|
||||
|
||||
SDK はビルド時に AST 解析を通じてこれらを検出するため、ファイル構成は自由です ― 慣例としては `src/objects/`、`src/fields/`、`src/indexes/` です。 安定した `universalIdentifier` UUID が、デプロイをまたいであらゆるものを結び付けます。
|
||||
|
||||
## インデックス(任意)
|
||||
|
||||
アプリは、オブジェクトと一緒にインデックスを同梱しておくことで、繰り返し実行されるクエリを高速に保つことができます。 最も一般的なケースは、頻繁に読み取るステータス列や外部キー列です。
|
||||
|
||||
```ts src/indexes/post-card-status.index.ts
|
||||
import { defineIndex } from 'twenty-sdk/define';
|
||||
|
||||
import {
|
||||
POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
STATUS_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
} from '../objects/post-card.object';
|
||||
|
||||
export default defineIndex({
|
||||
universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff0',
|
||||
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff1',
|
||||
fieldUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### 一意のインデックス
|
||||
|
||||
`defineIndex` は、単一列と複数列のどちらの一意性でも、`isUnique: true` を受け付けます。 これは推奨されるプリミティブです — `defineField({ isUnique: true })` は非推奨であり、今後のリリースで削除されます。
|
||||
|
||||
```ts
|
||||
defineIndex({
|
||||
universalIdentifier: '…',
|
||||
objectUniversalIdentifier: PERSON_UNIVERSAL_IDENTIFIER,
|
||||
isUnique: true,
|
||||
fields: [{ universalIdentifier: '…', fieldUniversalIdentifier: EMAIL_FIELD_UNIVERSAL_IDENTIFIER }],
|
||||
});
|
||||
```
|
||||
|
||||
### その他の制約
|
||||
|
||||
* 部分的な `WHERE` 句は管理者の管理下にあり、アプリ側で宣言することはできません。
|
||||
* 各オブジェクトにはカスタムインデックスを最大 10 個まで設定できます(フレームワーク自身のインデックスはカウントされません)。
|
||||
|
||||
`fields` 配列は、Postgres に使わせたい順序に並べてください ― 電話帳のように、最も左の列が先になります。 インデックスにはコストがかかります。テーブルへの書き込みのたびに更新されます。 必要とするクエリがある場合にのみインデックスを追加してください。
|
||||
|
||||
<Note>
|
||||
**Application Config** や **Roles & Permissions** をお探しですか? それらは、アプリが追加するデータではなくアプリ自体を記述するものであり、[Config](/l/ja/developers/extend/apps/config/overview) 配下にあります。 **Connections**(Linear、GitHub、Slack OAuth)をお探しですか? それらはロジック関数*から*呼び出されるために存在し、[Logic](/l/ja/developers/extend/apps/logic/connections) 配下にあります。
|
||||
</Note>
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
title: 関連
|
||||
description: 双方向の MANY_TO_ONE / ONE_TO_MANY リレーションでオブジェクト同士を接続します。
|
||||
icon: diagram-project
|
||||
---
|
||||
|
||||
リレーションは 2 つのオブジェクト同士を関連付けます。 Twenty におけるリレーションは常に **双方向** です — すべてのリレーションには 2 つの側面があり、それぞれが相手を参照するフィールドとして宣言されます。
|
||||
|
||||
| リレーションタイプ | 説明 | 外部キーあり? |
|
||||
| ------------- | --------------------------------- | -------------------- |
|
||||
| `MANY_TO_ONE` | このオブジェクトの多数のレコードが、対象の 1 件のレコードを指す | はい(`joinColumnName`) |
|
||||
| `ONE_TO_MANY` | このオブジェクトの 1 件のレコードが、対象の多数のレコードを持つ | いいえ(逆側) |
|
||||
|
||||
## リレーションの仕組み
|
||||
|
||||
すべてのリレーションには、互いを参照する**2 つのフィールド**が必要です:
|
||||
|
||||
1. **MANY_TO_ONE** 側 — 外部キーを保持するオブジェクト側に存在します。
|
||||
2. **ONE_TO_MANY** 側 — コレクションを所有するオブジェクト側に存在します。
|
||||
|
||||
両方のフィールドは `FieldType.RELATION` を使用し、`relationTargetFieldMetadataUniversalIdentifier` を介して相互参照します。
|
||||
|
||||
## 例: PostCard は多数の Recipient を持つ
|
||||
|
||||
`PostCard` は多数の `PostCardRecipient` レコードに送信できます。 各受取人は厳密に 1 件の PostCard に属します。
|
||||
|
||||
**ステップ 1: PostCard に ONE_TO_MANY 側を定義**(「1」の側):
|
||||
|
||||
```ts src/fields/post-card-recipients-on-post-card.field.ts
|
||||
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
|
||||
|
||||
// Export so the other side can reference it
|
||||
export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111';
|
||||
// Import from the other side
|
||||
import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field';
|
||||
|
||||
export default defineField({
|
||||
universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
|
||||
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
type: FieldType.RELATION,
|
||||
name: 'postCardRecipients',
|
||||
label: 'Post Card Recipients',
|
||||
icon: 'IconUsers',
|
||||
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
|
||||
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID,
|
||||
universalSettings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**ステップ 2: PostCardRecipient に MANY_TO_ONE 側を定義**(「多」の側 — 外部キーを保持):
|
||||
|
||||
```ts src/fields/post-card-on-post-card-recipient.field.ts
|
||||
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
|
||||
|
||||
// Export so the other side can reference it
|
||||
export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222';
|
||||
// Import from the other side
|
||||
import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field';
|
||||
|
||||
export default defineField({
|
||||
universalIdentifier: POST_CARD_FIELD_ID,
|
||||
objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
|
||||
type: FieldType.RELATION,
|
||||
name: 'postCard',
|
||||
label: 'Post Card',
|
||||
icon: 'IconMail',
|
||||
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: OnDeleteAction.CASCADE,
|
||||
joinColumnName: 'postCardId',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
**循環インポート:** 両方のリレーションフィールドは互いの `universalIdentifier` を参照します。 循環インポートの問題を避けるため、各ファイルからフィールド ID を名前付き定数としてエクスポートし、相手のファイルでインポートしてください。 ビルドシステムがコンパイル時にこれらを解決します。
|
||||
</Note>
|
||||
|
||||
## 標準オブジェクトとのリレーション
|
||||
|
||||
組み込みの Twenty オブジェクト(Person、Company など)とリレーションを作成するには、`STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` を使用します:
|
||||
|
||||
```ts src/fields/person-on-self-hosting-user.field.ts
|
||||
import {
|
||||
defineField,
|
||||
FieldType,
|
||||
RelationType,
|
||||
OnDeleteAction,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk/define';
|
||||
import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object';
|
||||
|
||||
export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333';
|
||||
export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444';
|
||||
|
||||
export default defineField({
|
||||
universalIdentifier: PERSON_FIELD_ID,
|
||||
objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER,
|
||||
type: FieldType.RELATION,
|
||||
name: 'person',
|
||||
label: 'Person',
|
||||
description: 'Person matching with the self hosting user',
|
||||
isNullable: true,
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
|
||||
relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: OnDeleteAction.SET_NULL,
|
||||
joinColumnName: 'personId',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## リレーションフィールドのプロパティ
|
||||
|
||||
| プロパティ | 必須 | 説明 |
|
||||
| ------------------------------------------------- | -------------- | ------------------------------------------------------------------- |
|
||||
| `type` | はい | `FieldType.RELATION` でなければなりません |
|
||||
| `relationTargetObjectMetadataUniversalIdentifier` | はい | 対象オブジェクトの `universalIdentifier` |
|
||||
| `relationTargetFieldMetadataUniversalIdentifier` | はい | 対象オブジェクト上の対応するフィールドの `universalIdentifier` |
|
||||
| `universalSettings.relationType` | はい | `RelationType.MANY_TO_ONE` または `RelationType.ONE_TO_MANY` |
|
||||
| `universalSettings.onDelete` | MANY_TO_ONE のみ | 参照先レコードが削除されたときの動作: `CASCADE`、`SET_NULL`、`RESTRICT`、または `NO_ACTION` |
|
||||
| `universalSettings.joinColumnName` | MANY_TO_ONE のみ | 外部キーのデータベース列名(例: `postCardId`) |
|
||||
|
||||
## インラインのリレーションフィールド
|
||||
|
||||
[`defineObject`](/l/ja/developers/extend/apps/data/objects) の中で、リレーションを直接宣言することもできます。 インラインで宣言する場合は、`objectUniversalIdentifier` を省略します — 親オブジェクトから継承されます:
|
||||
|
||||
```ts
|
||||
export default defineObject({
|
||||
universalIdentifier: '...',
|
||||
nameSingular: 'postCardRecipient',
|
||||
// ...
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: POST_CARD_FIELD_ID,
|
||||
type: FieldType.RELATION,
|
||||
name: 'postCard',
|
||||
label: 'Post Card',
|
||||
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
|
||||
universalSettings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
onDelete: OnDeleteAction.CASCADE,
|
||||
joinColumnName: 'postCardId',
|
||||
},
|
||||
},
|
||||
// … other fields
|
||||
],
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: 概念
|
||||
description: Twenty アプリの動作 — エンティティモデル、サンドボックス化、およびインストールライフサイクル。
|
||||
icon: sitemap
|
||||
---
|
||||
|
||||
Twenty アプリは、カスタムオブジェクト、ロジック、UI コンポーネント、AI 機能でワークスペースを拡張する TypeScript パッケージです。 それらは、完全なサンドボックス化と権限制御のもとで Twenty プラットフォーム上で実行されます。
|
||||
|
||||
## アプリの仕組み
|
||||
|
||||
アプリは、`twenty-sdk` パッケージの `defineEntity()` 関数を使って宣言された**エンティティ**の集合です。 SDK はビルド時に AST 解析によってこれらの宣言を検出し、**マニフェスト**を生成します—アプリがワークスペースに追加する内容を完全に記述したものです。 これらの関数はビルド時に設定を検証し、IDE の自動補完と型安全性を提供します。
|
||||
|
||||
```
|
||||
your-app/
|
||||
├── src/
|
||||
│ ├── application-config.ts ← defineApplication (required, one per app)
|
||||
│ ├── roles/ ← defineRole
|
||||
│ ├── objects/ ← defineObject
|
||||
│ ├── fields/ ← defineField
|
||||
│ ├── logic-functions/ ← defineLogicFunction
|
||||
│ ├── front-components/ ← defineFrontComponent
|
||||
│ ├── skills/ ← defineSkill
|
||||
│ ├── agents/ ← defineAgent
|
||||
│ ├── views/ ← defineView
|
||||
│ ├── navigation-menu-items/ ← defineNavigationMenuItem
|
||||
│ └── page-layouts/ ← definePageLayout
|
||||
├── public/ ← Static assets (images, icons)
|
||||
└── package.json
|
||||
```
|
||||
|
||||
<Note>
|
||||
**ファイル構成は自由です。** エンティティの検出は AST ベースです—ファイルの場所に関係なく、SDK は `export default defineEntity(...)` の呼び出しを見つけます。 上記のフォルダー構成は慣例であり、必須条件ではありません。
|
||||
</Note>
|
||||
|
||||
## エンティティタイプ
|
||||
|
||||
| エンティティ | 目的 | ドキュメント |
|
||||
| ----------------- | ----------------------------- | ----------------------------------------------------------------------------- |
|
||||
| **アプリケーション** | アプリの識別情報、デフォルトロール、変数 | [Application Config](/l/ja/developers/extend/apps/config/application) |
|
||||
| **ロール** | オブジェクトとフィールドの権限セット | [Roles & Permissions](/l/ja/developers/extend/apps/config/roles) |
|
||||
| **Object** | フィールドを持つカスタムレコードタイプ | [Objects](/l/ja/developers/extend/apps/data/objects) |
|
||||
| **フィールド** | 他のアプリのオブジェクトにフィールドを追加 | [Extending Objects](/l/ja/developers/extend/apps/data/extending-objects) |
|
||||
| **リレーション** | オブジェクト間の双方向リンク | [Relations](/l/ja/developers/extend/apps/data/relations) |
|
||||
| **ロジック関数** | トリガーを備えたサーバーサイド TypeScript | [ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions) |
|
||||
| **スキル** | 再利用可能な AI エージェントの指示 | [スキルとエージェント](/l/ja/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **エージェント** | カスタムプロンプトを備えた AI エージェント | [スキルとエージェント](/l/ja/developers/extend/apps/logic/skills-and-agents) |
|
||||
| **接続プロバイダー** | サードパーティ API 用の OAuth 資格情報 | [Connections](/l/ja/developers/extend/apps/logic/connections) |
|
||||
| **ビュー** | 事前設定済みのレコード一覧ビュー | [Views](/l/ja/developers/extend/apps/layout/views) |
|
||||
| **ナビゲーションメニュー項目** | カスタムのサイドバー項目 | [Navigation Menu Items](/l/ja/developers/extend/apps/layout/navigation-menu-items) |
|
||||
| **ページレイアウト** | レコードの詳細ページ上のタブとウィジェット | [Page Layouts](/l/ja/developers/extend/apps/layout/page-layouts) |
|
||||
| **フロントコンポーネント** | Twenty 内でサンドボックス化された React UI | [フロントコンポーネント](/l/ja/developers/extend/apps/layout/front-components) |
|
||||
| **コマンドメニュー項目** | クイックアクションと Cmd+K エントリ | [Command Menu Items](/l/ja/developers/extend/apps/layout/command-menu-items) |
|
||||
|
||||
## サンドボックス化
|
||||
|
||||
* **ロジック関数**は、サーバー上の分離された Node.js プロセスで実行されます。 データへのアクセスは型付き API クライアント経由に限定され、アプリのロール権限の範囲にスコープされます。
|
||||
* **フロントコンポーネント**は Remote DOM を用いた Web Workers 内で実行されます—メインページからサンドボックス化されつつ、ネイティブの DOM 要素(iframe ではありません)をレンダリングします。 メッセージパッシング型のホスト API を介して Twenty と通信します。
|
||||
* **権限**は API レベルで強制されます。 実行時トークン(`TWENTY_APP_ACCESS_TOKEN`)は、`defineApplication()` で定義されたロールに基づいて生成されます。
|
||||
|
||||
## アプリのライフサイクル
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Development │
|
||||
│ npx create-twenty-app → yarn twenty dev (live sync) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Build & Deploy │
|
||||
│ yarn twenty dev:build → yarn twenty app:publish │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Install flow │
|
||||
│ upload → [pre-install] → metadata migration → │
|
||||
│ generate SDK → [post-install] │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Publish │
|
||||
│ npm publish → appears in Twenty marketplace │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
* **`yarn twenty dev`** — ソースファイルを監視し、接続された Twenty サーバーへ変更をライブ同期します。 スキーマが変更されると、型付き API クライアントは自動的に再生成されます。
|
||||
* **`yarn twenty dev:build`** — TypeScript をコンパイルし、esbuild でロジック関数とフロントコンポーネントをバンドルして、マニフェストを生成します。
|
||||
* **プリ/ポストインストールフック** — インストール中に実行されるオプションの関数。 詳細は [Install Hooks](/l/ja/developers/extend/apps/config/install-hooks) を参照してください。
|
||||
|
||||
## 次のステップ
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="設定" icon="screwdriver-wrench" href="/l/ja/developers/extend/apps/config/overview">
|
||||
アプリの識別情報、デフォルトロール、およびインストールフック。
|
||||
</Card>
|
||||
<Card title="データ" icon="database" href="/l/ja/developers/extend/apps/data/overview">
|
||||
オブジェクト、フィールド、および双方向リレーション。
|
||||
</Card>
|
||||
<Card title="ロジック" icon="bolt" href="/l/ja/developers/extend/apps/logic/overview">
|
||||
ロジック関数、スキル、エージェント、OAuth 接続。
|
||||
</Card>
|
||||
<Card title="レイアウト" icon="table-columns" href="/l/ja/developers/extend/apps/layout/overview">
|
||||
ビュー、ナビゲーション、ページレイアウト、フロントコンポーネント。
|
||||
</Card>
|
||||
<Card title="オペレーション" icon="rocket" href="/l/ja/developers/extend/apps/operations/overview">
|
||||
CLI、テスト、リモート、CI、アプリの公開。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: ローカルサーバー
|
||||
description: ローカルの Twenty Docker サーバーを管理します — 起動、停止、アップグレード、並列テスト用インスタンス、および手動での SDK セットアップ。
|
||||
icon: server
|
||||
---
|
||||
|
||||
## ローカルサーバーの管理
|
||||
|
||||
ローカルの Twenty コンテナを操作するには `yarn twenty docker:*` を使用します:
|
||||
|
||||
| コマンド | 機能 |
|
||||
| -------------------------------------- | --------------------------- |
|
||||
| `yarn twenty docker:start` | サーバーを起動します(必要に応じてイメージを取得) |
|
||||
| `yarn twenty docker:start 2.2.0` | 特定のサーバーバージョンを起動する |
|
||||
| `yarn twenty docker:start --port 3030` | カスタムポートで起動 |
|
||||
| `yarn twenty docker:stop` | サーバーを停止します(データは保持) |
|
||||
| `yarn twenty docker:status` | URL、バージョン、ログイン資格情報を表示 |
|
||||
| `yarn twenty docker:logs` | サーバーログをストリーム表示 |
|
||||
| `yarn twenty docker:reset` | データを消去してクリーンに開始 |
|
||||
| `yarn twenty docker:upgrade` | `twenty-app-dev` の最新イメージを取得 |
|
||||
| `yarn twenty docker:upgrade 2.2.0` | 特定のバージョンにアップグレードする |
|
||||
|
||||
データは再起動をまたいで、2 つの Docker ボリューム(PostgreSQL 用の `twenty-app-dev-data`、ファイル用の `twenty-app-dev-storage`)に永続化されます。 `reset` を使用して、すべてを消去します。
|
||||
|
||||
## サーバーのバージョンを固定する
|
||||
|
||||
バージョンが指定されていない場合、`docker:start` は `package.json` 内のアプリの `engines.twenty` の範囲からバージョンを解決します。これは、アプリのインストール時にサーバーが検証に使用する範囲と同じです。 このコマンドは、その範囲を満たす最新の公開済み `twenty-app-dev` イメージを起動し、フィールドが存在しない場合や公開済みバージョンが一致しない場合は `latest` にフォールバックします。
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"engines": {
|
||||
"twenty": ">=2.2.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
単回の実行でその範囲を上書きするには、バージョンを明示的に指定します: `yarn twenty docker:start 2.3.0`。 すでに別バージョンのコンテナが存在する場合、`docker:start` はその場でアップグレードします(データボリュームを保持したままコンテナを再作成します)。
|
||||
|
||||
## サーバーイメージのアップグレード
|
||||
|
||||
`yarn twenty docker:upgrade` は最新のイメージを取得してダイジェストを比較し、実際に変更があった場合にのみコンテナを再作成します。 ボリュームは保持されます — 置き換えられるのはコンテナだけです。 新しいイメージが取得され、コンテナが稼働中だった場合、アップグレードは自動的に新しいコンテナを起動します。その後、`yarn twenty docker:start` を実行して、正常稼働になるまで待機します。
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty docker:upgrade # Latest
|
||||
yarn twenty docker:upgrade 2.2.0 # Specific version
|
||||
```
|
||||
|
||||
`yarn twenty docker:status` で実行中のバージョンを確認できます。コンテナの `APP_VERSION` が表示されます。
|
||||
|
||||
## テスト用インスタンスを並行実行する
|
||||
|
||||
任意の `docker:*` コマンドに `--test` を指定すると、完全に分離された第2のインスタンスを管理できます。統合テストの実行や、メインの開発データに触れることなく試す際に便利です。
|
||||
|
||||
| コマンド | 機能 |
|
||||
| ----------------------------------- | -------------------------------- |
|
||||
| `yarn twenty docker:start --test` | テスト用インスタンスを起動します(デフォルトはポート2021)。 |
|
||||
| `yarn twenty docker:stop --test` | 停止する |
|
||||
| `yarn twenty docker:status --test` | ステータスを表示 |
|
||||
| `yarn twenty docker:logs --test` | ログをストリーム表示 |
|
||||
| `yarn twenty docker:reset --test` | データを消去 |
|
||||
| `yarn twenty docker:upgrade --test` | イメージをアップグレード |
|
||||
|
||||
テスト用インスタンスは、専用のボリューム(`twenty-app-dev-test-data`、`twenty-app-dev-test-storage`)と設定を備えた独自の Docker コンテナ(`twenty-app-dev-test`)内で実行されるため、メインのインスタンスと競合することなく並行して実行できます。 `--test` と `--port` を併用して、2021 を上書きできます。
|
||||
|
||||
## 手動セットアップ(スキャフォルダーなし)
|
||||
|
||||
既存プロジェクトに SDK を追加する場合はスキャフォルダーを省略します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add twenty-sdk twenty-client-sdk
|
||||
```
|
||||
|
||||
`package.json` にスクリプトを追加します:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"scripts": {
|
||||
"twenty": "twenty"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
これで `yarn twenty dev`、`yarn twenty docker:start` などを実行できます。
|
||||
|
||||
<Note>
|
||||
`twenty-sdk` をグローバルインストールしないでください — プロジェクトごとに固定し、各アプリがそれぞれのバージョンを使用するようにします。
|
||||
</Note>
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: プロジェクト構成
|
||||
description: スキャフォールドされた Twenty アプリに含まれているもの — ファイルやフォルダー、およびそれぞれの役割について説明します。
|
||||
icon: folder-tree
|
||||
---
|
||||
|
||||
`npx create-twenty-app` で生成された新しいアプリは、次のような構成になります。
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
package.json
|
||||
src/
|
||||
application-config.ts # Required — your app's entry point
|
||||
default-role.ts # Permissions for logic functions
|
||||
constants/
|
||||
universal-identifiers.ts # Auto-generated UUIDs and metadata
|
||||
__tests__/
|
||||
setup-test.ts
|
||||
app-install.integration-test.ts
|
||||
.github/workflows/ci.yml # GitHub Actions
|
||||
public/ # Static assets
|
||||
vitest.config.ts # Test runner config
|
||||
tsconfig.json, tsconfig.spec.json
|
||||
.nvmrc, .yarnrc.yml, .oxlintrc.json
|
||||
README.md, LLMS.md
|
||||
```
|
||||
|
||||
## 主要ファイル
|
||||
|
||||
| ファイル / フォルダー | 目的 |
|
||||
| ---------------------------------------- | --------------------------------- |
|
||||
| `src/application-config.ts` | **必須。** アプリのメイン設定ファイルです。 |
|
||||
| `src/default-role.ts` | ロジック関数がアクセスできる範囲を制御するデフォルトのロールです。 |
|
||||
| `src/constants/universal-identifiers.ts` | 自動生成される UUID とアプリのメタデータ(表示名、説明)。 |
|
||||
| `src/__tests__/` | 統合テスト(セットアップ + サンプルテスト)。 |
|
||||
| `public/` | アプリとともに提供される静的アセット(画像、フォント)。 |
|
||||
|
||||
<Note>
|
||||
**ファイル構成は自由です。** 上記のフォルダーはあくまで慣習であり、SDK はファイルがどこにあっても、`export default defineEntity(...)` 呼び出しに対する AST 解析によってエンティティを検出します。
|
||||
</Note>
|
||||
|
||||
## 依存関係
|
||||
|
||||
Twenty の両方の SDK パッケージは、`dependencies` ではなく `devDependencies` の下に属します。
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"twenty-client-sdk": "^2.13.0",
|
||||
"twenty-sdk": "^2.13.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **`twenty-sdk`** は、`twenty` CLI とビルド/スキャフォールディング用のツールを提供します。 これは開発時とビルド時にのみ実行され、公開済みアプリのランタイムによってインポートされることは決してありません。
|
||||
* **`twenty-client-sdk`** はアプリのコード(`CoreApiClient`、`MetadataApiClient`、`RestApiClient`)によってインポートされますが、Twenty がランタイムで提供します — ロジック関数は生成された SDK レイヤーからそれを取得し、フロントエンドコンポーネントはサーバー提供のモジュールから解決します。 インストール済みのコピーは型チェックとデプロイ時のビルドにのみ使用されるため、デプロイされたバンドルに同梱される必要はありません。
|
||||
|
||||
どちらかのパッケージを `dependencies` の下に置いたままだと、インストールされたアプリのランタイムバンドルに取り込まれてしまい、不要な重荷になります。 いずれかが `dependencies` の下に残っていると、`twenty build` は警告を出力します。
|
||||
|
||||
アプリ独自のランタイム依存関係(ロジック関数が実際にランタイムでインポートするライブラリ)は、通常どおり `dependencies` の下に追加してください。
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
title: クイックスタート
|
||||
icon: rocket
|
||||
description: 数分で最初の Twenty アプリを作成しましょう。
|
||||
---
|
||||
|
||||
## 前提条件
|
||||
|
||||
* **Node.js 24+** — [こちらからダウンロード](https://nodejs.org/)
|
||||
* **Yarn 4** — Corepack 経由で Node.js に同梱されています。 有効化: `corepack enable`
|
||||
* **Docker** — [こちらからダウンロード](https://www.docker.com/products/docker-desktop/)。 ローカルの Twenty サーバーを実行するために必要です。 すでに別の場所で Twenty が稼働している場合はスキップしてください。
|
||||
|
||||
Twenty アプリの構築は 3 つのフェーズで構成されます。 スキャフォルダーはそれらをハッピーパスの 1 つのコマンドにまとめますが、各フェーズは別個の概念です — 何かが失敗したとき、いまどのフェーズにいるかが分かると、直すべき箇所が特定できます。
|
||||
|
||||
| フェーズ | やること | ツール | 結果 |
|
||||
| --------------- | ----------------------- | ----------------------------- | ------------------------ |
|
||||
| **1. スキャフォールド** | アプリのソースコードを生成する | `npx create-twenty-app` | ディスク上の TypeScript プロジェクト |
|
||||
| **2. サーバーを起動** | 同期先となる Twenty サーバーを起動する | Docker + `yarn twenty server` | 稼働中の Twenty インスタンス |
|
||||
| **3. 同期** | コードをサーバーにライブ同期する | `yarn twenty dev` | 変更が UI に反映されます |
|
||||
|
||||
---
|
||||
|
||||
## フェーズ 1 — プロジェクトをスキャフォールドする
|
||||
|
||||
テンプレートから新しいアプリを作成します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
```
|
||||
|
||||
名前と説明の入力を求められます — 既定値でよければ **Enter** を押します。 これにより、`my-twenty-app/` にスターターの `application-config.ts`、デフォルトロール、CI ワークフロー、統合テストを含む TypeScript プロジェクトが生成されます。
|
||||
|
||||
**このフェーズ後:** マシン上にアプリのソースコードがあります。 まだ実行はされていません — それはフェーズ 2 です。
|
||||
|
||||
---
|
||||
|
||||
## フェーズ 2 — ローカルの Twenty サーバーを起動する
|
||||
|
||||
アプリは同期先としての Twenty サーバーを必要とします。 サーバーは、UI、GraphQL API、PostgreSQL を備えた完全な Twenty インスタンスで、Docker 上でローカルに実行されます。 ローカルのコードは定義をそのサーバーにアップロードし、UI に反映されます。
|
||||
|
||||
スキャフォルダーが起動を提案します:
|
||||
|
||||
> **ローカルの Twenty インスタンスをセットアップしますか?**
|
||||
|
||||
* **Yes(推奨)** — `twentycrm/twenty-app-dev` Docker イメージを取得し、ポート `2020` で起動します。 まず Docker が起動していることを確認してください。
|
||||
* **No** — すでに接続したい Twenty サーバーがある場合に選択します。 後で `yarn twenty remote:add` で接続を設定できます。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="ローカルインスタンスを開始しますか?" />
|
||||
</div>
|
||||
|
||||
サーバーが起動すると、サインイン用にブラウザーが開きます。 あらかじめ用意されたデモアカウントを使用します:
|
||||
|
||||
* **メールアドレス:** `tim@apple.dev`
|
||||
* **パスワード:** `tim@apple.dev`
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty のログイン画面" />
|
||||
</div>
|
||||
|
||||
次の画面で **Authorize** をクリックします — これにより、CLI にワークスペースへのアクセスが許可されます。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Twenty CLI の承認画面" />
|
||||
</div>
|
||||
|
||||
ターミナルにセットアップ完了のメッセージが表示されます。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/scaffolded.png" alt="アプリのスキャフォルドに成功しました。" />
|
||||
</div>
|
||||
|
||||
**このフェーズ後:** あなたの CLI が同期を許可された Twenty サーバーが [http://localhost:2020](http://localhost:2020) で稼働しています。
|
||||
|
||||
<Note>
|
||||
Docker がインストールされていない、または起動していない場合、スキャフォルダーが OS に合った開始コマンドを案内します。 Docker が起動したら、`yarn twenty docker:start` で再開できます — 再スキャフォールドは不要です。
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## フェーズ 3 — 変更を同期する
|
||||
|
||||
ここが、最も多くの時間を費やす内側のループです。
|
||||
|
||||
```bash filename="Terminal"
|
||||
cd my-twenty-app
|
||||
yarn twenty dev
|
||||
```
|
||||
|
||||
これは `src/` を監視し、変更のたびに再ビルドして、その結果をサーバーに同期します。 ファイルを編集して保存すると、数秒以内にサーバーに変更が反映されます。 ターミナルにライブステータスパネルが表示されます。
|
||||
|
||||
より詳細な出力(ビルドログ、同期リクエスト、エラートレース)が必要な場合は、`--verbose` フラグを使用します。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/dev.png" alt="開発モードのターミナル出力" />
|
||||
</div>
|
||||
|
||||
ブラウザーで [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer) を開きます。 **Your Apps** にアプリが表示されるはずです。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-1.png" alt="Your Apps リストに My twenty app が表示されている様子" />
|
||||
</div>
|
||||
|
||||
**My twenty app** をクリックすると、その **application registration**(アプリを記述するサーバーレベルのレコード。名前、識別子、OAuth 認証情報、ソース)が表示されます。 同一サーバー上では、1 つの登録を複数のワークスペースにインストールできます。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-2.png" alt="アプリケーション登録の詳細" />
|
||||
</div>
|
||||
|
||||
ワークスペースへのインストールを確認するには **View installed app** をクリックします。 **About** タブには現在のバージョンと管理オプションが表示されます。
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/app-in-ui-3.png" alt="インストール済みのアプリ" />
|
||||
</div>
|
||||
|
||||
**このフェーズ後:** ライブな開発ループが確立しています。 `src/` 内の任意のファイルを編集すると、UI に反映されます。
|
||||
|
||||
### CI やスクリプト向けの一回限りの同期
|
||||
|
||||
単一のビルド+同期を実行して終了するには `--once` を指定します — パイプラインは同じでウォッチャーはありません:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once
|
||||
```
|
||||
|
||||
| コマンド | 動作 | 使用する場面 |
|
||||
| ---------------------------------- | ------------------------------------------- | -------------------------------------------- |
|
||||
| `yarn twenty dev` | ソースファイルを監視し、変更のたびに再同期します。 停止するまで実行し続けます。 | 対話的なローカル開発。 |
|
||||
| `yarn twenty dev --once` | ビルドと同期を一度だけ実行し、成功時はコード `0`、失敗時は `1` で終了します。 | CI、pre-commit フック、AI エージェント、スクリプト化されたワークフロー。 |
|
||||
| `yarn twenty dev --once --dry-run` | メタデータの変更をビルドして出力しますが、**実際には適用しません**。 | 同期によってどのような変更が行われるかを、実行を確定する前に確認します。 |
|
||||
|
||||
どちらのモードも、認証済みのリモートが必要です。 `--dry-run` について詳しくは、[Syncing & recovery](/l/ja/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) を参照してください。
|
||||
|
||||
### Dev モードのオプション
|
||||
|
||||
| フラグ | 説明 |
|
||||
| ------------------------------------- | --------------------------------------------------- |
|
||||
| `--once` | 一度ビルドと同期を実行したら終了します。 |
|
||||
| `--dry-run` | `--once` を使用すると、メタデータの変更を適用せずにプレビューできます。 何も書き込みません。 |
|
||||
| `--debounceMs \<ms>` | ファイル変更のデバウンス遅延をミリ秒単位で設定します (既定値: `2000`)。 |
|
||||
| `--verbose` / `--debug` | 詳細なビルドログ、同期リクエスト、およびエラートレースを表示します。 |
|
||||
|
||||
## 構築できるもの
|
||||
|
||||
アプリは**エンティティ**で構成されており、それぞれが単一の `export default` を持つ TypeScript ファイルとして定義されます:
|
||||
|
||||
| エンティティ | 機能 |
|
||||
| ---------------- | ---------------------------------------------------------------- |
|
||||
| **オブジェクトとフィールド** | カスタムデータモデル(ポストカード、請求書など) 型付きフィールド |
|
||||
| **ロジック関数** | HTTP ルート、cron スケジュール、またはデータベースイベントによってトリガーされるサーバーサイドの TypeScript |
|
||||
| **フロントコンポーネント** | Twenty の UI(サイドパネル、ウィジェット、コマンドメニュー)内でレンダリングされる React コンポーネント |
|
||||
| **スキルとエージェント** | AI 機能 — 再利用可能な指示と自律型アシスタント |
|
||||
| **ビューとナビゲーション** | 事前設定済みのリストビューとサイドバーのメニュー項目 |
|
||||
| **ページレイアウト** | タブとウィジェットを備えたカスタムのレコード詳細ページ |
|
||||
|
||||
完全なリファレンス: [Concepts](/l/ja/developers/extend/apps/getting-started/concepts)。
|
||||
|
||||
## 次のステップ
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="設定" icon="screwdriver-wrench" href="/l/ja/developers/extend/apps/config/overview">
|
||||
アプリケーション ID、デフォルトロール、インストールフック、公開アセット。
|
||||
</Card>
|
||||
<Card title="データ" icon="database" href="/l/ja/developers/extend/apps/data/overview">
|
||||
オブジェクト、フィールド、および双方向リレーション。
|
||||
</Card>
|
||||
<Card title="ロジック" icon="bolt" href="/l/ja/developers/extend/apps/logic/overview">
|
||||
ロジック関数、スキル、エージェント、および OAuth 接続。
|
||||
</Card>
|
||||
<Card title="レイアウト" icon="table-columns" href="/l/ja/developers/extend/apps/layout/overview">
|
||||
ビュー、ナビゲーション、ページレイアウト、フロントコンポーネント。
|
||||
</Card>
|
||||
<Card title="オペレーション" icon="rocket" href="/l/ja/developers/extend/apps/operations/overview">
|
||||
CLI、テスト、リモート、CI、およびアプリの公開。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: スキャフォルディング
|
||||
description: yarn twenty dev:add を使用して対話的にエンティティファイルを生成します — オブジェクト、フィールド、ビュー、ロジック関数などを作成できます。
|
||||
icon: wand-magic-sparkles
|
||||
---
|
||||
|
||||
エンティティファイルを手作業で作成する代わりに、対話型スキャフォルダーを使用します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:add
|
||||
```
|
||||
|
||||
これによりエンティティタイプの選択を促され、必要なフィールドの設定を順を追って案内されたあと、安定した `universalIdentifier` と正しい `defineEntity()` 呼び出しを含む、すぐに利用できるファイルが作成されます。
|
||||
|
||||
最初のプロンプトをスキップするために、エンティティタイプを直接渡すこともできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:add object
|
||||
yarn twenty dev:add logicFunction
|
||||
yarn twenty dev:add frontComponent
|
||||
```
|
||||
|
||||
## 利用可能なエンティティタイプ
|
||||
|
||||
| エンティティタイプ | コマンド | 生成されたファイル |
|
||||
| ------------- | ---------------------------------------- | ------------------------------------------------------- |
|
||||
| オブジェクト | `yarn twenty dev:add object` | `src/objects/\<name>.ts` |
|
||||
| フィールド | `yarn twenty dev:add field` | `src/fields/\<name>.ts` |
|
||||
| ロジック関数 | `yarn twenty dev:add logicFunction` | `src/logic-functions/\<name>.ts` |
|
||||
| フロントコンポーネント | `yarn twenty dev:add frontComponent` | `src/front-components/\<name>.tsx` |
|
||||
| ロール | `yarn twenty dev:add role` | `src/roles/\<name>.ts` |
|
||||
| スキル | `yarn twenty dev:add skill` | `src/skills/\<name>.ts` |
|
||||
| エージェント | `yarn twenty dev:add agent` | `src/agents/\<name>.ts` |
|
||||
| ビュー | `yarn twenty dev:add view` | `src/views/\<name>.ts` |
|
||||
| ナビゲーションメニュー項目 | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\<name>.ts` |
|
||||
| ページレイアウト | `yarn twenty dev:add pageLayout` | `src/page-layouts/\<name>.ts` |
|
||||
|
||||
## スキャフォルダーが生成するもの
|
||||
|
||||
各エンティティタイプには固有のテンプレートがあります。 例えば、`yarn twenty dev:add object` は次の項目の入力を求めます:
|
||||
|
||||
1. **名前(単数)** — 例:`invoice`
|
||||
2. **名前(複数)** — 例:`invoices`
|
||||
3. **ラベル(単数)** — 名前から自動入力(例:`Invoice`)
|
||||
4. **ラベル(複数)** — 自動入力(例:`Invoices`)
|
||||
5. **ビューとナビゲーション項目を作成しますか?** — はいと回答すると、スキャフォルダーは新しいオブジェクトに対応するビューとサイドバーリンクも生成します。
|
||||
|
||||
他のエンティティタイプはより簡単なプロンプトで、ほとんどは名前のみを尋ねます。
|
||||
|
||||
`field` エンティティタイプはより詳細で、フィールド名、ラベル、タイプ(`TEXT`、`NUMBER`、`SELECT`、`RELATION` など、利用可能なすべてのフィールドタイプのリストから選択)、および対象オブジェクトの `universalIdentifier` を尋ねます。
|
||||
|
||||
## 出力パスのカスタマイズ
|
||||
|
||||
`--path` フラグを使用して、生成ファイルをカスタムの場所に配置します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:add logicFunction --path src/custom-folder
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: トラブルシューティング
|
||||
description: 初回実行時によく発生する問題 — Docker、Node のバージョン、Yarn、依存関係。
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
* **Docker のエラー** — `yarn twenty docker:start` の前に Docker Desktop(またはデーモン)が起動していることを確認してください。 エラーメッセージに、OS に適した起動コマンドが表示されます。
|
||||
* **Node のバージョンが違います** — 24 以上が必要です。 `node -v` で確認してください。
|
||||
* **Yarn 4 が見つからない** — `corepack enable` を実行してください。
|
||||
* **依存関係の破損** — `rm -rf node_modules && yarn install`。
|
||||
* **`twenty-sdk` が v2.8.0 へのアップグレード後にエラーになる** — v2.8.0 で `dependencies` から `devDependencies` に移動しました。 [Project Structure → Dependencies](/l/ja/developers/extend/apps/getting-started/project-structure#dependencies) を参照してください。
|
||||
* **`twenty build` は `dependencies` 配下の `twenty-client-sdk` について警告します** — これは Twenty によって実行時に提供されるため、`twenty-sdk` と同様に `devDependencies` へ移動する必要があります。 [Project Structure → Dependencies](/l/ja/developers/extend/apps/getting-started/project-structure#dependencies) を参照してください。
|
||||
|
||||
行き詰まりましたか? [Twenty の Discord](https://discord.com/channels/1130383047699738754/1130386664812982322)でヘルプを依頼してください。
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: コマンドメニュー項目
|
||||
description: defineCommandMenuItem を使用して、フロントコンポーネントをクイックアクションやコマンドメニュー (Cmd+K) のエントリとして表示します。
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
**コマンドメニュー項目**は、ユーザーと[フロントコンポーネント](/l/ja/developers/extend/apps/layout/front-components)をつなぐブリッジです。 これは Twenty のコマンドメニュー (Cmd+K) にコンポーネントを登録し、必要に応じて、ページ右上のピン留めされたクイックアクションボタンとしても登録します。
|
||||
|
||||
```ts src/command-menu-items/open-dashboard.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
label: 'Open Dashboard',
|
||||
shortLabel: 'Dashboard',
|
||||
icon: 'IconLayoutDashboard',
|
||||
isPinned: true,
|
||||
availabilityType: 'GLOBAL',
|
||||
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
|
||||
});
|
||||
```
|
||||
|
||||
## 設定フィールド
|
||||
|
||||
| フィールド | 必須 | 説明 |
|
||||
| --------------------------------------- | --- | ------------------------------------------------------------------------------------------------ |
|
||||
| `universalIdentifier` | はい | コマンドの安定した一意の ID |
|
||||
| `label` | はい | コマンドメニュー(Cmd+K)に表示されるフルラベル |
|
||||
| `frontComponentUniversalIdentifier` | はい | このコマンドが開くフロントコンポーネントの `universalIdentifier` |
|
||||
| `shortLabel` | いいえ | ピン留めされたクイックアクションボタンに表示される短いラベル |
|
||||
| `icon` | いいえ | ラベルの横に表示するアイコン名(例:'IconBolt'、'IconSend') |
|
||||
| `isPinned` | いいえ | `true` の場合、ページ右上にクイックアクションボタンとして表示します |
|
||||
| `availabilityType` | いいえ | コマンドの表示場所を制御します:'GLOBAL'(常に利用可能)、'RECORD_SELECTION'(レコード選択時のみ)、または 'FALLBACK'(他のコマンドが一致しないときに表示) |
|
||||
| `availabilityObjectUniversalIdentifier` | いいえ | コマンドを特定のオブジェクトタイプのページに制限します(例:Company レコードのみ) |
|
||||
| `conditionalAvailabilityExpression` | いいえ | 表示可否を動的に制御するブール式(下記参照) |
|
||||
|
||||
## ヘッドレスコマンド
|
||||
|
||||
[ヘッドレスフロントコンポーネント](/l/ja/developers/extend/apps/layout/front-components#headless-vs-non-headless)とペアになったコマンドメニュー項目は、ワンクリックアクション(コードの実行、ナビゲーション、確認と実行)を提供するための一般的な方法です。 Front Components のページでは、アクション実行後にアンマウントするパターンを処理する [SDK Command components](/l/ja/developers/extend/apps/layout/front-components#sdk-command-components)(`Command`、`CommandLink`、`CommandModal`、`CommandOpenSidePanelPage`)について説明しています。
|
||||
|
||||
一般的なフロー:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
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,
|
||||
});
|
||||
```
|
||||
|
||||
```ts src/command-menu-items/run-action.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||||
label: 'Run my action',
|
||||
icon: 'IconPlayerPlay',
|
||||
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||||
});
|
||||
```
|
||||
|
||||
## 条件付き可用性式
|
||||
|
||||
`conditionalAvailabilityExpression` フィールドにより、現在のページコンテキストに基づいてコマンドの表示タイミングを制御できます。 式を構築するために、`twenty-sdk` から型付きの変数と演算子をインポートします:
|
||||
|
||||
```ts src/command-menu-items/bulk-update.command-menu-item.ts
|
||||
import {
|
||||
defineCommandMenuItem,
|
||||
objectPermissions,
|
||||
everyEquals,
|
||||
} from 'twenty-sdk/define';
|
||||
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: '...',
|
||||
label: 'Bulk Update',
|
||||
availabilityType: 'RECORD_SELECTION',
|
||||
frontComponentUniversalIdentifier: '...',
|
||||
conditionalAvailabilityExpression: everyEquals(
|
||||
objectPermissions,
|
||||
'canUpdateObjectRecords',
|
||||
true,
|
||||
),
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
`RECORD_SELECTION` は、すでに空ではない選択状態を意味します。特定の件数を示す場合にのみ `numberOfSelectedRecords` を使用してください(例: `>= 2`)。
|
||||
</Note>
|
||||
|
||||
### コンテキスト変数
|
||||
|
||||
これらは現在のページの状態を表します:
|
||||
|
||||
| 変数 | タイプ | 説明 |
|
||||
| ------------------------------ | --------- | ----------------------------------------------- |
|
||||
| `pageType` | `string` | 現在のページタイプ(例:'RecordIndexPage'、'RecordShowPage') |
|
||||
| `isInSidePanel` | `boolean` | コンポーネントがサイドパネル内でレンダリングされているかどうか |
|
||||
| `numberOfSelectedRecords` | `number` | 現在選択されているレコード数 |
|
||||
| `isSelectAll` | `boolean` | 「すべて選択」が有効かどうか |
|
||||
| `selectedRecords` | `array` | 選択されたレコードオブジェクト |
|
||||
| `favoriteRecordIds` | `array` | お気に入り登録されたレコードの ID |
|
||||
| `objectPermissions` | `object` | 現在のオブジェクトタイプの権限 |
|
||||
| `targetObjectReadPermissions` | `object` | 対象オブジェクトの読み取り権限 |
|
||||
| `targetObjectWritePermissions` | `object` | 対象オブジェクトの書き込み権限 |
|
||||
| `featureFlags` | `object` | 有効な機能フラグ |
|
||||
| `objectMetadataItem` | `object` | 現在のオブジェクトタイプのメタデータ |
|
||||
| `hasAnySoftDeleteFilterOnView` | `boolean` | 現在のビューにソフトデリートフィルターがあるかどうか |
|
||||
|
||||
### 演算子
|
||||
|
||||
変数を組み合わせてブール式を作成します:
|
||||
|
||||
| 演算子 | 説明 |
|
||||
| ----------------------------------- | ----------------------------------------- |
|
||||
| `isDefined(value)` | 値が null/undefined でない場合は `true` |
|
||||
| `isNonEmptyString(value)` | 値が空でない文字列の場合は `true` |
|
||||
| `includes(array, value)` | 配列に値が含まれている場合は `true` |
|
||||
| `includesEvery(array, prop, value)` | すべての要素のプロパティに値が含まれている場合は `true` |
|
||||
| `every(array, prop)` | すべての要素でそのプロパティが truthy の場合は `true` |
|
||||
| `everyDefined(array, prop)` | すべての要素でそのプロパティが定義されている場合は `true` |
|
||||
| `everyEquals(array, prop, value)` | すべての要素でそのプロパティが値に等しい場合は `true` |
|
||||
| `some(array, prop)` | 少なくとも 1 つの要素でそのプロパティが truthy の場合は `true` |
|
||||
| `someDefined(array, prop)` | 少なくとも 1 つの要素でそのプロパティが定義されている場合は `true` |
|
||||
| `someEquals(array, prop, value)` | 少なくとも 1 つの要素でそのプロパティが値に等しい場合は `true` |
|
||||
| `someNonEmptyString(array, prop)` | 少なくとも 1 つの要素でそのプロパティが空でない文字列である場合は `true` |
|
||||
| `none(array, prop)` | すべての要素でそのプロパティが falsy の場合は `true` |
|
||||
| `noneDefined(array, prop)` | すべての要素でそのプロパティが未定義の場合は `true` |
|
||||
| `noneEquals(array, prop, value)` | どの要素でもそのプロパティが値に等しくない場合は `true` |
|
||||
@@ -0,0 +1,551 @@
|
||||
---
|
||||
title: フロントコンポーネント
|
||||
description: Twenty の UI 内でレンダリングされる、サンドボックスで分離された React コンポーネントを構築します。
|
||||
icon: window-maximize
|
||||
---
|
||||
|
||||
フロントコンポーネントは、Twenty の UI 内で直接レンダリングされる React コンポーネントです。 フロントコンポーネントは Remote DOM を使用する**分離された Web Worker**内で実行されます—コードはサンドボックス化されていますが、iframe ではなくページ内でネイティブにレンダリングされます。
|
||||
|
||||
## フロントコンポーネントを使用できる場所
|
||||
|
||||
フロントコンポーネントは、Twenty 内の2つの場所でレンダリングできます:
|
||||
|
||||
* **サイドパネル** — ヘッドレスでないフロントコンポーネントは、右側のサイドパネルで開きます。 フロントコンポーネントがコマンドメニューからトリガーされた場合のデフォルトの動作です。
|
||||
* **ウィジェット(ダッシュボードとレコードページ)** — フロントコンポーネントは、[ページレイアウト](/l/ja/developers/extend/apps/layout/page-layouts)内にウィジェットとして埋め込めます。 ダッシュボードやレコードページのレイアウトを設定する際、ユーザーはフロントコンポーネントのウィジェットを追加できます。
|
||||
|
||||
フロントコンポーネント単体では UI から直接アクセスできないため、それを*表示*する必要があります。 それを行う方法は次の 2 つです。
|
||||
|
||||
* **[コマンドメニュー項目](/l/ja/developers/extend/apps/layout/command-menu-items)とペアにする** — コマンドメニュー(Cmd+K)に登録し、必要に応じてピン留めされたクイックアクションとして登録します。
|
||||
* **[ページレイアウト](/l/ja/developers/extend/apps/layout/page-layouts)内のウィジェットとして埋め込む** — レコードの詳細ページまたはダッシュボード上に配置します。
|
||||
|
||||
## 基本的な例
|
||||
|
||||
フロントコンポーネントの動作を手早く確認するには、[`defineCommandMenuItem`](/l/ja/developers/extend/apps/layout/command-menu-items)とペアにして、ページ右上隅にクイックアクションボタンとして表示させるのが最も簡単です。
|
||||
|
||||
```tsx src/front-components/hello-world.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
|
||||
const HelloWorld = () => {
|
||||
return (
|
||||
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
|
||||
<h1>Hello from my app!</h1>
|
||||
<p>This component renders inside Twenty.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
|
||||
name: 'hello-world',
|
||||
description: 'A simple front component',
|
||||
component: HelloWorld,
|
||||
});
|
||||
```
|
||||
|
||||
```ts src/command-menu-items/hello-world.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
|
||||
shortLabel: 'Hello',
|
||||
label: 'Hello World',
|
||||
icon: 'IconBolt',
|
||||
isPinned: true,
|
||||
availabilityType: 'GLOBAL',
|
||||
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
|
||||
});
|
||||
```
|
||||
|
||||
`yarn twenty dev` で同期するか(または 1 回限りで `yarn twenty dev --once` を実行すると)、ページ右上にクイックアクションが表示されます:
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="右上のクイックアクションボタン" />
|
||||
</div>
|
||||
|
||||
クリックすると、コンポーネントがインラインでレンダリングされます。
|
||||
|
||||
## 設定フィールド
|
||||
|
||||
| フィールド | 必須 | 説明 |
|
||||
| --------------------- | --- | ----------------------------------------- |
|
||||
| `universalIdentifier` | はい | このコンポーネントの安定した一意の ID |
|
||||
| `component` | はい | React コンポーネント関数 |
|
||||
| `name` | いいえ | 表示名 |
|
||||
| `description` | いいえ | コンポーネントの機能の説明 |
|
||||
| `isHeadless` | いいえ | コンポーネントに可視の UI がない場合は `true` を設定します(下記参照) |
|
||||
|
||||
## フロントコンポーネントをページに配置する
|
||||
|
||||
コマンド以外にも、**ページレイアウト**でウィジェットとして追加することで、フロントコンポーネントをレコードページに直接埋め込めます。 詳しくは[ページレイアウト](/l/ja/developers/extend/apps/layout/page-layouts)を参照してください。
|
||||
|
||||
## ヘッドレスと非ヘッドレス
|
||||
|
||||
フロントコンポーネントには、`isHeadless` オプションで制御される2つのレンダリングモードがあります:
|
||||
|
||||
**非ヘッドレス(デフォルト)** — コンポーネントは可視のUIをレンダリングします。 コマンドメニューからトリガーされた場合、サイドパネルで開きます。 `isHeadless` が `false` または省略された場合のデフォルトの動作です。
|
||||
|
||||
**ヘッドレス (`isHeadless: true`)** — コンポーネントはバックグラウンドで不可視のままマウントされます。 サイドパネルは開きません。 ヘッドレスコンポーネントは、ロジックを実行して自動的にアンマウントするアクション向けに設計されています。例えば、非同期タスクの実行、ページへのナビゲーション、確認モーダルの表示などです。 以下で説明する SDK の Command コンポーネントと自然に組み合わせて使用できます。
|
||||
|
||||
```tsx src/front-components/sync-tracker.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const SyncTracker = () => {
|
||||
const recordId = useRecordId();
|
||||
|
||||
useEffect(() => {
|
||||
enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' });
|
||||
}, [recordId]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '...',
|
||||
name: 'sync-tracker',
|
||||
description: 'Tracks record views silently',
|
||||
isHeadless: true,
|
||||
component: SyncTracker,
|
||||
});
|
||||
```
|
||||
|
||||
このコンポーネントが `null` を返すため、Twenty はそのためのコンテナのレンダリングをスキップします—レイアウトに空白は発生しません。 コンポーネントは引き続き、すべてのフックとホスト通信 API にアクセスできます。
|
||||
|
||||
## SDK の Command コンポーネント
|
||||
|
||||
`twenty-sdk` パッケージは、ヘッドレスのフロントコンポーネント向けに設計された4つの Command ヘルパーコンポーネントを提供します。 各コンポーネントは、マウント時にアクションを実行し、エラーをスナックバー通知で処理し、完了時にフロントコンポーネントを自動的にアンマウントします。
|
||||
|
||||
`twenty-sdk/command` からインポートします:
|
||||
|
||||
* **`Command`** — `execute` プロップ経由で非同期コールバックを実行します。
|
||||
* **`CommandLink`** — アプリのパスにナビゲートします。 Props: `to`, `params`, `queryParams`, `options`.
|
||||
* **`CommandModal`** — 確認モーダルを開きます。 ユーザーが確認すると、`execute` コールバックを実行します。 Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
|
||||
* **`CommandOpenSidePanelPage`** — サイドパネルページを開きます。 Props は `page` に依存します — 例: `ViewRecord` は `recordId` と `objectNameSingular` を受け取り、他のページは `pageTitle` と `pageIcon` を受け取ります。
|
||||
|
||||
`Command` を使用してコマンドメニューからアクションを実行するヘッドレスのフロントコンポーネントの完全な例です:
|
||||
|
||||
```tsx src/front-components/run-action.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
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,
|
||||
});
|
||||
```
|
||||
|
||||
```ts src/command-menu-items/run-action.command-menu-item.ts
|
||||
import { defineCommandMenuItem } from 'twenty-sdk/define';
|
||||
|
||||
export default defineCommandMenuItem({
|
||||
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
|
||||
label: 'Run my action',
|
||||
icon: 'IconPlayerPlay',
|
||||
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
|
||||
});
|
||||
```
|
||||
|
||||
さらに、実行前に確認を求めるために `CommandModal` を使用する例です:
|
||||
|
||||
```tsx src/front-components/delete-draft.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
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,
|
||||
});
|
||||
```
|
||||
|
||||
## ロジック関数の呼び出し
|
||||
|
||||
フロントコンポーネントはサンドボックス化された Web Worker 内でブラウザーサイドで実行され、一方で[ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions)はサーバーサイドで実行されます。 両者の間にプロセス内での直接呼び出しはありません。その代わり、フロントコンポーネントは HTTP 経由でロジック関数にアクセスします。
|
||||
|
||||
`httpRouteTriggerSettings` で宣言されたロジック関数は、そのルートパスで HTTP 経由でアクセスできます。 Twenty は、関数が提供されるベース URL を `TWENTY_FUNCTIONS_URL` としてワーカーに注入し、呼び出しを認証する `TWENTY_APP_ACCESS_TOKEN` も併せて渡します。 独自の関数を呼び出すための専用 SDK クライアントはまだないため、シンプルな `fetch` を使って呼び出してください。
|
||||
|
||||
> **Twenty Cloud では、HTTP トリガーのロジック関数はワークスペースごとの専用ドメインで提供されます**。`https://\<your-workspace-subdomain>.twenty.com\<path>` がそのドメインであり、これが `TWENTY_FUNCTIONS_URL` が解決される先とまったく同じです。 外部から呼び出す場合は、関数の **HTTP trigger** 設定、もしくはアプリケーションの **Settings** タブから、正確な URL をコピーしてください。
|
||||
|
||||
<Warning>
|
||||
レガシーな `/s/` 関数ルートは**非推奨**となっており、**2026-07-24 に無効化されます**。 代わりに(上記の)`TWENTY_FUNCTIONS_URL` を使用し、その日までにハードコードされた `/s/` URL をすべて移行してください。 `/s/` ルートはセルフホスティング向けには引き続き利用可能です。
|
||||
</Warning>
|
||||
|
||||
ヘッドレスフロントコンポーネントは、`Command` コンポーネント経由でマウント時に呼び出しを実行し、その後自動的にアンマウントできます。
|
||||
|
||||
```tsx src/front-components/sync-prs.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { Command } from 'twenty-sdk/command';
|
||||
|
||||
const SyncPrs = () => {
|
||||
const execute = async () => {
|
||||
await fetch(`${process.env.TWENTY_FUNCTIONS_URL}/github/fetch-prs`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.TWENTY_APP_ACCESS_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ owner: 'twentyhq', repo: 'twenty' }),
|
||||
});
|
||||
};
|
||||
|
||||
return <Command execute={execute} />;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '...',
|
||||
name: 'sync-prs',
|
||||
description: 'Triggers the fetch-prs logic function',
|
||||
isHeadless: true,
|
||||
component: SyncPrs,
|
||||
});
|
||||
```
|
||||
|
||||
`TWENTY_FUNCTIONS_URL` に付加されるパスは、ロジック関数の `httpRouteTriggerSettings.path` です。 `isAuthRequired: true` のままにしておいてください。コンポーネント用に Twenty が発行する `TWENTY_APP_ACCESS_TOKEN` によってリクエストが認証されます。
|
||||
|
||||
```ts src/logic-functions/fetch-prs.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import type { RoutePayload } from 'twenty-sdk/logic-function';
|
||||
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const { owner, repo } = (event.body ?? {}) as { owner: string; repo: string };
|
||||
// ...fetch from GitHub and persist records...
|
||||
return { ok: true };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: '...',
|
||||
name: 'fetch-prs',
|
||||
handler,
|
||||
httpRouteTriggerSettings: {
|
||||
path: '/github/fetch-prs',
|
||||
httpMethod: 'POST',
|
||||
isAuthRequired: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
`TWENTY_FUNCTIONS_URL` と `TWENTY_APP_ACCESS_TOKEN` は自動的に挿入されます。詳しくは [Application variables](#application-variables) を参照してください。 秘匿アプリケーション変数はフロントコンポーネントに公開されることがないため、API キーやその他の機密性の高いロジックはフロントコンポーネントではなく、ロジック関数側に保持してください。
|
||||
</Note>
|
||||
|
||||
### Twenty REST API の呼び出し
|
||||
|
||||
フロントコンポーネントから Twenty のレコードを読み書きするには、`twenty-client-sdk/rest` の `RestApiClient` を使用します。 これは `CoreApiClient` や `MetadataApiClient` と同じクライアントファミリーに属しますが、GraphQL API ではなく Twenty REST API(`/rest/...`)を対象とし、そのベース URL を `TWENTY_API_URL` から取得します。
|
||||
|
||||
| メソッド | 説明 |
|
||||
| --------------------------------- | ------------------------- |
|
||||
| `get(path, options?)` | `GET` リクエストを送信します |
|
||||
| `post(path, body?, options?)` | `POST` リクエストを送信します |
|
||||
| `put(path, body?, options?)` | `PUT` リクエストを送信します |
|
||||
| `patch(path, body?, options?)` | `PATCH` リクエストを送信します |
|
||||
| `delete(path, options?)` | `DELETE` リクエストを送信します |
|
||||
| `request(method, path, options?)` | 任意の HTTP メソッドによる汎用的なリクエスト |
|
||||
|
||||
`options` には、`headers`、`query`(クエリ文字列パラメーターのレコード。null 相当の値はスキップされます)、および `signal` 経由の `AbortSignal` を指定できます。 `FormData` ではないオブジェクト `body` は、自動的に JSON シリアル化されます。 `401` が発生した場合、クライアントはホスト経由で一度だけアクセス トークンを更新し、そのリクエストを再試行します。
|
||||
|
||||
ベース URL とトークンは、デフォルトで環境から解決されます。 必要に応じてコンストラクターに上書き設定を渡します — たとえばテスト時などです:
|
||||
|
||||
```ts
|
||||
const client = new RestApiClient({
|
||||
baseUrl: 'https://myworkspace.twenty.com',
|
||||
token: 'my-token',
|
||||
});
|
||||
```
|
||||
|
||||
失敗したリクエストは `RestApiClientError` をスローし、`status`、`statusText`、`url`、および解析済みの `body` を公開します:
|
||||
|
||||
```tsx
|
||||
import { RestApiClient, RestApiClientError } from 'twenty-client-sdk/rest';
|
||||
|
||||
const client = new RestApiClient();
|
||||
|
||||
try {
|
||||
const people = await client.get('/rest/people', {
|
||||
query: { limit: 10 },
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof RestApiClientError) {
|
||||
console.error(error.status, error.body);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ランタイムコンテキストへのアクセス
|
||||
|
||||
コンポーネント内で、SDK のフックを使用して現在のユーザー、レコード、コンポーネントインスタンスにアクセスします:
|
||||
|
||||
```tsx src/front-components/record-info.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import {
|
||||
useUserId,
|
||||
useRecordId,
|
||||
useFrontComponentId,
|
||||
} from 'twenty-sdk/front-component';
|
||||
|
||||
const RecordInfo = () => {
|
||||
const userId = useUserId();
|
||||
const recordId = useRecordId();
|
||||
const componentId = useFrontComponentId();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>User: {userId}</p>
|
||||
<p>Record: {recordId ?? 'No record context'}</p>
|
||||
<p>Component: {componentId}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012',
|
||||
name: 'record-info',
|
||||
component: RecordInfo,
|
||||
});
|
||||
```
|
||||
|
||||
利用可能なフック:
|
||||
|
||||
| フック | 戻り値 | 説明 |
|
||||
| --------------------------------------------- | ---------------------- | ------------------------------------------------ |
|
||||
| `useUserId()` | `string` または `null` | 現在のユーザーの ID |
|
||||
| `useSelectedRecordIds()` | `string[]` | 選択されたレコードIDの配列(未選択の場合は空配列) |
|
||||
| `useRecordId()` | `string` または `null` | **非推奨。** 代わりに `useSelectedRecordIds()` を使用してください |
|
||||
| `useFrontComponentId()` | `string` | このコンポーネントインスタンスの ID |
|
||||
| `useColorScheme()` | `'light'` または `'dark'` | ホスト UI のアクティブなカラースキーム(`System` はすでに解決済み) |
|
||||
| `useFrontComponentExecutionContext(selector)` | 項目により異なる | セレクター関数で実行コンテキスト全体にアクセス |
|
||||
|
||||
## アプリケーション変数
|
||||
|
||||
`isSecret: false` が設定された [`defineApplication()`](/l/ja/developers/extend/apps/config/application) 内で定義されたアプリケーション変数は、`getApplicationVariable` ユーティリティを通じてフロントコンポーネント内で利用できます。
|
||||
|
||||
```tsx src/front-components/greeting.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { getApplicationVariable } from 'twenty-sdk/front-component';
|
||||
|
||||
const Greeting = () => {
|
||||
const recipientName = getApplicationVariable('DEFAULT_RECIPIENT_NAME') ?? 'World';
|
||||
|
||||
return <p>Hello, {recipientName}!</p>;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '...',
|
||||
name: 'greeting',
|
||||
component: Greeting,
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>
|
||||
シークレット変数(`isSecret: true`)はフロントコンポーネントには公開**されません**。 それらは、サーバーサイドで実行される[ロジック関数](/l/ja/developers/extend/apps/logic/logic-functions)でのみ利用できます。 これにより、API キーなどの機密値がブラウザーに送信されるのを防ぎます。
|
||||
</Warning>
|
||||
|
||||
次のシステム変数は、常に `process.env` 経由で利用できます。
|
||||
|
||||
| 変数 | 説明 |
|
||||
| ------------------------- | ----------------------------- |
|
||||
| `TWENTY_FUNCTIONS_URL` | アプリの HTTP ロジック関数が提供されるベース URL |
|
||||
| `TWENTY_API_URL` | Twenty コア API のベース URL |
|
||||
| `TWENTY_APP_ACCESS_TOKEN` | アプリのロールにスコープされた有効期間の短いトークン |
|
||||
|
||||
## ホスト通信 API
|
||||
|
||||
フロントコンポーネントは、`twenty-sdk` の関数を使用してナビゲーション、モーダル、通知をトリガーできます:
|
||||
|
||||
| 機能 | 説明 |
|
||||
| ----------------------------------------------- | -------------- |
|
||||
| `navigate(to, params?, queryParams?, options?)` | アプリ内のページに移動 |
|
||||
| `openSidePanelPage(params)` | サイドパネルを開く |
|
||||
| `closeSidePanel()` | サイドパネルを閉じる |
|
||||
| `openCommandConfirmationModal(params)` | 確認ダイアログを表示 |
|
||||
| `enqueueSnackbar(params)` | トースト通知を表示 |
|
||||
| `unmountFrontComponent()` | コンポーネントをアンマウント |
|
||||
| `updateProgress(progress)` | 進行状況インジケーターを更新 |
|
||||
|
||||
アクション完了後にホスト API を使用してスナックバーを表示し、サイドパネルを閉じる例です:
|
||||
|
||||
```tsx src/front-components/archive-record.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { useRecordId } from 'twenty-sdk/front-component';
|
||||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
|
||||
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,
|
||||
});
|
||||
```
|
||||
|
||||
### 複数のレコードを扱う
|
||||
|
||||
複数の選択されたレコードを処理するには `useSelectedRecordIds()` を使用してください。 これは一括操作に役立ちます:
|
||||
|
||||
```tsx src/front-components/bulk-export.tsx
|
||||
import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define';
|
||||
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
|
||||
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
|
||||
import { CoreApiClient } from 'twenty-sdk/clients';
|
||||
|
||||
const BulkExport = () => {
|
||||
const selectedRecordIds = useSelectedRecordIds();
|
||||
|
||||
const handleExport = async () => {
|
||||
const client = new CoreApiClient();
|
||||
|
||||
for (const recordId of selectedRecordIds) {
|
||||
await client.mutation({
|
||||
updateTask: {
|
||||
__args: { id: recordId, data: { exported: true } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await enqueueSnackbar({
|
||||
message: `Exported ${selectedRecordIds.length} records`,
|
||||
variant: 'success',
|
||||
});
|
||||
|
||||
await closeSidePanel();
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<p>Export {selectedRecordIds.length} selected record(s)?</p>
|
||||
<button onClick={handleExport}>Export</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
|
||||
name: 'bulk-export',
|
||||
description: 'Export selected records',
|
||||
component: BulkExport,
|
||||
command: {
|
||||
universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
|
||||
label: 'Bulk Export',
|
||||
availabilityType: 'RECORD_SELECTION',
|
||||
conditionalAvailabilityExpression: numberOfSelectedRecords > 0,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 公開アセット
|
||||
|
||||
フロントコンポーネントは、`getPublicAssetUrl` を使用してアプリの `public/` ディレクトリ内のファイルにアクセスできます:
|
||||
|
||||
```tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { getPublicAssetUrl } from 'twenty-sdk/utils';
|
||||
|
||||
const Logo = () => <img src={getPublicAssetUrl('logo.png')} alt="Logo" />;
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '...',
|
||||
name: 'logo',
|
||||
component: Logo,
|
||||
});
|
||||
```
|
||||
|
||||
詳細は[公開アセットのセクション](/l/ja/developers/extend/apps/config/public-assets)を参照してください。
|
||||
|
||||
## スタイリング
|
||||
|
||||
フロントコンポーネントは複数のスタイリング手法をサポートしています。 次のものを使用できます:
|
||||
|
||||
* **インラインスタイル** — `style={{ color: 'red' }}`
|
||||
* **Twenty UI コンポーネント** — `twenty-sdk/ui` からインポート(Button、Tag、Status、Chip、Avatar など)
|
||||
* **Emotion** — `@emotion/react` による CSS-in-JS
|
||||
* **styled-components** — `styled.div` パターン
|
||||
* **Tailwind CSS** — ユーティリティクラス
|
||||
* React と互換性のある**任意の CSS-in-JS ライブラリ**
|
||||
|
||||
```tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { Button, Tag, Status } from 'twenty-sdk/ui';
|
||||
|
||||
const StyledWidget = () => {
|
||||
return (
|
||||
<div style={{ padding: '16px', display: 'flex', gap: '8px' }}>
|
||||
<Button title="Click me" onClick={() => alert('Clicked!')} />
|
||||
<Tag text="Active" color="green" />
|
||||
<Status color="green" text="Online" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456',
|
||||
name: 'styled-widget',
|
||||
component: StyledWidget,
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: ナビゲーションメニュー項目
|
||||
description: ワークスペースのサイドバーにカスタムエントリを追加します。保存済みビューや外部 URL へのリンクを設定できます。
|
||||
icon: bars
|
||||
---
|
||||
|
||||
**ナビゲーションメニュー項目**は、左サイドバー内の 1 つのエントリです。 カスタムのサイドバーリンクを提供するには `defineNavigationMenuItem()` を使用します。通常は、提供する各[ビュー](/l/ja/developers/extend/apps/layout/views)ごとに 1 つずつ、または外部 URL を指すリンクとして使用します。
|
||||
|
||||
```ts src/navigation-menu-items/example-navigation-menu-item.ts
|
||||
import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define';
|
||||
import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from '../views/example-view';
|
||||
|
||||
export default defineNavigationMenuItem({
|
||||
universalIdentifier: '9327db91-afa1-41b6-bd9d-2b51a26efb4c',
|
||||
name: 'example-navigation-menu-item',
|
||||
icon: 'IconList',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
type: NavigationMenuItemType.VIEW,
|
||||
viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
```
|
||||
|
||||
## 主なポイント
|
||||
|
||||
* `type` は、そのメニュー項目がどこにリンクするかを決定します。 各 `type` は、特定の識別子フィールドと組み合わされます。
|
||||
|
||||
| タイプ | 機能 | 必須フィールド |
|
||||
| ------------------------------------ | --------------------------- | ------------------------------------------------------- |
|
||||
| `NavigationMenuItemType.VIEW` | 保存済みビューを開きます | `viewUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.LINK` | 外部 URL を開きます | `link` |
|
||||
| `NavigationMenuItemType.FOLDER` | ネストされた項目をラベルの下にグループ化します | `name`(子項目は `folderUniversalIdentifier` を通じてフォルダを参照します) |
|
||||
| `NavigationMenuItemType.OBJECT` | オブジェクトのデフォルトのインデックスページを開きます | `targetObjectUniversalIdentifier` |
|
||||
| `NavigationMenuItemType.PAGE_LAYOUT` | スタンドアロンのページレイアウトを開きます | `pageLayoutUniversalIdentifier` |
|
||||
|
||||
* `position` はサイドバーでの表示順を制御します。
|
||||
|
||||
* `icon` と `color` は任意で、エントリの見た目をカスタマイズします。
|
||||
|
||||
* `folderUniversalIdentifier` は、任意の項目で利用でき、その項目を `FOLDER` タイプの親の内側にネストするために使用します。
|
||||
|
||||
<Note>
|
||||
**よくある落とし穴:** ビューおよびナビゲーションメニュー項目が関連付けられていないオブジェクトを作成すると、そのオブジェクトはユーザーから見えなくなります。 技術的/内部的なオブジェクトでない限り、すべてのカスタムオブジェクトには、デフォルトビューと、それを指すサイドバーエントリの両方を用意する必要があります。
|
||||
</Note>
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: 概要
|
||||
description: Twenty の UI 内にアプリを配置します — サイドバー項目、保存済みビュー、レコードページのタブ、およびサンドボックス化された React コンポーネント。
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
Twenty アプリの **レイアウトレイヤー** とは、ユーザーに見えるすべての要素を指します。アプリがサイドバーのどこに表示されるか、どのリストビューを提供するか、レコード詳細ページがどのように構成されるか、そしてそれらのページ内でどのカスタム React コンポーネントがレンダリングされるかなどです。
|
||||
|
||||
```text
|
||||
Sidebar Record list Record detail page
|
||||
─────── ─────────── ──────────────────
|
||||
[📋 My View] ────▶ ┌──────────┐ ┌─────────────────────┐
|
||||
[📋 Drafts ] │ Companies│ │ Tabs: [Overview ] │
|
||||
[📋 Inbox ] │ ──────── │ │ [Notes ] │
|
||||
▲ │ Apple │ │ [Hello ]◀──── definePageLayoutTab
|
||||
│ │ Acme │ │ │ adds a tab...
|
||||
└ defineNavi- │ … │ │ ┌────────────────┐ │
|
||||
gationMenu- └────▲─────┘ │ │ │ │
|
||||
Item points │ │ │ React UI │◀── …with a
|
||||
to a defineView │ │ │ (sandboxed in │ │ defineFrontComponent
|
||||
└ defineView │ │ a Worker) │ │ widget inside
|
||||
picks columns │ └────────────────┘ │
|
||||
and filters └─────────────────────┘
|
||||
```
|
||||
|
||||
## このセクションについて
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="ビュー" icon="list" href="/l/ja/developers/extend/apps/layout/views">
|
||||
`defineView` — 表示カラム、フィルター、グループなどを含む、保存済みリスト構成。
|
||||
</Card>
|
||||
<Card title="ナビゲーションメニュー項目" icon="bars" href="/l/ja/developers/extend/apps/layout/navigation-menu-items">
|
||||
`defineNavigationMenuItem` — ビューまたは外部 URL を指すサイドバー項目。
|
||||
</Card>
|
||||
<Card title="ページレイアウト" icon="table-columns" href="/l/ja/developers/extend/apps/layout/page-layouts">
|
||||
`definePageLayout` および `definePageLayoutTab` — レコード詳細ページ上のタブとウィジェット。
|
||||
</Card>
|
||||
<Card title="フロントコンポーネント" icon="window-maximize" href="/l/ja/developers/extend/apps/layout/front-components">
|
||||
`defineFrontComponent` — Twenty 内でレンダリングされる、サンドボックス化された React コンポーネント。
|
||||
</Card>
|
||||
<Card title="コマンドメニュー項目" icon="terminal" href="/l/ja/developers/extend/apps/layout/command-menu-items">
|
||||
`defineCommandMenuItem` — フロントコンポーネントを Cmd+K の項目およびクイックアクションとして登録します。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## アプリが表示される場所
|
||||
|
||||
| 表示場所 | 制御内容 | エンティティ |
|
||||
| -------------------- | ------------------------------------------------ | ----------------------------------------- |
|
||||
| **サイドバー** | 保存済みビューまたは外部 URL へリンクするカスタム項目 | `defineNavigationMenuItem` |
|
||||
| **レコードリスト** | オブジェクト向けの保存済み構成 — 表示カラム、順序、フィルター、グループ | `defineView` |
|
||||
| **レコード詳細ページ** | レコードページ上のタブおよびウィジェット(独自オブジェクトのもの、または標準オブジェクトのもの) | `definePageLayout`, `definePageLayoutTab` |
|
||||
| **上記いずれかの内部** | カスタム React ウィジェット — ボタン、フォーム、ダッシュボード、連携機能 | `defineFrontComponent` |
|
||||
| **コマンドメニュー (Cmd+K)** | ピン留めされたクイックアクションまたは非表示コマンド | `defineCommandMenuItem` |
|
||||
|
||||
フロントコンポーネントは Remote DOM を使用して、分離された Web Worker 内で実行されます。ページ内に *ネイティブに*(iframe 内ではなく)レンダリングされますが、ホストページや DOM へ直接アクセスすることはできません。 Twenty との通信は、メッセージパッシング型のホスト API を通じて行われます。
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: ページレイアウト
|
||||
description: definePageLayout と definePageLayoutTab を使用して、レコードの詳細ページのカスタマイズ(タブ、ウィジェット、front components のレンダー位置)を行います。
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
**ページレイアウト**は、レコードの詳細ページがどのように構成されるかを制御します。どのタブを表示し、そのタブにどのウィジェットを含めるかを決定します。 自分が所有するオブジェクトに対してレイアウトを宣言するには `definePageLayout()` を使用し、既に存在するレイアウト(自分のもの、または標準の Twenty のレイアウト)に単一のタブを追加するには `definePageLayoutTab()` を使用します。
|
||||
|
||||
| ユースケース | エンティティ |
|
||||
| ----------------------------------------- | --------------------- |
|
||||
| 自分が所有するオブジェクト上のレコードページ全体のレイアウトを定義する | `definePageLayout` |
|
||||
| 既存のレイアウト(自分のオブジェクト、または標準オブジェクト)にタブを1つ追加する | `definePageLayoutTab` |
|
||||
|
||||
## definePageLayout
|
||||
|
||||
自分が詳細ページ全体を所有している場合に使用します。通常は、自分で定義したカスタムオブジェクトに対して使用します。
|
||||
|
||||
```ts src/page-layouts/example-record-page-layout.ts
|
||||
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
|
||||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
|
||||
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
|
||||
|
||||
export default definePageLayout({
|
||||
universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134',
|
||||
name: 'Example Record Page',
|
||||
type: 'RECORD_PAGE',
|
||||
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
tabs: [
|
||||
{
|
||||
universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5',
|
||||
title: 'Hello World',
|
||||
position: 50,
|
||||
icon: 'IconWorld',
|
||||
layoutMode: PageLayoutTabLayoutMode.CANVAS,
|
||||
widgets: [
|
||||
{
|
||||
universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d',
|
||||
title: 'Hello World',
|
||||
type: 'FRONT_COMPONENT',
|
||||
configuration: {
|
||||
configurationType: 'FRONT_COMPONENT',
|
||||
frontComponentUniversalIdentifier:
|
||||
HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### 主なポイント
|
||||
|
||||
* `type` は通常、特定のオブジェクトの詳細ビューをカスタマイズするために `'RECORD_PAGE'` を指定します。
|
||||
* `objectUniversalIdentifier` は、このレイアウトを適用するオブジェクトを指定します。
|
||||
* 各 `tab` は、`title`、`position`、`layoutMode`(フリーフォームのレイアウトには `CANVAS`)を持つページのセクションを定義します。
|
||||
* タブ内の各 `widget` は、[front component](/l/ja/developers/extend/apps/layout/front-components)、リレーションリスト、その他のビルトインウィジェットタイプをレンダリングできます。
|
||||
* タブの `position` は表示順を制御します。 組み込みタブの後にカスタムタブを配置するには、より大きな値(例:50)を使用します。
|
||||
|
||||
## definePageLayoutTab
|
||||
|
||||
既存のレイアウトにタブを**追加**したい場合にのみ使用します。たとえば、標準の Company ページにアナリティクスタブを追加する場合や、自分のオブジェクトのレイアウトに AI サマリータブを追加する場合などです。
|
||||
|
||||
```ts src/page-layouts/example-extra-tab.ts
|
||||
import {
|
||||
definePageLayoutTab,
|
||||
PageLayoutTabLayoutMode,
|
||||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk/define';
|
||||
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
|
||||
|
||||
export default definePageLayoutTab({
|
||||
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
|
||||
pageLayoutUniversalIdentifier:
|
||||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage
|
||||
.universalIdentifier,
|
||||
title: 'Hello World',
|
||||
position: 1000,
|
||||
icon: 'IconWorld',
|
||||
layoutMode: PageLayoutTabLayoutMode.CANVAS,
|
||||
widgets: [
|
||||
{
|
||||
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002',
|
||||
title: 'Hello World',
|
||||
type: 'FRONT_COMPONENT',
|
||||
configuration: {
|
||||
configurationType: 'FRONT_COMPONENT',
|
||||
frontComponentUniversalIdentifier:
|
||||
HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### 主なポイント
|
||||
|
||||
* `pageLayoutUniversalIdentifier` は**必須**であり、インストール時点ですでに存在するページレイアウトを指している必要があります。標準の Twenty のレイアウト、または自分のアプリで定義したレイアウトのいずれかです。 別のインストール済みアプリが所有するレイアウトへのアプリ間参照は、現時点ではサポートされていません。 親レイアウトが存在しない場合、インストールは明確な検証エラーとともに失敗します。
|
||||
|
||||
* 標準の Twenty レイアウトの場合は、`twenty-sdk/define` から識別子を import します:
|
||||
|
||||
```ts
|
||||
import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||||
|
||||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier
|
||||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier
|
||||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier
|
||||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier
|
||||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier
|
||||
// …
|
||||
```
|
||||
|
||||
各レイアウトエントリは、その `tabs` とその `widgets` も公開しているため、任意の階層を参照できます:
|
||||
|
||||
```ts
|
||||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.universalIdentifier
|
||||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets.fields.universalIdentifier
|
||||
```
|
||||
|
||||
短いエイリアス `STANDARD_PAGE_LAYOUT` も利用できます:
|
||||
|
||||
```ts
|
||||
import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define';
|
||||
|
||||
STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier;
|
||||
```
|
||||
|
||||
* `widgets` はこのタブのみにスコープされます。`definePageLayout` 内でインライン定義されたウィジェットとまったく同様に、[front components](/l/ja/developers/extend/apps/layout/front-components)、views などを参照します。
|
||||
|
||||
* `position` は、対象のレイアウト上にある既存のタブとの順序を制御します。 組み込みタブとの相対位置が希望どおりになるような値を選択してください。
|
||||
|
||||
* 既存のレイアウトに追加だけを行いたい場合は、`definePageLayout` の代わりにこちらを使用してください。 レイアウト全体を自分が所有している場合は、`definePageLayout` を使用してください。
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: ビュー
|
||||
description: アプリのオブジェクト向けに、列の順序、フィルター、グループが事前設定済みの保存ビューを提供します。
|
||||
icon: list
|
||||
---
|
||||
|
||||
**ビュー**とは、オブジェクトのレコードをどのように表示するかについての保存された設定です。どのフィールドを表示するか、その順序、表示・非表示、および適用されるフィルターやグループなどを含みます。 `defineView()` を使用して、アプリにあらかじめ設定されたビューを組み込みます。通常は、作成する各カスタムオブジェクトに対してデフォルトのインデックスビューを用意します。
|
||||
|
||||
```ts src/views/example-view.ts
|
||||
import { defineView, ViewKey } from 'twenty-sdk/define';
|
||||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
|
||||
import { NAME_FIELD_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
|
||||
|
||||
export default defineView({
|
||||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
name: 'All example items',
|
||||
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
icon: 'IconList',
|
||||
key: ViewKey.INDEX,
|
||||
position: 0,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'f926bdb7-6af7-4683-9a09-adbca56c29f0',
|
||||
fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 200,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 主なポイント
|
||||
|
||||
* `objectUniversalIdentifier` は、このビューを適用するオブジェクトを指定します。 定義したカスタムオブジェクトでも、Twenty の標準オブジェクトでも可能です。
|
||||
* `key` はビューの種類を決定します。`ViewKey.INDEX` は、そのオブジェクトのメインのリストビューです。
|
||||
* `fields` は、どの列をどの順序で表示するかを制御します。 各フィールドは `fieldMetadataUniversalIdentifier` を参照します。
|
||||
* さらに高度な構成のために、`filters`、`filterGroups`、`groups`、`fieldGroups` も定義できます。
|
||||
* 同じオブジェクトに複数のビューがある場合、`position` が表示順を制御します。
|
||||
|
||||
## フィルター
|
||||
|
||||
ビューには、あらかじめフィルターを適用した状態で提供できます。 各フィルターには 3 つの要素があります: フィルタリング対象の**フィールド**、**オペランド**(どのように比較するか)、**値**(何と比較するか)。 この 3 つがすべてそろっている必要があります — フィールドの型に適用できないオペランドを使用すると、同期時に拒否されます。
|
||||
|
||||
```ts
|
||||
import { ViewFilterOperand } from 'twenty-shared/types';
|
||||
|
||||
filters: [
|
||||
{
|
||||
universalIdentifier: '...',
|
||||
fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
operand: ViewFilterOperand.IS,
|
||||
value: ['ACTIVE'],
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
### フィールドタイプごとにサポートされているオペランド
|
||||
|
||||
| フィールドタイプ | サポートされるオペランド |
|
||||
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `BOOLEAN` | `IS` |
|
||||
| `UUID` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
|
||||
| `TS_VECTOR` | `VECTOR_SEARCH` |
|
||||
|
||||
> 名前が似ているフィールドタイプでも、まったく異なるオペランドを使用する場合があります。一般的な例としては `SELECT` と `MULTI_SELECT` があります。
|
||||
|
||||
### オペランドごとの値の形式
|
||||
|
||||
`value` フィールドは常に JSON シリアライズ可能な値ですが、想定される形式はオペランドによって異なります。
|
||||
|
||||
| オペランドの種類 | 値の形式 | 例 |
|
||||
| ------------------------------------------------------- | ---------------------- | ------------------------ |
|
||||
| `SELECT` に対する `IS`, `IS_NOT` | オプションキー(文字列)の配列 | `['ACTIVE', 'PENDING']` |
|
||||
| `MULTI_SELECT` に対する `CONTAINS`, `DOES_NOT_CONTAIN` | オプションキー(文字列)の配列 | `['TAG_A']` |
|
||||
| `RELATION` に対する `IS`, `IS_NOT` | レコード ID(UUID)の配列 | `['c5a1...']` |
|
||||
| テキスト系フィールドに対する `CONTAINS`, `DOES_NOT_CONTAIN` | 文字列 | `'acme'` |
|
||||
| `NUMBER` に対する `IS`, `IS_NOT` | 文字列(値) | `'5'` |
|
||||
| `RATING` / `UUID` に対する `IS` | 文字列(値) | `'5'` |
|
||||
| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | 文字列(境界値) | `'10'` |
|
||||
| `DATE` / `DATE_TIME` に対する `IS`, `IS_BEFORE`, `IS_AFTER` | ISO 8601 文字列 | `'2025-01-01T00:00:00Z'` |
|
||||
| `IS_EMPTY`, `IS_NOT_EMPTY` | 空の文字列 | `''` |
|
||||
| `BOOLEAN` に対する `IS` | `'true'` または `'false'` | `'true'` |
|
||||
|
||||
## ビューが UI にどのように表示されるか
|
||||
|
||||
ビュー単体では、サイドバーから直接アクセスすることはできません。 それをサイドバーに表示するには、ビューの `universalIdentifier` を指す、種類が `VIEW` の[ナビゲーションメニュー項目](/l/ja/developers/extend/apps/layout/navigation-menu-items)と組み合わせます。 これが標準的なパターンです。各カスタムオブジェクトは通常、デフォルトビューと、それを開くためのサイドバーエントリをセットで提供します。
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
title: 接続
|
||||
description: OAuth を介して、アプリがユーザーに代わってサードパーティのサービスで操作を行えるようにします。
|
||||
icon: plug
|
||||
---
|
||||
|
||||
接続(Connection)は、ユーザーが外部サービス(Linear、GitHub、Slack など)のために保持する認証情報です。 あなたのアプリは、それらの認証情報を**どのように**取得するか — **接続プロバイダー** — を宣言し、実行時にそれらを利用してサードパーティの API へ認証付きの呼び出しを行います。
|
||||
|
||||
現在は OAuth 2.0 のみサポートされています。 将来的な認証情報タイプ(パーソナルアクセストークン、API キー、Basic 認証)も同じインターフェースに差し込まれます — すでに `defineConnectionProvider({ type: 'oauth', ... })` を使用しているアプリは移行の必要がありません。
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="defineConnectionProvider" description="アプリの接続をどのように取得するかを宣言する">
|
||||
|
||||
接続プロバイダーは、アプリに必要な OAuth のハンドシェイクを記述します。 ユーザーがアプリの設定で"接続を追加"をクリックし、プロバイダーの同意画面を完了すると、そのワークスペースに `ConnectedAccount` の行が作成されます。
|
||||
|
||||
動作するセットアップには、**2 つのファイル**が必要です — 接続プロバイダーと、OAuth クライアント認証情報を保持する `defineApplication` 上の対応する `serverVariables` 宣言です。
|
||||
|
||||
```ts src/connection-providers/linear-connection.ts
|
||||
import { defineConnectionProvider } from 'twenty-sdk/define';
|
||||
|
||||
export default defineConnectionProvider({
|
||||
universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f',
|
||||
name: 'linear',
|
||||
displayName: 'Linear',
|
||||
icon: 'IconBrandLinear',
|
||||
type: 'oauth',
|
||||
oauth: {
|
||||
authorizationEndpoint: 'https://linear.app/oauth/authorize',
|
||||
tokenEndpoint: 'https://api.linear.app/oauth/token',
|
||||
scopes: ['read', 'write'],
|
||||
// These must match keys in `defineApplication.serverVariables` below.
|
||||
clientIdVariable: 'LINEAR_CLIENT_ID',
|
||||
clientSecretVariable: 'LINEAR_CLIENT_SECRET',
|
||||
// Optional: defaults to 'json'. Some providers (Linear, Slack) want
|
||||
// 'form-urlencoded' for the token request.
|
||||
tokenRequestContentType: 'form-urlencoded',
|
||||
// Optional: defaults to true. Disable only if the provider rejects PKCE.
|
||||
usePkce: false,
|
||||
// Optional: extra query params on the authorize URL.
|
||||
// authorizationParams: { prompt: 'consent' },
|
||||
// Optional: provider's RFC 7009 token revocation endpoint, called on disconnect.
|
||||
// revokeEndpoint: 'https://example.com/oauth/revoke',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts src/application.config.ts
|
||||
import { defineApplication } from 'twenty-sdk/define';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: '...',
|
||||
displayName: 'Linear',
|
||||
description: 'Connect Linear to Twenty.',
|
||||
// OAuth client credentials live on the app registration (one OAuth app per
|
||||
// Twenty server, configured by the admin) — not per-workspace. Declare them
|
||||
// as serverVariables so the admin can fill them in once for all installs.
|
||||
serverVariables: {
|
||||
LINEAR_CLIENT_ID: {
|
||||
description: 'OAuth client ID from your Linear OAuth application.',
|
||||
isSecret: false,
|
||||
isRequired: true,
|
||||
},
|
||||
LINEAR_CLIENT_SECRET: {
|
||||
description: 'OAuth client secret from your Linear OAuth application.',
|
||||
isSecret: true,
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
|
||||
* `listConnections({ providerName })` で使用される一意の識別子文字列が `name` です(kebab-case、`^[a-z][a-z0-9-]*$` に一致する必要があります)。
|
||||
* `displayName` はアプリごとの設定タブおよび AI ツール一覧に表示されます。
|
||||
* `clientIdVariable` / `clientSecretVariable` は値ではなく**名前**です — `defineApplication.serverVariables` で宣言されたキーと一致している必要があります。 実際の `client_id` と `client_secret` は、アプリ登録 UI を通じてサーバー管理者が入力し、あなたのリポジトリにコミットされることはありません。
|
||||
* `applicationVariables` ではなく `serverVariables` を使用してください — OAuth の認証情報はサーバー全体で共有され、Twenty サーバーごとに 1 つの OAuth アプリとなります。
|
||||
* 両方の `serverVariables` が入力されるまで、アプリごとの設定タブには"サーバー管理者が必要"というヒントが表示され、"接続を追加"ボタンは無効になります。
|
||||
* 現在サポートされる値は `type: 'oauth'` のみです。 この判別子は前方互換です。将来のタイプ(`'pat'`、`'api-key'`、...) は、`oauth` に並ぶ新しいサブ設定ブロックを追加します。
|
||||
|
||||
プロバイダーで許可リストに登録する必要がある OAuth のコールバック URL は次のとおりです:
|
||||
|
||||
```
|
||||
https://<your-twenty-server>/auth/apps/callback
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="listConnections / getConnection" description="ロジック関数から接続を使用する">
|
||||
|
||||
ロジック関数ハンドラー内では、`listConnections({ providerName })` が指定したプロバイダーに対するこのアプリの `ConnectedAccount` 行を、更新済みのアクセストークン付きで返します。
|
||||
|
||||
```ts src/logic-functions/handlers/create-linear-issue-handler.ts
|
||||
import { listConnections } from 'twenty-sdk/logic-function';
|
||||
|
||||
export const createLinearIssueHandler = async (input: {
|
||||
teamId?: string;
|
||||
title?: string;
|
||||
}) => {
|
||||
if (!input.teamId || !input.title) {
|
||||
return { success: false, error: 'teamId and title are required' };
|
||||
}
|
||||
|
||||
const connections = await listConnections({ providerName: 'linear' });
|
||||
|
||||
// Workspace-shared credentials win when present; fall back to the first
|
||||
// user-visibility one. For HTTP-route triggers you typically pick the
|
||||
// request user's connection via event.userWorkspaceId instead.
|
||||
const connection =
|
||||
connections.find((c) => c.visibility === 'workspace') ?? connections[0];
|
||||
|
||||
if (!connection) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
'Linear is not connected. Open the app settings and click "Add connection".',
|
||||
};
|
||||
}
|
||||
|
||||
// Use connection.accessToken to call the third-party API.
|
||||
const response = await fetch('https://api.linear.app/graphql', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${connection.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`,
|
||||
}),
|
||||
});
|
||||
|
||||
return { success: response.ok };
|
||||
};
|
||||
```
|
||||
|
||||
各接続には次の項目があります:
|
||||
|
||||
| フィールド | 説明 |
|
||||
| ----------------- | ---------------------------------------------------------- |
|
||||
| `id` | 一意の行 ID。単一の接続を再取得するには `getConnection(id)` に渡します。 |
|
||||
| `visibility` | `'user'`(ワークスペースの 1 メンバー専用)または `'workspace'`(全メンバーと共有) |
|
||||
| `scopes` | 連携先プロバイダーによって付与された OAuth 権限(`visibility` とは別で、無関係です) |
|
||||
| `userWorkspaceId` | 所有者の userWorkspace ID — HTTP ルートトリガーで"要求ユーザーの接続"を選ぶのに役立ちます |
|
||||
| `accessToken` | 新しい OAuth アクセストークン(期限切れの場合は自動更新) |
|
||||
| `name` / `handle` | 接続の表示名(OAuth コールバック時に自動取得。ユーザーが名称変更可能) |
|
||||
| `authFailedAt` | 直近の更新に失敗した場合に設定されます。ユーザーは再接続する必要があります。 |
|
||||
|
||||
主なポイント:
|
||||
|
||||
* プロバイダーで絞り込むには `{ providerName }` を渡します。省略すると、このアプリが保有するすべてのプロバイダーの接続を取得します。
|
||||
* サーバーは返却前にアクセストークンを透過的に更新します。 ハンドラーは常に利用可能なトークン(または `authFailedAt` が設定された状態)を受け取ります。
|
||||
* `getConnection(id)` は単一行の取得に相当します。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ユーザー単位 vs ワークスペース共有の可視性" description="ユーザーがプライベートと共有の認証情報を選ぶ方法">
|
||||
|
||||
ユーザーが"接続を追加"をクリックすると、可視性の選択を求められます:
|
||||
|
||||
* **自分だけ** — 認証情報は接続したユーザー専用です。 そのユーザーの権限で呼び出される任意のロジック関数(`isAuthRequired: true` の HTTP ルートトリガー)は参照できますが、cron トリガーやデータベースイベントは参照できません。
|
||||
* **ワークスペースで共有** — どのワークスペースメンバーでもその認証情報を使用できます。 リクエストユーザーを持たないため、Cron/データベースのトリガーからも参照されます。
|
||||
|
||||
各ハンドラーで適切な方を使用してください:
|
||||
|
||||
```ts
|
||||
// HTTP-route trigger — prefer the request user's own connection.
|
||||
const conn =
|
||||
connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ??
|
||||
connections.find((c) => c.visibility === 'workspace');
|
||||
|
||||
// Cron trigger — no request user; only shared credentials are sensible.
|
||||
const conn = connections.find((c) => c.visibility === 'workspace');
|
||||
```
|
||||
|
||||
(ユーザー、プロバイダー)ごとに複数の接続が許可されているため、同一ユーザーが "Personal Linear" と "Work Linear" を並行して保持できます。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="一度きりのプロバイダー設定" description="サードパーティサービスに OAuth アプリを登録する">
|
||||
|
||||
各接続プロバイダーごとに、まずサーバー管理者がサードパーティ側で OAuth アプリを登録する必要があります。
|
||||
|
||||
1. プロバイダーの開発者設定に移動します(例: https://linear.app/settings/api/applications/new)。
|
||||
2. **Redirect URI** を `\<SERVER_URL>/auth/apps/callback` に設定します。
|
||||
3. 生成された **Client ID** と **Client Secret** をコピーします。
|
||||
4. サーバー管理者として Twenty でインストール済みアプリを開き、対応する `serverVariables` に値を設定します。
|
||||
5. その後、ワークスペースメンバーはアプリごとの **Connections** セクションから接続を追加できます。
|
||||
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
title: キーバリューストア
|
||||
description: シンプルなキーと値のオブジェクトを使って、中間結果を永続化し、データをキャッシュし、ロジック関数の実行間で状態を共有します。
|
||||
icon: database
|
||||
---
|
||||
|
||||
ロジック関数は短命な Node.js プロセス内でサンドボックス実行されます。1 回の実行が終了すると、メモリ上に保持されていたものは何も残りません。 実行間で**何かを記憶しておく必要がある**場合(高コストな API レスポンスのキャッシュ、増分同期のためのカーソルの保存、処理のデバウンス、ある関数から別の関数への状態の受け渡しなど)、ワークスペースのデータベースに永続化します。
|
||||
|
||||
これには専用のストレージプリミティブは必要ありません。`key` フィールドと `value` フィールドを持つ小さな**技術的なオブジェクト**があれば、ワークスペースをスコープとした永続的なキーバリューストアになり、既にレコード用に使用しているのと同じ [型付き API クライアント](/l/ja/developers/extend/apps/logic/logic-functions#typed-api-clients-twenty-client-sdk)からクエリできます。
|
||||
|
||||
```text
|
||||
┌─────────────────┐ set(key, value) ┌──────────────────────────┐
|
||||
│ Logic function │ ───────────────────▶ │ "KV Store" object │
|
||||
│ (your handler) │ ◀─────────────────── │ key (unique) │ value │
|
||||
└─────────────────┘ get(key) └──────────────────────────┘
|
||||
```
|
||||
|
||||
## ストアオブジェクトを定義する
|
||||
|
||||
2 つのフィールドを持つカスタムオブジェクトを宣言します — `key`(一意な `TEXT`)と `value`(任意の JSON シリアライズ可能なペイロードを保存できるようにするための `RAW_JSON`)。 完全な `defineObject` のリファレンスについては [Objects](/l/ja/developers/extend/apps/data/objects) を参照してください。
|
||||
|
||||
```ts src/objects/kv-store.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk/define';
|
||||
|
||||
export const KV_STORE_UNIVERSAL_IDENTIFIER =
|
||||
'2f1c8a90-3b6d-4e2a-9c47-7d0e5a1b9f33';
|
||||
export const KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER =
|
||||
'4a7e2d11-9c83-4f60-b5a2-1e6c8d0f4b21';
|
||||
export const KV_STORE_VALUE_FIELD_UNIVERSAL_IDENTIFIER =
|
||||
'8b3f6c02-5d19-47ae-9f31-2c4a7e0b6d58';
|
||||
|
||||
export default defineObject({
|
||||
universalIdentifier: KV_STORE_UNIVERSAL_IDENTIFIER,
|
||||
nameSingular: 'kvStore',
|
||||
namePlural: 'kvStores',
|
||||
labelSingular: 'KV Store',
|
||||
labelPlural: 'KV Store',
|
||||
description: 'Key-value storage for logic functions',
|
||||
icon: 'IconDatabase',
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
name: 'key',
|
||||
type: FieldType.TEXT,
|
||||
label: 'Key',
|
||||
description: 'Unique lookup key',
|
||||
icon: 'IconKey',
|
||||
},
|
||||
{
|
||||
universalIdentifier: KV_STORE_VALUE_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
name: 'value',
|
||||
type: FieldType.RAW_JSON,
|
||||
label: 'Value',
|
||||
description: 'Stored JSON payload',
|
||||
icon: 'IconJson',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### キーの一意性を保証する
|
||||
|
||||
`key` に対して**一意インデックス**を追加し、同じキーに 2 行が割り当てられないようにします。 これは一意性のために推奨されるプリミティブです。詳細は [Data → Unique indexes](/l/ja/developers/extend/apps/data/overview#unique-indexes) を参照してください。
|
||||
|
||||
```ts src/indexes/kv-store-key.index.ts
|
||||
import { defineIndex } from 'twenty-sdk/define';
|
||||
import {
|
||||
KV_STORE_UNIVERSAL_IDENTIFIER,
|
||||
KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
} from '../objects/kv-store.object';
|
||||
|
||||
export default defineIndex({
|
||||
universalIdentifier: 'c0d4e8f2-6a1b-4c93-8e57-3f9a2d0b7e14',
|
||||
objectUniversalIdentifier: KV_STORE_UNIVERSAL_IDENTIFIER,
|
||||
isUnique: true,
|
||||
fields: [
|
||||
{
|
||||
universalIdentifier: 'c0d4e8f2-6a1b-4c93-8e57-3f9a2d0b7e15',
|
||||
fieldUniversalIdentifier: KV_STORE_KEY_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## ロジック関数から読み書きする
|
||||
|
||||
オブジェクトをいくつかの小さなヘルパーでラップし、残りのコードを `get`、`set`、`del` を持つキーバリュー API のように記述できるようにします。 これらは [`CoreApiClient`](/l/ja/developers/extend/apps/logic/logic-functions#typed-api-clients-twenty-client-sdk) を使用します。これはワークスペースのスキーマから生成され、`kvStore` オブジェクトに対して完全に型付けされています。
|
||||
|
||||
```ts src/logic-functions/handlers/kv-store.ts
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
import { isDefined } from 'twenty-sdk/utils';
|
||||
|
||||
const client = new CoreApiClient();
|
||||
|
||||
// Look up a single row by its key.
|
||||
const findByKey = async (key: string) => {
|
||||
const { kvStores } = await client.query({
|
||||
kvStores: {
|
||||
__args: { filter: { key: { eq: key } }, first: 1 },
|
||||
edges: { node: { id: true, value: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return kvStores.edges[0]?.node;
|
||||
};
|
||||
|
||||
// Read a value. Returns undefined when the key is missing.
|
||||
export const get = async <TValue>(key: string): Promise<TValue | undefined> => {
|
||||
const row = await findByKey(key);
|
||||
|
||||
return isDefined(row) ? (row.value as TValue) : undefined;
|
||||
};
|
||||
|
||||
// Write a value. Creates the row on first write, updates it afterwards (upsert).
|
||||
export const set = async (key: string, value: unknown): Promise<void> => {
|
||||
const existing = await findByKey(key);
|
||||
|
||||
if (isDefined(existing)) {
|
||||
await client.mutation({
|
||||
updateKvStore: {
|
||||
__args: { id: existing.id, data: { value } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await client.mutation({
|
||||
createKvStore: {
|
||||
__args: { data: { key, value } },
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Delete a value. No-op when the key is missing.
|
||||
export const del = async (key: string): Promise<void> => {
|
||||
const existing = await findByKey(key);
|
||||
|
||||
if (isDefined(existing)) {
|
||||
await client.mutation({
|
||||
deleteKvStore: { __args: { id: existing.id }, id: true },
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
一意インデックスは重複を防ぎますが、**同じ新しいキー**を 2 つの実行がまったく同時に書き込もうとした場合、ルックアップと作成の間で競合状態が発生する可能性があります。 作成が一意制約で失敗した場合は「他の誰かが勝った」と見なし、それをキャッチして再読み込みするか、更新としてリトライします。
|
||||
</Note>
|
||||
|
||||
## 使ってみる:高コストな呼び出しをキャッシュする
|
||||
|
||||
典型的な用途としては、遅い、またはレート制限されたサードパーティのレスポンスをキャッシュしておき、毎回コストを支払うのではなく、繰り返しの実行で再利用することが挙げられます。
|
||||
|
||||
```ts src/logic-functions/getExchangeRate.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import { get, set } from './handlers/kv-store';
|
||||
|
||||
const ONE_HOUR_MS = 60 * 60 * 1000;
|
||||
|
||||
type CachedRate = { rate: number; fetchedAt: number };
|
||||
|
||||
const handler = async (params: { from: string; to: string }) => {
|
||||
const cacheKey = `exchange-rate:${params.from}:${params.to}`;
|
||||
const cached = await get<CachedRate>(cacheKey);
|
||||
|
||||
if (cached && Date.now() - cached.fetchedAt < ONE_HOUR_MS) {
|
||||
return { rate: cached.rate, cached: true };
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.example.com/rate?from=${params.from}&to=${params.to}`,
|
||||
);
|
||||
const { rate } = (await response.json()) as { rate: number };
|
||||
|
||||
await set(cacheKey, { rate, fetchedAt: Date.now() });
|
||||
|
||||
return { rate, cached: false };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'd9b2f4e6-1c83-4a07-9e52-6b1d3c8a0f47',
|
||||
name: 'get-exchange-rate',
|
||||
timeoutSeconds: 10,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
## パターン & ヒント
|
||||
|
||||
* **ネームスペース化。** さまざまな用途を分離し、まとめてルックアップしやすくするために、キーにプレフィックスを付けます — `sync-cursor:linear`、`cache:exchange-rate:USD:EUR`、`lock:nightly-report`。 `key: { like: 'cache:%' }` でフィルタリングして、ネームスペース全体を一覧表示またはクリアします。
|
||||
* **期限 (TTL)。** ストアには組み込みの有効期限はありません。 `value` 内にタイムスタンプを保存して(キャッシュの例のように)読み取り時にチェックするか、`DATE_TIME` フィールドを追加し、[cron-triggered function](/l/ja/developers/extend/apps/logic/logic-functions) から定期的に古い行をクリアします。
|
||||
* **何を保存するか。** `RAW_JSON` には、数値、文字列、配列、オブジェクトなど、任意の JSON シリアライズ可能な値を格納できます。 エントリは小さく保ってください。これは調整やキャッシュのためのものであり、大きな BLOB やファイル用ではありません。 ファイルには `FILES` フィールドと [`uploadFile`](/l/ja/developers/extend/apps/logic/logic-functions#uploading-files) を使用してください。
|
||||
* **可視性と権限。** 行は他のレコードと同様にワークスペースのデータベース内に存在するため、API を通じてクエリでき、アプリの [role](/l/ja/developers/extend/apps/config/roles) に従った権限が適用されます。 ストアをメイン UI から隠しておきたい場合は、[navigation menu](/l/ja/developers/extend/apps/layout/navigation-menu-items) に追加しないでください。
|
||||
* **レコードへのスコープ。** グローバルなキーではなく、レコード単位の状態が必要ですか? ストアオブジェクトから対象オブジェクトへの [relation](/l/ja/developers/extend/apps/data/relations) を追加し、id をキーにエンコードするのではなく関連付けを使います。
|
||||
|
||||
<Note>
|
||||
これは約束事であり、別個の機能ではありません。「KV Store」は、標準の API で定義およびクエリする、通常のカスタムオブジェクトにすぎません。 つまり、アプリの他のデータと同様に、同じ同期、権限、ツール群の恩恵を受けられます。
|
||||
</Note>
|
||||
@@ -0,0 +1,677 @@
|
||||
---
|
||||
title: ロジック関数
|
||||
description: HTTP、cron、およびデータベースのイベントトリガーを備えたサーバーサイドの TypeScript 関数を定義します。
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
ロジック関数は、Twenty プラットフォーム上で実行されるサーバーサイドの TypeScript 関数です。 HTTPリクエスト、cronスケジュール、またはデータベースイベントでトリガーでき、AIエージェント向けのツールとして公開することもできます。
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineLogicFunction" description="ロジック関数とそのトリガーを定義">
|
||||
|
||||
各関数ファイルは、ハンドラーと任意のトリガーを含む設定を `defineLogicFunction()` でエクスポートします。
|
||||
|
||||
```ts src/logic-functions/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import type { RoutePayload } from 'twenty-sdk/logic-function';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new CoreApiClient();
|
||||
const body = (params.body ?? {}) as { name?: string };
|
||||
const name = body.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world';
|
||||
|
||||
const result = await client.mutation({
|
||||
createPostCard: {
|
||||
__args: { data: { name } },
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||||
name: 'create-new-post-card',
|
||||
timeoutSeconds: 2,
|
||||
handler,
|
||||
httpRouteTriggerSettings: {
|
||||
path: '/post-card/create',
|
||||
httpMethod: 'POST',
|
||||
isAuthRequired: true,
|
||||
},
|
||||
/*databaseEventTriggerSettings: {
|
||||
eventName: 'people.created',
|
||||
},*/
|
||||
/*cronTriggerSettings: {
|
||||
pattern: '0 0 1 1 *',
|
||||
},*/
|
||||
});
|
||||
```
|
||||
|
||||
利用可能なトリガーの種類:
|
||||
* **httpRoute**:`/s/` エンドポイント配下で、HTTP のパスとメソッドで関数を公開します:
|
||||
> 例:`path: '/post-card/create'` は `https://your-twenty-server.com/s/post-card/create` で呼び出せます
|
||||
|
||||
<Note>
|
||||
(ヘッドレスの)フロントコンポーネントからルートトリガー型ロジック関数を呼び出す方法については、[ロジック関数を呼び出す](/l/ja/developers/extend/apps/layout/front-components#calling-a-logic-function)を参照してください。
|
||||
</Note>
|
||||
* **cron**: CRON 式を使用してスケジュールで関数を実行します。
|
||||
* **databaseEvent**: ワークスペースのオブジェクトのライフサイクルイベントで実行されます。 イベント操作が `updated` の場合、監視する特定のフィールドを `updatedFields` 配列で指定できます。 未定義または空のままにすると、任意の更新でも関数がトリガーされます。
|
||||
> 例:`person.updated`、`*.created`、`company.*`
|
||||
* **serverRoute**: 1 つの登録スコープの HTTP ルートを公開します。 **resolver** 関数(`serverRouteTriggerSettings` で宣言)は、オーナーワークスペースで実行され、ディスパッチ先のターゲットワークスペースとターゲットロジック関数の両方を返します。その後、プラットフォームはその **target** 関数を実行し、そのレスポンスを返します。 [サーバールートトリガー](#server-route-trigger) を参照してください。
|
||||
|
||||
<Note>
|
||||
CLI を使用して、関数を手動で実行することもできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}'
|
||||
```
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
ログは次のコマンドで監視できます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:function:logs
|
||||
```
|
||||
</Note>
|
||||
|
||||
#### ルートトリガーのペイロード
|
||||
|
||||
ルートトリガーがロジック関数を呼び出すと、
|
||||
[AWS HTTP API v2 形式](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) に従う `RoutePayload` オブジェクトを受け取ります。
|
||||
`RoutePayload` 型を `twenty-sdk/logic-function` からインポートします:
|
||||
|
||||
```ts
|
||||
import type { RoutePayload } from 'twenty-sdk/logic-function';
|
||||
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const { headers, queryStringParameters, pathParameters, body } = event;
|
||||
const { method, path } = event.requestContext.http;
|
||||
|
||||
return { message: 'Success' };
|
||||
};
|
||||
```
|
||||
|
||||
`RoutePayload` 型は次の構造になっています:
|
||||
|
||||
| プロパティ | タイプ | 説明 | 例 |
|
||||
| ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
|
||||
| `headers` | `Record\<string, string \| undefined>` | HTTP ヘッダー (`forwardedRequestHeaders` に列挙されたもののみ) | 下記のセクションを参照 |
|
||||
| `queryStringParameters` | `Record\<string, string \| undefined>` | クエリ文字列パラメーター (複数の値はカンマで連結) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` |
|
||||
| `pathParameters` | `Record\<string, string \| undefined>` | ルートパターンから抽出されたパスパラメーター | `/users/:id`, `/users/123` -> `{ id: '123' }` |
|
||||
| `body` | `object \| null` | 解析済みのリクエストボディ (JSON) | `{ id: 1 }` -> `{ id: 1 }` |
|
||||
| `rawBody` | `string \| undefined` | JSON 解析前の元の UTF-8 リクエストボディ。 HMAC 方式の Webhook 署名を検証する際に役立ちます(例: GitHub の `X-Hub-Signature-256`、Stripe)。 ランタイムがそれを保持しなかった場合は `undefined` です。 | |
|
||||
| `isBase64Encoded` | `boolean` | body が base64 エンコードされているかどうか | |
|
||||
| `requestContext.http.method` | `string` | HTTP メソッド (GET, POST, PUT, PATCH, DELETE) | |
|
||||
| `requestContext.http.path` | `string` | 生のリクエストパス | |
|
||||
|
||||
|
||||
#### forwardedRequestHeaders
|
||||
|
||||
デフォルトでは、セキュリティ上の理由から、受信リクエストの HTTP ヘッダーはロジック関数に**渡されません**。
|
||||
特定のヘッダーにアクセスするには、`forwardedRequestHeaders` 配列に明示的に列挙してください:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||||
name: 'webhook-handler',
|
||||
handler,
|
||||
httpRouteTriggerSettings: {
|
||||
path: '/webhook',
|
||||
httpMethod: 'POST',
|
||||
isAuthRequired: false,
|
||||
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
ハンドラー内では、転送されたヘッダーに次のようにアクセスします:
|
||||
|
||||
```ts
|
||||
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>
|
||||
ヘッダー名は小文字に正規化されます。 小文字のキーを使用してアクセスしてください(例:`event.headers['content-type']`)。
|
||||
</Note>
|
||||
|
||||
#### カスタム HTTP レスポンス
|
||||
|
||||
デフォルトでは、ハンドラーからプレーンな値を返すと、それが `200` レスポンスとして送信されます(オブジェクトの場合は JSON、文字列の場合は `text/plain`)。 ステータスコードとレスポンスヘッダーを制御するには、`twenty-sdk/logic-function` から `Response` を返します。
|
||||
|
||||
```ts
|
||||
import { Response } from 'twenty-sdk/logic-function';
|
||||
|
||||
const handler = async (event: RoutePayload) => {
|
||||
return new Response('<h1>Hello</h1>', {
|
||||
status: 201,
|
||||
headers: { 'content-type': 'text/html' },
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
セキュリティ上の理由から、レスポンスヘッダーは許可リストに限定されています。 リストに含まれていないヘッダー(例: `Set-Cookie`、`Access-Control-Allow-Origin` などの CORS ヘッダー、カスタム `X-*` ヘッダー)は、レスポンスが送信される前に暗黙的に削除されます。 許可されているレスポンスヘッダーは次のとおりです。
|
||||
|
||||
* `content-type`
|
||||
* `content-language`
|
||||
* `content-disposition`
|
||||
* `cache-control`
|
||||
* `retry-after`
|
||||
|
||||
<Note>
|
||||
ステータスコードは、有効な HTTP ステータスコード(100 から 599 の間)でなければなりません。 レスポンスヘッダー名は、大文字と小文字を区別せずに照合されます。
|
||||
</Note>
|
||||
|
||||
#### サーバールートトリガー
|
||||
|
||||
`httpRouteTriggerSettings` は `/s/` 配下に関数を公開し、リクエストホストからワークスペースを特定します。これは、各ワークスペースが独自ドメインを持つ場合に機能します。 しかし、サードパーティプロバイダーは、すべてのテナントのイベントを **1 つの** URL に配信します。 その場合は、`serverRouteTriggerSettings` を使用します。
|
||||
|
||||
トリガーには 2 つの構成要素があります:
|
||||
|
||||
1. **resolver** ロジック関数 — `serverRouteTriggerSettings` で宣言される — は、**オーナーワークスペース**(アプリケーション登録を所有するワークスペース)で実行されます。 受信リクエストを検査し、`{ workspaceId, targetLogicFunctionUniversalIdentifier, payload? }` を返し、ターゲットのワークスペースと関数の *両方* を選択します。 resolver は単一の認可ポイントであり、URL には resolver の識別子のみが含まれます。 **ここがリクエスト署名を検証するための推奨箇所です**。resolver は副作用が発生する前に実行され、元の `rawBody` と転送されたヘッダーにアクセスでき、ターゲットに一切触れずにリクエストを拒否できます。
|
||||
2. **target** ロジック関数 — 通常のワークスペース単位のロジック関数 — は、その後、resolver によって解決されたワークスペースで、resolver が返したペイロード(resolver が変換しなかった場合は元のリクエストペイロード)を使って実行されます。 その戻り値が HTTP レスポンスになります。
|
||||
|
||||
```ts src/logic-functions/resolve-server-route.logic-function.ts
|
||||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import type { RoutePayload } from 'twenty-sdk/logic-function';
|
||||
|
||||
// Runs in the owner workspace. Verifies the request signature, picks
|
||||
// which target function should handle the event, and returns the
|
||||
// workspace + target the platform should dispatch to.
|
||||
const handler = async (event: RoutePayload) => {
|
||||
// Fail closed if the secret isn't configured — never fall back to an
|
||||
// empty key, which would let any caller forge a matching signature.
|
||||
const secret = process.env.GITHUB_WEBHOOK_SECRET;
|
||||
|
||||
if (!secret) {
|
||||
throw new Error('GITHUB_WEBHOOK_SECRET is not configured');
|
||||
}
|
||||
|
||||
const signature = event.headers['x-hub-signature-256'] ?? '';
|
||||
const expected =
|
||||
'sha256=' +
|
||||
createHmac('sha256', secret).update(event.rawBody ?? '').digest('hex');
|
||||
|
||||
const a = Buffer.from(signature);
|
||||
const b = Buffer.from(expected);
|
||||
|
||||
if (a.length !== b.length || !timingSafeEqual(a, b)) {
|
||||
throw new Error('invalid signature');
|
||||
}
|
||||
|
||||
const body = (event.body ?? {}) as {
|
||||
metadata?: { twentyWorkspaceId?: string };
|
||||
type?: string;
|
||||
};
|
||||
|
||||
return {
|
||||
workspaceId: body.metadata?.twentyWorkspaceId ?? '',
|
||||
// Route different event types to different target functions.
|
||||
targetLogicFunctionUniversalIdentifier:
|
||||
body.type === 'invoice.paid'
|
||||
? 'c4e2a9b1-7d4e-4c9a-9f2b-2e1d6a4c8e10' // handle-invoice-paid
|
||||
: 'd5f3b0c2-8e5f-5d0b-a0c3-3f2e7b5d9f21', // handle-other-event
|
||||
};
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'b3c2f0a1-7d4e-4c9a-9f2b-2e1d6a4c8e10',
|
||||
name: 'resolve-server-route',
|
||||
handler,
|
||||
serverRouteTriggerSettings: {
|
||||
forwardedRequestHeaders: ['x-hub-signature-256'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts src/logic-functions/handle-invoice-paid.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import type { RoutePayload } from 'twenty-sdk/logic-function';
|
||||
|
||||
// Runs in the resolved workspace. The resolver has already authenticated
|
||||
// the request, so this handler can focus on the actual work.
|
||||
const handler = async (event: RoutePayload) => {
|
||||
// ...handle the verified event
|
||||
return { received: true };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'c4e2a9b1-7d4e-4c9a-9f2b-2e1d6a4c8e10',
|
||||
name: 'handle-invoice-paid',
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
エンドポイントには次の URL でアクセスできます:
|
||||
|
||||
```
|
||||
POST https://your-twenty-server.com/webhooks/server/:resolverLogicFunctionUniversalIdentifier
|
||||
```
|
||||
|
||||
識別子は、マニフェストに記載されている resolver の `universalIdentifier` です。 その URL をプロバイダーに登録します。
|
||||
|
||||
<Note>
|
||||
**アプリケーションは所有者ワークスペースでクレームされ、インストールされている必要があります。** リゾルバーは **所有者ワークスペース**(アプリケーション登録を所有しているワークスペース)上で実行されるため、サーバールートトリガーが動作するのは、アプリケーションが*クレーム*されている、つまり所有者ワークスペースを持っていること **かつ** そのアプリケーションが **所有者ワークスペースにインストールされている** 場合のみです。 この2つの条件がどちらも満たされるまでは、リゾルバーを実行する場所が存在しないため、ルートをディスパッチできません。 したがって、`serverRouteTriggerSettings` ロジック関数を公開するアプリケーションは、所有者ワークスペースでクレームされインストールされるまで、マーケットプレイスに掲載することはできません。
|
||||
</Note>
|
||||
|
||||
**Resolver の契約**。SDK の `LogicFunctionConfig` 型は、コンパイル時にこれを強制します。`serverRouteTriggerSettings` を設定するとすぐに、ハンドラーは `{ workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object }`(またはその `Promise`)を返すように制約されます。 `workspaceId` は、ターゲット関数がインストールされているワークスペースである必要があります。そうでない場合、リクエストは `404` で拒否されます。
|
||||
|
||||
| フィールド | タイプ | ノート |
|
||||
| ---------------------------------------- | --------------- | -------------------------------------------- |
|
||||
| `workspaceId` | `string` | ターゲットが実行されるワークスペースの UUID。 |
|
||||
| `targetLogicFunctionUniversalIdentifier` | `string` | そのワークスペースで呼び出すロジック関数の `universalIdentifier`。 |
|
||||
| `payload` | `object`(オプション) | 設定された場合、ターゲットに送信されるリクエストボディを置き換えます。 |
|
||||
|
||||
<Warning>
|
||||
**署名の検証はあなたの責任です — resolver 内で検証してください。** プラットフォームはリクエスト署名を検証しません。 resolver はそれを行う推奨箇所です。最初に実行され、`event.rawBody` と、`forwardedRequestHeaders` に指定したヘッダーにアクセスできます。また、エラーをスローする(または一致しない `workspaceId` を返す)ことで、ターゲットが呼び出される前にディスパッチを停止できます。 代わりに検証処理を target 側に押し下げる場合、target は `rawBody` とヘッダーを失わないよう注意する必要があります。つまり、resolver は `payload` を返してはいけません。 副作用を伴う処理の**前に**必ず検証を行い、コンスタントタイム比較を使用してください。
|
||||
</Warning>
|
||||
|
||||
リクエスト署名については、ほとんどのプロバイダーが HMAC-SHA256 で署名します。異なるのはヘッダー名、ダイジェストのエンコーディング、および署名対象となるペイロード文字列です。 いくつかの例を示します。
|
||||
|
||||
| プロバイダー | 転送するヘッダー | 署名対象文字列 | ダイジェスト |
|
||||
| ---------------------------- | ------------------------------------------------------ | ---------------------------- | ---------------------------------------------------- |
|
||||
| Svix (Recall, Resend, Clerk) | `webhook-id`, `webhook-timestamp`, `webhook-signature` | `{id}.{timestamp}.{rawBody}` | base64(シークレットは `whsec_` を取り除いた後の文字列を base64 として扱います) |
|
||||
| Stripe | `stripe-signature` | `{timestamp}.{rawBody}` | hex |
|
||||
| GitHub | `x-hub-signature-256` | `{rawBody}` | hex(先頭に `sha256=` が付与されます) |
|
||||
| Shopify | `x-shopify-hmac-sha256` | `{rawBody}` | base64 |
|
||||
| Slack | `x-slack-signature`, `x-slack-request-timestamp` | `v0:{timestamp}:{rawBody}` | hex(先頭に `v0=` が付与されます) |
|
||||
|
||||
上記の resolver の例では、すでに GitHub の HMAC-SHA256 フローを示しています。連携するプロバイダーに合わせて、ヘッダー名、ダイジェストのエンコーディング、および署名対象となるペイロード文字列を調整してください。
|
||||
|
||||
<Note>
|
||||
target は**同期的に**実行され、その戻り値が HTTP レスポンスになるため、呼び出し元はあなたのステータスコードを確認し、2xx 以外の場合にリトライできます。 両方のハンドラーを高速に保ってください。Slack など一部のプロバイダーは数秒でタイムアウトします。 resolver はパブリックエンドポイントとして到達可能であるため、エッジでレート制限をかけて保護してください。
|
||||
</Note>
|
||||
|
||||
#### データベースイベントトリガーのペイロード
|
||||
|
||||
データベースイベントトリガーがロジック関数を呼び出すと、変更されたレコードごとに 1 つの `DatabaseEventPayload` を受け取ります。 このペイロードは、ソースワークスペースとオブジェクトに関するメタデータを、レコードレベルのイベントと組み合わせたものです。
|
||||
|
||||
```ts
|
||||
import type {
|
||||
DatabaseEventPayload,
|
||||
ObjectRecordCreateEvent,
|
||||
ObjectRecordDestroyEvent,
|
||||
ObjectRecordUpdateEvent,
|
||||
} from 'twenty-sdk/logic-function';
|
||||
|
||||
type Person = {
|
||||
id: string;
|
||||
emails?: { primaryEmail?: string };
|
||||
};
|
||||
```
|
||||
|
||||
ペイロードには次のものが含まれます:
|
||||
|
||||
| プロパティ | 説明 |
|
||||
| ------------------------------------------------ | ------------------------------------------------------------------- |
|
||||
| `name` | `person.updated` などのイベント名。 |
|
||||
| `workspaceId` | イベントが発生したワークスペース。 |
|
||||
| `objectMetadata` | 変更されたオブジェクトのメタデータ。 |
|
||||
| `recordId` | 変更されたレコードの ID。 |
|
||||
| `userId`, `userWorkspaceId`, `workspaceMemberId` | イベントがワークスペースユーザーによって引き起こされた場合のアクターに関するフィールド。 |
|
||||
| `properties` | イベントのレコードデータ。操作に応じて `before`、`after`、`diff`、`updatedFields` が含まれます。 |
|
||||
|
||||
| イベント | レコードデータ |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `person.created` | `event.properties.after` |
|
||||
| `person.updated` | `event.properties.before`, `event.properties.after`, `event.properties.diff`, `event.properties.updatedFields` |
|
||||
| `person.destroyed` | `event.properties.before` |
|
||||
|
||||
ソフトデリートの場合、レコードの `deletedAt` フィールドが変化するため、`.deleted` はアップデート時と同じ形式になります。
|
||||
完全な削除の場合は `.destroyed` を使用します。
|
||||
|
||||
<Note>
|
||||
`databaseEventTriggerSettings.updatedFields` は、どの更新イベントで関数をトリガーするかをフィルタリングします。
|
||||
`event.properties.updatedFields` は、現在のイベントで実際にどのフィールドが変更されたかを示します。
|
||||
</Note>
|
||||
|
||||
作成イベントの例:
|
||||
|
||||
```ts
|
||||
type PersonCreatedEvent = DatabaseEventPayload<
|
||||
ObjectRecordCreateEvent<Person>
|
||||
>;
|
||||
|
||||
const handler = async (event: PersonCreatedEvent) => {
|
||||
const person = event.properties.after;
|
||||
|
||||
return {
|
||||
personId: event.recordId,
|
||||
email: person.emails?.primaryEmail,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
更新イベントの例:
|
||||
|
||||
```ts
|
||||
type PersonUpdatedEvent = DatabaseEventPayload<
|
||||
ObjectRecordUpdateEvent<Person>
|
||||
>;
|
||||
|
||||
const handler = async (event: PersonUpdatedEvent) => {
|
||||
const { before, after, diff, updatedFields } = event.properties;
|
||||
|
||||
return {
|
||||
personId: event.recordId,
|
||||
updatedFields,
|
||||
previousEmail: before.emails?.primaryEmail,
|
||||
currentEmail: after.emails?.primaryEmail,
|
||||
emailDiff: diff.emails,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
メール更新時のみトリガーする例:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
...,
|
||||
databaseEventTriggerSettings: {
|
||||
eventName: 'person.updated',
|
||||
updatedFields: ['emails'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
削除イベントの例:
|
||||
|
||||
```ts
|
||||
type PersonDestroyedEvent = DatabaseEventPayload<
|
||||
ObjectRecordDestroyEvent<Person>
|
||||
>;
|
||||
|
||||
const handler = async (event: PersonDestroyedEvent) => {
|
||||
const personBeforeDestroy = event.properties.before;
|
||||
|
||||
return {
|
||||
personId: event.recordId,
|
||||
email: personBeforeDestroy.emails?.primaryEmail,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
#### 関数を AI ツールまたはワークフロー アクションとして公開する
|
||||
|
||||
ロジック関数は 2 つのサーフェス上で公開でき、それぞれに固有のトリガーがあります:
|
||||
|
||||
* **`toolTriggerSettings`** — Twenty の AI 機能(チャット、MCP、関数呼び出し)からその関数を見つけられるようにします。 標準的な JSON Schema を使用します。これは LLM がネイティブに理解する形式です。
|
||||
* **`workflowActionTriggerSettings`** — ビジュアル ワークフロー ビルダー内のステップとして関数を表示します。 ビルダーが適切なフィールドエディタ、変数ピッカー、ラベルをレンダリングできるよう、Twenty の充実した `InputSchema` を使用します。
|
||||
|
||||
関数は一方、もう一方、またはその両方を選択できます。 これらは `cronTriggerSettings`、`databaseEventTriggerSettings`、`httpRouteTriggerSettings` と並列に存在します — 同じパターン、同じ形です。
|
||||
|
||||
```ts src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
__args: {
|
||||
data: {
|
||||
title: `Enrich data for ${params.companyName}`,
|
||||
body: `Domain: ${params.domain ?? 'unknown'}`,
|
||||
},
|
||||
},
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
return { taskId: result.createTask.id };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
||||
name: 'enrich-company',
|
||||
description: 'Enrich a company record with external data',
|
||||
timeoutSeconds: 10,
|
||||
handler,
|
||||
toolTriggerSettings: {},
|
||||
});
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
|
||||
* 関数はサーフェスを混在させることができます — `toolTriggerSettings` と `workflowActionTriggerSettings` の両方を宣言して、チャットおよびワークフロー ビルダーの両方に公開します。
|
||||
* `toolTriggerSettings.inputSchema` と `workflowActionTriggerSettings.inputSchema` はいずれも任意です。 省略された場合、マニフェストビルダーはハンドラーのソースコードからそれらを推論します(AI ツールには JSON Schema、ワークフロー アクションには Twenty の `InputSchema`)。 より豊富な型付けが必要な場合は、明示的に指定してください — たとえば、ワークフロー ビルダー向けに `CURRENCY` や `RELATION` といった `FieldMetadataType` に対応したフィールド、または AI エージェントが読み取れる `description` フィールドを使用する場合など:
|
||||
|
||||
```ts
|
||||
export default defineLogicFunction({
|
||||
...,
|
||||
toolTriggerSettings: {
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
companyName: {
|
||||
type: 'string',
|
||||
description: 'The name of the company to enrich',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
description: 'The company website domain (optional)',
|
||||
},
|
||||
},
|
||||
required: ['companyName'],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
パラメーターを**一度だけ**宣言して両方のサーフェスで利用できるようにするには、単一の JSON Schema(`InputJsonSchema`)を定義し、`twenty-sdk/logic-function` の `jsonSchemaToInputSchema` を使ってワークフローアクション用に変換します。 `toolTriggerSettings.inputSchema` は JSON Schema を直接受け取りますが、`workflowActionTriggerSettings.inputSchema` には Twenty の `InputSchema` が必要です。
|
||||
|
||||
```ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import { jsonSchemaToInputSchema, type InputJsonSchema } from 'twenty-sdk/logic-function';
|
||||
|
||||
const inputSchema: InputJsonSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
companyName: { type: 'string', label: 'Company name' },
|
||||
domain: { type: 'string', label: 'Domain' },
|
||||
},
|
||||
required: ['companyName'],
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
...,
|
||||
toolTriggerSettings: { inputSchema },
|
||||
workflowActionTriggerSettings: {
|
||||
label: 'Enrich Company',
|
||||
icon: 'IconBuilding',
|
||||
inputSchema: jsonSchemaToInputSchema(inputSchema),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
**良い `description` を記述してください。** AI エージェントは、ツールをいつ使用するかを判断するために関数の `description` フィールドに依存します。 ツールが何を行い、いつ呼び出すべきかを具体的に記述してください。
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
**ランタイムヘルパー。** `twenty-sdk/utils` は、小さなランタイムヘルパーを再エクスポートすることで、ハンドラーが直接 `twenty-shared` からインポートする必要がないようにします。 たとえば、`isDefined(value)` は `null` と `undefined` の両方に対して `false` を返します。これを使うと、オプショナルなハンドラー入力を安全に絞り込めます。こうした入力は、型としては `T | undefined` であっても、実行時には `null` として渡される場合があります。
|
||||
|
||||
```ts
|
||||
import { isDefined } from 'twenty-sdk/utils';
|
||||
|
||||
const handler = async (params: { parentMessageId?: string }) => {
|
||||
if (isDefined(params.parentMessageId)) {
|
||||
// params.parentMessageId is narrowed to string here
|
||||
}
|
||||
};
|
||||
```
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**インストールフック** — pre-install と post-install のハンドラー — はこのランタイムを共有しますが、それぞれ独自の define 関数で宣言され、トリガー設定は受け取りません。 `definePreInstallLogicFunction` と `definePostInstallLogicFunction` については、[インストールフック](/l/ja/developers/extend/apps/config/install-hooks) を参照してください。
|
||||
</Note>
|
||||
|
||||
## 型付き API クライアント(`twenty-client-sdk`)
|
||||
|
||||
`twenty-client-sdk` パッケージは、ロジック関数やフロントコンポーネントから Twenty API とやり取りするための、型付き GraphQL クライアントを 2 つ提供します。
|
||||
|
||||
| クライアント | インポート | エンドポイント | 自動生成? |
|
||||
| ------------------- | ---------------------------- | ------------------------------------ | ----------------- |
|
||||
| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — ワークスペースデータ(レコード、オブジェクト) | はい、開発/ビルド時 |
|
||||
| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — ワークスペース設定、ファイルアップロード | いいえ、あらかじめビルド済みで提供 |
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="CoreApiClient" description="ワークスペースデータ(レコード、オブジェクト)をクエリおよび変更">
|
||||
|
||||
`CoreApiClient` は、ワークスペースデータのクエリと変更のための主要なクライアントです。 `yarn twenty dev` または `yarn twenty dev:build` の実行時に**ワークスペースのスキーマから生成される**ため、オブジェクトやフィールドに一致する完全な型付けが行われます。
|
||||
|
||||
```ts
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
|
||||
const client = new CoreApiClient();
|
||||
|
||||
// Query records
|
||||
const { companies } = await client.query({
|
||||
companies: {
|
||||
edges: {
|
||||
node: {
|
||||
id: true,
|
||||
name: true,
|
||||
domainName: {
|
||||
primaryLinkLabel: true,
|
||||
primaryLinkUrl: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Create a record
|
||||
const { createCompany } = await client.mutation({
|
||||
createCompany: {
|
||||
__args: {
|
||||
data: {
|
||||
name: 'Acme Corp',
|
||||
},
|
||||
},
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
このクライアントは selection-set 構文を使用します。フィールドを含めるには `true` を渡し、引数には `__args` を使い、リレーションにはオブジェクトをネストします。 ワークスペースのスキーマに基づく完全な補完と型チェックが得られます。
|
||||
|
||||
<Note>
|
||||
**CoreApiClient は開発/ビルド時に生成されます。** 先に `yarn twenty dev` または `yarn twenty dev:build` を実行せずに使用すると、エラーが発生します。 生成は自動で行われます—CLI がワークスペースの GraphQL スキーマをイントロスペクトし、`@genql/cli` を使用して型付きクライアントを生成します。
|
||||
</Note>
|
||||
|
||||
#### CoreSchema を型注釈に使用する
|
||||
|
||||
`CoreSchema` は、ワークスペースのオブジェクトに対応する TypeScript の型を提供し、コンポーネントの状態や関数パラメーターの型付けに役立ちます:
|
||||
|
||||
```ts
|
||||
import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core';
|
||||
import { useState } from 'react';
|
||||
|
||||
const [company, setCompany] = useState<
|
||||
Pick<CoreSchema.Company, 'id' | 'name'> | undefined
|
||||
>(undefined);
|
||||
|
||||
const client = new CoreApiClient();
|
||||
const result = await client.query({
|
||||
company: {
|
||||
__args: { filter: { position: { eq: 1 } } },
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
setCompany(result.company);
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="MetadataApiClient" description="ワークスペースの設定、アプリケーション、ファイルアップロード">
|
||||
|
||||
`MetadataApiClient` は SDK に同梱されており、あらかじめビルド済みです(生成は不要)。 ワークスペースの設定、アプリケーション、ファイルアップロードのために `/metadata` エンドポイントに対してクエリを実行します。
|
||||
|
||||
```ts
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
|
||||
const metadataClient = new MetadataApiClient();
|
||||
|
||||
// List first 10 objects in the workspace
|
||||
const { objects } = await metadataClient.query({
|
||||
objects: {
|
||||
edges: {
|
||||
node: {
|
||||
id: true,
|
||||
nameSingular: true,
|
||||
namePlural: true,
|
||||
labelSingular: true,
|
||||
isCustom: true,
|
||||
},
|
||||
},
|
||||
__args: {
|
||||
filter: {},
|
||||
paging: { first: 10 },
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### ファイルのアップロード
|
||||
|
||||
`MetadataApiClient` には、ファイル型フィールドにファイルを添付するための `uploadFile` メソッドが含まれています:
|
||||
|
||||
```ts
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const metadataClient = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
const uploadedFile = await metadataClient.uploadFile(
|
||||
fileBuffer, // file contents as a Buffer
|
||||
'invoice.pdf', // filename
|
||||
'application/pdf', // MIME type
|
||||
'58a0a314-d7ea-4865-9850-7fb84e72f30b', // field universalIdentifier
|
||||
);
|
||||
|
||||
console.log(uploadedFile);
|
||||
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
|
||||
```
|
||||
|
||||
| パラメーター | タイプ | 説明 |
|
||||
| ---------------------------------- | -------- | ------------------------------------------------ |
|
||||
| `fileBuffer` | `Buffer` | ファイルの生データ |
|
||||
| `filename` | `string` | 保存および表示に使用されるファイル名 |
|
||||
| `contentType` | `string` | MIME タイプ(省略時は `application/octet-stream` がデフォルト) |
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | オブジェクト上のファイルタイプのフィールドの `universalIdentifier` |
|
||||
|
||||
主なポイント:
|
||||
* フィールドの `universalIdentifier`(ワークスペース固有の ID ではありません)を使用するため、アップロードコードはアプリがインストールされている任意のワークスペースで動作します。
|
||||
* 返される `url` は、アップロード済みファイルにアクセスするために使用できる署名付き URL です。
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
コードが Twenty 上で実行される際(ロジック関数やフロントコンポーネント)、プラットフォームは認証情報を環境変数として注入します:
|
||||
|
||||
* `TWENTY_API_URL` — Twenty API のベース URL
|
||||
* `TWENTY_APP_ACCESS_TOKEN` — アプリケーションのデフォルト関数ロールにスコープされた短命のキー
|
||||
|
||||
これらをクライアントに渡す必要はありません — 自動的に `process.env` から読み取ります。 API キーの権限は、`defineApplicationRole()` で宣言されたロール(または `application-config.ts` の `defaultRoleUniversalIdentifier` で参照されるロール)によって決まります。
|
||||
</Note>
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: 概要
|
||||
description: HTTP ルート、cron スケジュール、データベースイベント、AI ツール、またはワークフローアクションによってトリガーされ、Twenty 内部で実行されるサーバーサイドの TypeScript。
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
Twenty アプリの **ロジックレイヤー** は、*実行される* コードです。HTTP リクエスト、cron スケジュール、レコードの変更に反応するサーバーサイドの TypeScript ハンドラー、ワークスペース内で動作する AI スキルとエージェント、そして関数がサードパーティサービス上でユーザーに代わって動作できるようにする OAuth 接続が含まれます。
|
||||
|
||||
```text
|
||||
┌─ HTTP route ──┐
|
||||
│ Cron schedule │
|
||||
│ Database event │ ┌────────────────────┐
|
||||
triggers ─┤ AI tool call ├─────▶│ Logic function │
|
||||
│ Workflow action │ │ (your handler) │
|
||||
│ Manual exec │ └────────────────────┘
|
||||
└────────────────────┘ │
|
||||
▼
|
||||
┌────────────────────────────┐
|
||||
│ Twenty API (records) │
|
||||
│ Third-party API │
|
||||
│ (via Connection token) │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## このセクションについて
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="ロジック関数" icon="bolt" href="/l/ja/developers/extend/apps/logic/logic-functions">
|
||||
コアとなる構成要素 — トリガーの種類、ペイロード、および型付き API クライアント。
|
||||
</Card>
|
||||
<Card title="スキルとエージェント" icon="robot" href="/l/ja/developers/extend/apps/logic/skills-and-agents">
|
||||
再利用可能な AI エージェント向けの指示や、カスタムのシステムプロンプトを備えたアシスタント。
|
||||
</Card>
|
||||
<Card title="接続" icon="plug" href="/l/ja/developers/extend/apps/logic/connections">
|
||||
Linear、GitHub、Slack など、サードパーティサービス向けにアプリが保持する OAuth 資格情報。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## トリガータイプの概要
|
||||
|
||||
ロジック関数は 1 つ以上のトリガーを選択します。以下の各項目は、`defineLogicFunction()` 上の個別のフィールドです。
|
||||
|
||||
| トリガー | 実行タイミング | 設定 |
|
||||
| --------------- | --------------------------------------------------- | ------------------------------- |
|
||||
| **HTTP ルート** | リクエストが `/s/\<path>` エンドポイントに到達したとき | `httpRouteTriggerSettings` |
|
||||
| **クロン** | CRON 式が一致したとき | `cronTriggerSettings` |
|
||||
| **データベースイベント** | ワークスペースのレコードが作成、更新、または削除されたとき | `databaseEventTriggerSettings` |
|
||||
| **AI ツール** | Twenty の AI 機能が関数を呼び出すことを決定したとき | `toolTriggerSettings` |
|
||||
| **ワークフローアクション** | ワークフローステップが関数を呼び出したとき | `workflowActionTriggerSettings` |
|
||||
|
||||
関数は分離された Node.js プロセス内でサンドボックス実行され、[`defineApplication()`](/l/ja/developers/extend/apps/config/application) で宣言されたロールにスコープされた型付き API クライアントを通じてワークスペースにアクセスします。
|
||||
|
||||
<Note>
|
||||
**インストール時フック** — インストールの前後に実行されるコード — はこのランタイムを共有しますが、独自の define 関数を使用し、[Config → Install Hooks](/l/ja/developers/extend/apps/config/install-hooks) の下に配置されます。
|
||||
</Note>
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: スキルとエージェント
|
||||
description: アプリ向けの AI のスキルとエージェントを定義します。
|
||||
icon: robot
|
||||
---
|
||||
|
||||
<Warning>
|
||||
スキルとエージェントは現在アルファ版です。 この機能は動作しますが、まだ進化の途上です。
|
||||
</Warning>
|
||||
|
||||
アプリは、ワークスペース内で動作する AI 機能 — 再利用可能なスキルの指示やカスタムのシステムプロンプトを持つエージェントを定義できます。
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="defineSkill" description="AI エージェントのスキルを定義">
|
||||
|
||||
スキルは、ワークスペース内で AI エージェントが利用できる再利用可能な指示と機能を定義します。 組み込み検証付きでスキルを定義するには `defineSkill()` を使用します:
|
||||
|
||||
```ts src/skills/example-skill.ts
|
||||
import { defineSkill } from 'twenty-sdk/define';
|
||||
|
||||
export default defineSkill({
|
||||
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
name: 'sales-outreach',
|
||||
label: 'Sales Outreach',
|
||||
description: 'Guides the AI agent through a structured sales outreach process',
|
||||
icon: 'IconBrain',
|
||||
content: `You are a sales outreach assistant. When reaching out to a prospect:
|
||||
1. Research the company and recent news
|
||||
2. Identify the prospect's role and likely pain points
|
||||
3. Draft a personalized message referencing specific details
|
||||
4. Keep the tone professional but conversational`,
|
||||
});
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
* `name` はスキルの一意の識別子文字列です(kebab-case を推奨)。
|
||||
* `label` は、UI に表示される人間が読める表示名です。
|
||||
* `content` にはスキルの指示が含まれます — これは AI エージェントが使用するテキストです。
|
||||
* `icon`(任意)は、UI に表示されるアイコンを設定します。
|
||||
* `description`(任意)は、スキルの目的に関する追加のコンテキストを提供します。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="defineAgent" description="カスタムプロンプトで AI エージェントを定義">
|
||||
|
||||
エージェントは、ワークスペース内で動作する AI アシスタントです。 カスタムのシステムプロンプトでエージェントを作成するには `defineAgent()` を使用します:
|
||||
|
||||
```ts src/agents/example-agent.ts
|
||||
import { defineAgent } from 'twenty-sdk/define';
|
||||
|
||||
export default defineAgent({
|
||||
universalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
|
||||
name: 'sales-assistant',
|
||||
label: 'Sales Assistant',
|
||||
description: 'Helps the sales team draft outreach emails and research prospects',
|
||||
icon: 'IconRobot',
|
||||
prompt: 'You are a helpful sales assistant. Help users with their questions and tasks.',
|
||||
});
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
* `name` はエージェントの一意の識別子文字列です(kebab-case を推奨)。
|
||||
* `label` は UI に表示される表示名です。
|
||||
* `prompt` はエージェントの動作を定義するシステムプロンプトです。
|
||||
* `description`(任意)は、エージェントが何を行うかのコンテキストを提供します。
|
||||
* `icon`(任意)は、UI に表示されるアイコンを設定します。
|
||||
* `modelId`(任意)は、エージェントが使用するデフォルトの AI モデルを上書きします。
|
||||
* `responseFormat`(オプション)は、エージェントの出力の形式を制御します。 自由形式テキストの場合、デフォルトは `{ type: 'text' }` です。 構造化された JSON 出力を強制するには、`{ type: 'json', schema }` を使用します。
|
||||
|
||||
デフォルトでは、エージェントは自由形式テキストを返します。 構造化された出力を取得するには、`responseFormat` を `{ type: 'json' }` に設定し、`schema` を指定します:
|
||||
|
||||
```ts src/agents/structured-agent.ts
|
||||
import { defineAgent } from 'twenty-sdk/define';
|
||||
|
||||
export default defineAgent({
|
||||
universalIdentifier: 'c4d5e6f7-a8b9-0123-cdef-456789012345',
|
||||
name: 'lead-scorer',
|
||||
label: 'Lead Scorer',
|
||||
prompt: 'Score the lead and explain your reasoning.',
|
||||
responseFormat: {
|
||||
type: 'json',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
score: { type: 'number', description: 'Lead score from 0 to 100' },
|
||||
summary: { type: 'string', description: 'Short reasoning for the score' },
|
||||
},
|
||||
required: ['score', 'summary'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
スキーマに関する補足:
|
||||
* スキーマはフラットなオブジェクトであり、各プロパティの `type` はプリミティブ型(`string`、`number`、`boolean` のいずれか)でなければなりません。 ネストされたオブジェクトおよび配列はサポートされていません。
|
||||
* 各プロパティの `description`(オプション)は、そのプロパティに何を入れるべきかをモデルに示します。
|
||||
* `required`(オプション)は、モデルが常に返さなければならないプロパティを列挙します。
|
||||
* `additionalProperties: false`(オプション)は、`properties` に宣言されていないプロパティを禁止します。
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="runAgent" description="ロジック関数からエージェントを実行する">
|
||||
|
||||
`runAgent()` を使用すると、ロジック関数からアプリのエージェント(そのスキルやツールを含む)を実行できます。 `defineAgent()` に渡した `universalIdentifier` でエージェントを識別します:
|
||||
|
||||
```ts src/logic-functions/run-enricher.ts
|
||||
import { runAgent } from 'twenty-sdk/logic-function';
|
||||
|
||||
const { result, error, success } = await runAgent({
|
||||
agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123',
|
||||
prompt: 'Enrich House Ad <recordId>: fill empty fields from its listing URL.',
|
||||
});
|
||||
```
|
||||
|
||||
主なポイント:
|
||||
* エージェントは**同期的に**実行され、自身のツールを通じてレコードの読み取り/更新ができます。`runAgent()` は、実行が完了すると解決されます。
|
||||
* アプリは自分自身のエージェントのみを実行できます。
|
||||
* アプリの[デフォルトロール](/l/ja/developers/extend/apps/config/roles)には `AI` パーミッションフラグが付与されている必要があります — その `permissionFlagUniversalIdentifiers` に `SystemPermissionFlag.AI` を追加するか(または `canAccessAllTools: true` を設定します)。
|
||||
これがない場合、`runAgent()` はパーミッションエラーで失敗します。
|
||||
* ロジック関数には十分に長い `timeoutSeconds` を設定してください — エージェントの実行には数秒かかることがあります。
|
||||
* 実行が完了すると `success` は `true` になり、`result` は null 以外になります。失敗時には `success` は `false`、`result` は `null` で、`error` に理由が入ります(たとえば、実行の途中でワークスペースの AI クレジットが不足した場合など)。
|
||||
|
||||
```ts src/roles/default-role.ts
|
||||
import { defineApplicationRole, SystemPermissionFlag } from 'twenty-sdk/define';
|
||||
|
||||
export default defineApplicationRole({
|
||||
universalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061',
|
||||
label: 'Default function role',
|
||||
// runAgent() requires the AI permission flag on the app's default role.
|
||||
permissionFlagUniversalIdentifiers: [SystemPermissionFlag.AI],
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**ループを避けてください:** `*.updated` データベースイベントトリガーから `runAgent()` を呼び出し、そのエージェントが同じレコードを更新する場合、`updatedFields` を使ってエージェントが決して書き込まないフィールド(例: ソース URL)にスコープするか、`runAgent()` を呼び出す前に、対象フィールドのいずれかがまだ空であるかどうかを判定してガードしてください。
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: CLI
|
||||
description: 関数の実行、ログのストリーミング、アプリのインストール管理、リモートの切り替えを行う yarn twenty のコマンド。
|
||||
icon: terminal
|
||||
---
|
||||
|
||||
`dev`、`dev:build`、`dev:add`、`dev:typecheck` 以外にも、`yarn twenty` CLI には関数の実行、ログの表示、アプリのインストール管理のためのコマンドがあります。
|
||||
|
||||
## 関数の実行(`yarn twenty dev:function:exec`)
|
||||
|
||||
HTTP、cron、データベースイベントを介さずに、ロジック関数を手動で実行します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Execute by function name
|
||||
yarn twenty dev:function:exec -n create-new-post-card
|
||||
|
||||
# Execute by universalIdentifier
|
||||
yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
|
||||
# Pass a JSON payload
|
||||
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
|
||||
|
||||
# Execute the post-install function
|
||||
yarn twenty dev:function:exec --postInstall
|
||||
```
|
||||
|
||||
## 関数ログの表示(`yarn twenty dev:function:logs`)
|
||||
|
||||
アプリのロジック関数の実行ログをストリーミング表示します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Stream all function logs
|
||||
yarn twenty dev:function:logs
|
||||
|
||||
# Filter by function name
|
||||
yarn twenty dev:function:logs -n create-new-post-card
|
||||
|
||||
# Filter by universalIdentifier
|
||||
yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
|
||||
```
|
||||
|
||||
<Note>
|
||||
これは Docker コンテナのログを表示する `yarn twenty docker:logs` とは異なります。 `yarn twenty dev:function:logs` は、Twenty サーバーからアプリの関数実行ログを表示します。
|
||||
</Note>
|
||||
|
||||
## 型付きクライアントを生成する(`yarn twenty dev:generate-client`)
|
||||
|
||||
アプリをビルドしたり同期したりすることなく、アクティブなリモートのスキーマから型付き API クライアント(`twenty-client-sdk`)を再生成します。 これを使用すると、別リポジトリにあるバックエンドサービスのような、Twenty インスタンスと通信する任意のプロジェクトで型付きクライアントを取得できます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# In your project (no Twenty app definition required)
|
||||
yarn add twenty-sdk twenty-client-sdk
|
||||
|
||||
# Connect to the Twenty instance to generate the client from
|
||||
yarn twenty remote:add
|
||||
|
||||
# Generate the typed client into node_modules/twenty-client-sdk
|
||||
yarn twenty dev:generate-client
|
||||
```
|
||||
|
||||
次に、クライアントをコードでインポートします:
|
||||
|
||||
```typescript
|
||||
import { CoreApiClient } from 'twenty-client-sdk/core';
|
||||
```
|
||||
|
||||
データモデルが変更されるたびにコマンドを再実行して、生成された型を更新してください。
|
||||
|
||||
<Note>
|
||||
クライアントは `node_modules` 内に生成されるため、コードと一緒にはコミットされません。 インストールのたびに(たとえば `postinstall` スクリプトや CI で)`yarn twenty dev:generate-client` を実行してください。
|
||||
</Note>
|
||||
|
||||
## アプリのアンインストール(`yarn twenty app:uninstall`)
|
||||
|
||||
アクティブなワークスペースからアプリを削除します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty app:uninstall
|
||||
|
||||
# Skip the confirmation prompt
|
||||
yarn twenty app:uninstall --yes
|
||||
```
|
||||
|
||||
## リモートの管理
|
||||
|
||||
**remote** とは、アプリが接続する Twenty サーバーのことです。 セットアップ中に、スキャフォルダーが自動的にリモートを作成します。 リモートはいつでも追加や切り替えができます。
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new remote (opens a browser for OAuth login)
|
||||
yarn twenty remote:add
|
||||
|
||||
# Connect to a local Twenty server (auto-detects port 2020 or 3000)
|
||||
yarn twenty remote:add --local
|
||||
|
||||
# Add a remote non-interactively (useful for CI)
|
||||
yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote
|
||||
|
||||
# List all configured remotes
|
||||
yarn twenty remote:list
|
||||
|
||||
# Set the active remote
|
||||
yarn twenty remote:use <name>
|
||||
```
|
||||
|
||||
認証情報は `~/.twenty/config.json` に保存されます。
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: 概要
|
||||
description: アプリのビルド、テスト、リリース — CLI コマンド、統合テスト、CI、サーバーまたは npm への公開。
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
**オペレーションレイヤー** とは、アプリを使って *何かをする* のではなく、アプリに対して *行う* あらゆる操作を指します。具体的には、CLI コマンドの実行、実際の Twenty サーバーに対する統合テストの実行、CI の設定、そしてリリースの提供です — 単一サーバーにデプロイする tarball として、またはマーケットプレイスに掲載される npm パッケージとして行われます。
|
||||
|
||||
```text
|
||||
develop ─▶ test ─▶ build ─▶ deploy / publish
|
||||
─────── ──── ───── ─────────────────
|
||||
yarn yarn yarn yarn twenty app:publish --private (tarball → one server)
|
||||
twenty test twenty
|
||||
dev dev:build yarn twenty app:publish (npm → marketplace)
|
||||
```
|
||||
|
||||
## このセクションについて
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="CLI" icon="terminal" href="/l/ja/developers/extend/apps/operations/cli">
|
||||
`yarn twenty` リファレンス — exec、logs、uninstall、remotes。
|
||||
</Card>
|
||||
<Card title="同期と復旧" icon="compass" href="/l/ja/developers/extend/apps/operations/sync-and-recovery">
|
||||
どのコマンドをいつ使うか、同期の差分の読み方、そして復旧の手順。
|
||||
</Card>
|
||||
<Card title="テスト" icon="flask" href="/l/ja/developers/extend/apps/operations/testing">
|
||||
Vitest のセットアップ、統合テスト、型チェック、CI ワークフロー。
|
||||
</Card>
|
||||
<Card title="公開" icon="upload" href="/l/ja/developers/extend/apps/operations/publishing">
|
||||
ビルド、tarball のデプロイ、npm への公開、インストール。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,294 @@
|
||||
---
|
||||
title: 公開
|
||||
icon: upload
|
||||
description: Twenty アプリをマーケットプレイスに公開するか、社内向けにデプロイします。
|
||||
---
|
||||
|
||||
## 概要
|
||||
|
||||
[ローカルでビルドとテストが完了](/l/ja/developers/extend/apps/getting-started/concepts)したら、配布方法は次の2通りです:
|
||||
|
||||
* **tarball をデプロイ** — 内部またはプライベート用途のために、特定の Twenty サーバーにアプリを直接アップロードします。
|
||||
* **npm に公開** — Twenty マーケットプレイスにアプリを掲載し、任意のワークスペースが見つけてインストールできるようにします。
|
||||
|
||||
どちらの方法も同じ**ビルド**手順から始まります。
|
||||
|
||||
## アプリのビルド
|
||||
|
||||
build コマンドを実行してアプリをコンパイルし、配布可能な `manifest.json` を生成します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:build
|
||||
```
|
||||
|
||||
これにより、TypeScript ソースがコンパイルされ、ロジック関数とフロントエンドコンポーネントがトランスパイルされ、すべてが `.twenty/output/` に書き込まれます。 手動配布または公開コマンド用の `.tgz` パッケージも生成するには、`--tarball` を追加します。
|
||||
|
||||
## サーバーへのデプロイ(tarball)
|
||||
|
||||
一般公開したくないアプリ(プロプライエタリなツール、エンタープライズ専用の連携、実験的ビルドなど)の場合は、tarball を Twenty サーバーに直接デプロイできます。
|
||||
|
||||
### 前提条件
|
||||
|
||||
デプロイ前に、対象サーバーを指すリモートを設定しておく必要があります。 リモートは、サーバーの URL と認証情報をローカルの `~/.twenty/config.json` に保存します。
|
||||
|
||||
リモートを追加:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty remote:add --url https://your-twenty-server.com --as production
|
||||
```
|
||||
|
||||
### デプロイ
|
||||
|
||||
アプリをビルドしてサーバーにアップロードするまでを1ステップで実行します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty app:publish --private
|
||||
# To deploy to a specific remote:
|
||||
# yarn twenty app:publish --private --remote production
|
||||
```
|
||||
|
||||
### デプロイ済みアプリの共有
|
||||
|
||||
<Warning>
|
||||
プライベート(tarball)アプリをワークスペース間で共有することは、**Enterprise** の機能です。 ワークスペースに有効な Enterprise キーがあるまで、**Distribution** タブは共有コントロールの代わりにアップグレードのプロンプトを表示します。 有効にするには、[設定 > 管理パネル > Enterprise](/settings/admin-panel#enterprise) に移動します。
|
||||
</Warning>
|
||||
|
||||
tarball アプリは公開マーケットプレイスには掲載されないため、同じサーバー上の他のワークスペースは、ブラウズしてもそれらを見つけられません。 ワークスペースが Enterprise プランの場合、デプロイ済みのアプリを次のように共有できます:
|
||||
|
||||
1. **設定 > アプリケーション > 登録** に移動して、あなたのアプリを開きます
|
||||
2. **Distribution** タブで、**Copy share link** をクリックします
|
||||
3. このリンクを他のワークスペースのユーザーと共有すると、アプリのインストールページに直接移動できます。
|
||||
|
||||
共有リンクはサーバーのベース URL(ワークスペースのサブドメインなし)を使用するため、サーバー上のどのワークスペースでも機能します。
|
||||
|
||||
### バージョン管理
|
||||
|
||||
既にデプロイ済みの tarball アプリを更新する場合、サーバーは、`package.json` の `version` が、現在デプロイされているバージョンよりも **厳密に大きい** ([semver](https://semver.org) の順序に従って) ことを要求します。 同じバージョンを再デプロイする、またはそれより低いバージョンをプッシュする操作は、tarball が保存される前に拒否されます—CLI から `VERSION_ALREADY_EXISTS` エラーが表示されます。
|
||||
|
||||
アップデートをリリースするには:
|
||||
|
||||
1. `package.json` の `version` フィールドを更新してください(例:`1.2.3` → `1.2.4`、`1.3.0`、または `2.0.0`)
|
||||
2. `yarn twenty app:publish --private`(または `yarn twenty app:publish --private --remote production`)を実行します。
|
||||
3. アプリをインストールしているワークスペースの設定に、利用可能なアップグレードが表示されます。
|
||||
|
||||
<Note>
|
||||
プレリリースタグは期待どおりに動作します。`1.0.0-rc.1` → `1.0.0-rc.2` への引き上げは許可され、`1.0.0` のような最終リリースは `1.0.0-rc.5` よりも高いバージョンとして正しく認識されます。 `package.json` のバージョンは、それ自体が有効な semver 文字列でなければなりません。
|
||||
</Note>
|
||||
|
||||
{/* TODO: add screenshot of the Upgrade button */}
|
||||
|
||||
### サーバーバージョンの互換性
|
||||
|
||||
特定の Twenty サーバーバージョンで導入された機能(例: v2.3.0 で追加された OAuth プロバイダー)をアプリで使用している場合、`package.json` の `engines.twenty` フィールドを使用して、アプリが必要とする最小サーバーバージョンを宣言してください:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"name": "twenty-my-app",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
"twenty": ">=2.3.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
値は標準的な [SemVer の範囲](https://github.com/npm/node-semver#ranges) です。 よくあるパターン:
|
||||
|
||||
| 範囲 | 意味 |
|
||||
| ---------------------------------- | -------------------------------------- |
|
||||
| `>=2.3.0` | 2.3.0 以降のどのサーバーでも |
|
||||
| `>=2.3.0 \<3.0.0` | 2.3.0 以上(次のメジャーバージョン未満) |
|
||||
| `^2.3.0` | `>=2.3.0 \<3.0.0` と同じ |
|
||||
|
||||
**デプロイ時とインストール時に起こること:**
|
||||
|
||||
* `engines.twenty` が設定されていて、ターゲットサーバーのバージョンがその範囲を満たさない場合、デプロイ(tarball のアップロード)またはインストールは `SERVER_VERSION_INCOMPATIBLE` エラーと、要求される範囲と実際のサーバーバージョンの双方を示すメッセージとともに拒否されます。
|
||||
* `engines.twenty` が**未設定**の場合、アプリはどのサーバーバージョンでも受け入れられます(既存のアプリとの後方互換性)。
|
||||
* サーバーで `APP_VERSION` が設定されていない場合、このチェックはスキップされます。
|
||||
|
||||
<Note>
|
||||
サーバーが最終的な判定を行い、tarball のアップロードとワークスペースへのインストールの両方で `engines.twenty` を検証します。 tarball を別経路でデプロイする場合やマーケットプレイスからインストールする場合でも、サーバーは互換性を引き続き強制します。
|
||||
</Note>
|
||||
|
||||
## 自動化された CI/CD(スキャフォールド済みのワークフロー)
|
||||
|
||||
`create-twenty-app` で生成されたアプリには、`.github/workflows/` 配下に GitHub Actions のワークフローが 2 つ、最初から同梱されています。 リポジトリを GitHub にプッシュするとすぐに実行可能です。CI に追加のセットアップは不要で、CD にはシークレットを 1 つ用意するだけです。
|
||||
|
||||
### CI — `ci.yml`
|
||||
|
||||
`main` へのプッシュやプルリクエストのたびに、統合テストを自動実行します。
|
||||
|
||||
**機能:**
|
||||
|
||||
1. アプリのソースをチェックアウトします。
|
||||
2. `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` 複合アクションを使って、分離された Twenty のテスト用インスタンスを起動します(`yarn twenty docker:start --test` の CI 版)。
|
||||
3. Corepack を有効化し、`.nvmrc` に基づいて Node.js をセットアップし、`yarn install --immutable` で依存関係をインストールします。
|
||||
4. `yarn test` を実行し、起動したインスタンスから `TWENTY_API_URL` と `TWENTY_API_KEY` を渡すことで、テストが実サーバーと通信できるようにします。
|
||||
|
||||
**設定項目:**
|
||||
|
||||
* `TWENTY_VERSION`(env、デフォルトは `latest`)— `ci.yml` でこれを編集して、CI で使用する Twenty サーバーのバージョンを固定します。
|
||||
* 同時実行は `github.ref` 単位でグループ化され、新しいプッシュ時に進行中の実行をキャンセルします。
|
||||
|
||||
シークレットは不要です。テスト用インスタンスは一時的で、ジョブの実行中のみ存続します。
|
||||
|
||||
### CD — `cd.yml`
|
||||
|
||||
`main` へのプッシュのたびに、設定済みの Twenty サーバーへアプリをデプロイします。また、`deploy` ラベルが付与されたプルリクエストからのデプロイにも対応します(任意)。
|
||||
|
||||
**機能:**
|
||||
|
||||
1. (ラベル付き PR の場合は)PR のヘッド、またはプッシュされたコミットをチェックアウトします。
|
||||
2. `twentyhq/twenty/.github/actions/deploy-twenty-app@main` を実行します(`yarn twenty app:publish --private` の CI 版)。
|
||||
3. `twentyhq/twenty/.github/actions/install-twenty-app@main` を実行し、新しくデプロイしたバージョンを対象ワークスペースにインストールします。
|
||||
|
||||
**必須の設定:**
|
||||
|
||||
| 設定 | 場所 | 目的 |
|
||||
| ----------------------- | ------------------------------------------------------------ | -------------------------------------------------- |
|
||||
| `TWENTY_DEPLOY_URL` | `cd.yml` の `env`(デフォルトは `http://localhost:3000`) | デプロイ先の Twenty サーバー。 初回利用前に、実際のサーバーの URL に変更してください。 |
|
||||
| `TWENTY_DEPLOY_API_KEY` | GitHub リポジトリの **Settings → Secrets and variables → Actions** | 対象サーバーでデプロイ権限を持つ API キー。 |
|
||||
|
||||
<Note>
|
||||
デフォルトの `TWENTY_DEPLOY_URL`(`http://localhost:3000`)はプレースホルダーであり、GitHub ホストランナーからは到達できません。 CD を有効化する前に、サーバーの公開 URL に更新するか、ネットワークアクセス可能なセルフホストランナーを使用してください。
|
||||
</Note>
|
||||
|
||||
**PR からプレビューデプロイをトリガーする:**
|
||||
|
||||
プルリクエストに `deploy` ラベルを追加します。 `cd.yml` の `if:` ガードにより、その PR のヘッドコミットを使ってジョブが実行され、マージ前に対象サーバー上で変更を検証できます。
|
||||
|
||||
### 再利用可能なアクションのバージョン固定
|
||||
|
||||
両方のワークフローは再利用可能なアクションを `@main` で参照しているため、`twentyhq/twenty` リポジトリでのアクション更新が自動的に取り込まれます。 ビルドを決定的にしたい場合は、各 `uses:` 行の `@main` をコミット SHA またはリリースタグに置き換えてください。
|
||||
|
||||
## npm への公開
|
||||
|
||||
npm に公開すると、Twenty マーケットプレイスでアプリが見つけられるようになります。 任意の Twenty ワークスペースは、UI からマーケットプレイスのアプリを参照、インストール、およびアップグレードできます。
|
||||
|
||||
### 要件
|
||||
|
||||
* [npm](https://www.npmjs.com) アカウント
|
||||
* `package.json` の `keywords` 配列にある `twenty-app` キーワード(手動で追加してください — `create-twenty-app` テンプレートにはデフォルトで含まれていません)
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"name": "twenty-app-postcard-sender",
|
||||
"version": "1.0.0",
|
||||
"keywords": ["twenty-app"]
|
||||
}
|
||||
```
|
||||
|
||||
### マーケットプレイスのメタデータ
|
||||
|
||||
`defineApplication()` の設定は、マーケットプレイスでのアプリの表示方法を制御する任意のフィールドをサポートします。 `public/` フォルダー内の画像を参照するには、`logoUrl` と `screenshots` を使用します:
|
||||
|
||||
```ts src/application-config.ts
|
||||
export default defineApplication({
|
||||
universalIdentifier: '...',
|
||||
displayName: 'My App',
|
||||
description: 'A great app',
|
||||
logoUrl: 'public/logo.png',
|
||||
screenshots: [
|
||||
'public/screenshot-1.png',
|
||||
'public/screenshot-2.png',
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
マーケットプレイスのフィールド(`author`、`category`、`aboutDescription`、`websiteUrl`、`termsUrl` など)の完全な一覧は、Building Apps ページの[defineApplication アコーディオン](/l/ja/developers/extend/apps/config/application#marketplace-metadata)を参照してください。
|
||||
|
||||
#### 推奨されるスクリーンショットのサイズ
|
||||
|
||||
マーケットプレイスでは、`screenshots` は固定の `8:5` コンテナ内で表示されます(例:`1600×1000 px`)。
|
||||
|
||||
<Note>
|
||||
どのようなアスペクト比でもスクリーンショットは全体が表示され、トリミングされることはありません。ただし、`8:5` よりも著しく縦長または横長の場合は、上下または左右に空白の帯が表示されます。
|
||||
</Note>
|
||||
|
||||
### 公開
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty app:publish
|
||||
```
|
||||
|
||||
特定の dist-tag(例: `beta` や `next`)で公開するには:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty app:publish --tag beta
|
||||
```
|
||||
|
||||
### マーケットプレイスの検出の仕組み
|
||||
|
||||
Twenty サーバーは、npm レジストリからマーケットプレイスのカタログを**毎時**同期します。
|
||||
|
||||
待たずにすぐに同期をトリガーできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:catalog-sync
|
||||
# To target a specific remote:
|
||||
# yarn twenty dev:catalog-sync --remote production
|
||||
```
|
||||
|
||||
マーケットプレイスに表示されるメタデータは、`defineApplication()` の設定に由来します。`displayName`、`description`、`author`、`category`、`logoUrl`、`screenshots`、`aboutDescription`、`websiteUrl`、`termsUrl` などのフィールドです。
|
||||
|
||||
<Note>
|
||||
アプリで`defineApplication()`内に`aboutDescription`が定義されていない場合、マーケットプレイスはnpm上のパッケージの`README.md`を概要ページのコンテンツとして自動的に使用します。 つまり、npm と Twenty のマーケットプレイスの両方に対して、1 つの README を維持できます。 マーケットプレイスで異なる説明文を使用したい場合は、`aboutDescription` を明示的に設定してください。
|
||||
</Note>
|
||||
|
||||
### CI での公開
|
||||
|
||||
この GitHub Actions のワークフローを使用すると、各リリース時に自動で公開できます([OIDC](https://docs.npmjs.com/trusted-publishers) を使用)。
|
||||
|
||||
```yaml filename=".github/workflows/publish.yml"
|
||||
name: Publish
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
registry-url: https://registry.npmjs.org
|
||||
- run: yarn install --immutable
|
||||
- run: npx twenty dev:build
|
||||
- run: npm publish --provenance --access public
|
||||
working-directory: .twenty/output
|
||||
```
|
||||
|
||||
他の CI システム(GitLab CI、CircleCI など)でも同じ3つのコマンドを使用します: `yarn install`、`yarn twenty dev:build`、そして `.twenty/output` から `npm publish` を実行します。
|
||||
|
||||
<Note>
|
||||
**npm provenance** は任意ですが、推奨されます。 `--provenance` を付けて公開すると、npm のリスティングにトラストバッジが追加され、公開 CI パイプラインの特定のコミットからビルドされたことをユーザーが検証できるようになります。 セットアップ手順は、[npm provenance のドキュメント](https://docs.npmjs.com/generating-provenance-statements)を参照してください。
|
||||
</Note>
|
||||
|
||||
## アプリのインストール
|
||||
|
||||
アプリが公開(npm)またはデプロイ(tarball)されると、ワークスペースは UI からインストールできます。
|
||||
|
||||
Twenty の **設定 > アプリケーション** ページに移動すると、マーケットプレイスと tarball でデプロイされたアプリの両方を参照してインストールできます。
|
||||
|
||||
{/* TODO: add screenshot of the UI when the app is registered */}
|
||||
|
||||
コマンドラインからアプリをインストールすることもできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty app:install
|
||||
```
|
||||
|
||||
<Note>
|
||||
サーバーはインストール時に semver によるバージョニングを強制し、デプロイ時の規則を反映しています:
|
||||
|
||||
* ワークスペースに既にインストールされているものと同じバージョンをインストールしようとすると、`APP_ALREADY_INSTALLED` エラーで拒否されます。
|
||||
* 現在インストールされているものより低いバージョンをインストールしようとすると、`CANNOT_DOWNGRADE_APPLICATION` エラーで拒否されます。
|
||||
|
||||
より新しいバージョンをインストールするには、まずデプロイまたは公開してから、`yarn twenty app:install` を再実行してください。
|
||||
</Note>
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: 同期と復旧
|
||||
description: どのコマンドをいつ使うか、同期の出力の読み方、そしてローカルのメタデータがずれたときに完全リセットへ到達する前に行うべき復旧手順の段階的な手引きについて説明します。
|
||||
icon: compass
|
||||
---
|
||||
|
||||
ローカルでのアプリ開発は**同期**を中心に回ります。CLI がマニフェストを再ビルドし、サーバーはそれと、すでにワークスペースに存在するメタデータとの差分だけを適用します。 このページでは、どのコマンドを使うべきか、同期で何が変更されたのかをどう読み取るか、そしてローカルの状態が一貫していないように見えるときに取るべき手順を(順番に)説明します。
|
||||
|
||||
## どのコマンドをいつ使うか
|
||||
|
||||
<Note>
|
||||
日々のローカルでの反復開発では、ほとんどの場合 `yarn twenty dev` を使うことになります。 デプロイと公開はリリースのためのものであり、ローカルでの開発ループのためのもの**ではありません**。
|
||||
</Note>
|
||||
|
||||
| やりたいこと… | コマンド | ノート |
|
||||
| ------------------------------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| ライブ同期でローカルに反復開発する | `yarn twenty dev` | ファイルを監視し、変更のたびに同期します。 |
|
||||
| 1 回だけ同期して終了(CI、スクリプト、フック向け) | `yarn twenty dev --once` | 1 回ビルドして同期し、その後終了します。 |
|
||||
| 変更を**適用せずに**プレビュー | `yarn twenty dev --once --dry-run` | 差分を計算して表示しますが、何も書き込みません。 |
|
||||
| ワークスペースからアプリを削除する | `yarn twenty app:uninstall` | プロンプトをスキップするには、`--yes` を追加します。 |
|
||||
| サーバーに tarball をアップロードする | `yarn twenty app:publish --private` | `package.json` のバージョンが**厳密により高い**必要があります — [Publishing](/l/ja/developers/extend/apps/operations/publishing) を参照してください。 |
|
||||
| マーケットプレイス(npm)に公開する | `yarn twenty app:publish` | — |
|
||||
| デプロイ済みバージョンをインストール / アップグレードする | `yarn twenty app:install` | 現在デプロイされているバージョンをインストールします。 |
|
||||
| ローカルサーバーを消去してクリーンに開始する | `yarn twenty docker:reset` | ローカルデータを**すべて**削除します — 最終手段です。 |
|
||||
|
||||
### ローカル同期ではバージョンの更新は不要
|
||||
|
||||
厳密に増加する `version` のルール(デプロイ時の `VERSION_ALREADY_EXISTS`、インストール時の `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION`)は、リリースパスである **`app:publish` / `app:install`** に適用されます。 `yarn twenty dev` はマニフェストをその場で同期し、バージョン変更を要求することはないため、反復するのに `package.json` を触る必要はありません。 ローカルの変更をテストするためにバージョンを上げている場合は、開発ループが必要なところでリリース経路を使ってしまっています。
|
||||
|
||||
## 同期出力の読み方
|
||||
|
||||
各同期では、適用された(`--dry-run` の場合は適用されるはずだった)メタデータ変更が出力されます。
|
||||
|
||||
```text filename="Terminal"
|
||||
Metadata changes: 2 created, 1 updated, 1 deleted
|
||||
created objectMetadata rocket
|
||||
created fieldMetadata timelineActivities
|
||||
updated fieldMetadata launchedAt
|
||||
deleted pageLayout legacyTab
|
||||
✓ Synced
|
||||
```
|
||||
|
||||
これは最初の診断手段です。どのオブジェクト、フィールド、レイアウトが変更されたかを正確に示すので、UI を確認する前に、同期が想定どおりに動作したかを確認できます。
|
||||
|
||||
同期が単一のエンティティで失敗した場合、エラーには問題のエンティティとその `universalIdentifier` が、次のように示されます。
|
||||
|
||||
```text
|
||||
Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed
|
||||
```
|
||||
|
||||
その識別子を使って、推測で衝突元を探すのではなく、マニフェスト内(必要であればワークスペース内)のエンティティを特定してください。
|
||||
|
||||
## 変更内容のプレビュー(ドライラン)
|
||||
|
||||
`yarn twenty dev --once --dry-run` はマニフェストをビルドし、サーバーにマイグレーションプランを問い合わせ、その内容を**何も適用せずに**表示します。 コミットする前に「この同期は何を変更するか?」という問いに安全に答える方法です。
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev --once --dry-run
|
||||
```
|
||||
|
||||
```text filename="Terminal"
|
||||
Building manifest...
|
||||
Computing metadata diff (dry run, nothing will be applied)...
|
||||
Metadata changes: 1 created, 1 updated
|
||||
created fieldMetadata timelineActivities
|
||||
updated objectMetadata rocket
|
||||
✓ Dry run complete for My App — no changes were applied
|
||||
```
|
||||
|
||||
ドライランでは次のことが行われます。
|
||||
|
||||
* **何も書き込みません** — メタデータマイグレーション、アプリケーションレコードの更新、デフォルトのロール / タブの変更、API クライアントの生成は一切行いません。
|
||||
* 実際の同期が適用するのと**同じ差分**を返すため、作成 / 更新 / 削除されるエンティティを事前に確認できます。
|
||||
* リスクの高い変更の前や、AI 生成の変更をレビューするとき、または予期せぬ変更が行われそうな場合にスクリプトを失敗させたいときなどに有用です。
|
||||
|
||||
<Note>
|
||||
ドライランでは**メタデータ**の変更のみをプレビューします。また、アプリが少なくとも一度は同期されている(ワークスペース側がその存在を知っている)必要があります。 一度も同期されていないアプリに対して実行すると、サーバーはそのアプリがインストールされていないと報告します — まず一度 `yarn twenty dev` を実行してください。
|
||||
</Note>
|
||||
|
||||
## リカバリーラダー
|
||||
|
||||
ローカルのメタデータが正しくないように見える場合は、次の順番でエスカレートし、問題が解消したところで止めてください。 各ステップは前のものよりも影響が大きくなります。
|
||||
|
||||
1. **再同期。** `yarn twenty dev --once` を再度実行します。 同期はべき等であり、クリーンなマニフェストを再実行しても安全で、多くの場合は一時的な不具合が解消されます。
|
||||
2. **プランをプレビュー。** `yarn twenty dev --once --dry-run` を実行して、次の同期が何を変更しようとしているのかを、適用せずに正確に確認します。
|
||||
3. **名前付きエラーを読む。** 同期が失敗した場合は、メッセージ内のメタデータタイプと `universalIdentifier`(上記参照)を確認し、そのエンティティをマニフェスト内で特定します。 コンフリクトは、重複または再利用された識別子を指していることがほとんどです。
|
||||
4. **アンインストールして再インストール。** `yarn twenty app:uninstall` を実行し、その後再度同期します(`yarn twenty dev`)。 これにより、ワークスペースの残りを維持したまま、アプリのメタデータをクリーンな状態から再構築します。
|
||||
5. **フルリセット(最後の手段)。** `yarn twenty docker:reset` を実行し、その後再シードと再同期を行います。
|
||||
|
||||
<Warning>
|
||||
`yarn twenty docker:reset` はローカルインスタンス内のデータを**すべて**削除します — すべてのワークスペース、レコード、アプリが消去されます。 それまでの手順がすべて失敗した場合にのみ使用してください。
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
メタデータエラーが発生しましたか? [issue を作成](https://github.com/twentyhq/twenty/issues/new/choose)し、失敗したマイグレーションメッセージ(メタデータタイプと `universalIdentifier` を含む)、同期時の `Metadata changes` の出力、および実行したコマンドを記載してください。
|
||||
</Note>
|
||||
|
||||
## 1 つのワークスペースでの同時同期を避ける
|
||||
|
||||
同期ではメタデータマイグレーションが適用されます。 **同じワークスペースに対して同時に**複数の同期、デプロイ、インストール操作を実行すると(たとえば、複数のターミナルや AI エージェントが並行して反復している場合)、それらのマイグレーションが入り混じり、メタデータが一部だけ適用された状態のままになってしまう可能性があります。
|
||||
|
||||
サーバーはワークスペースごとに同期を直列化してこれを防いでいますが、それでも、センシティブなメタデータ操作は同時に実行するのではなく、**単一の**プロセスに集約するべきです。 複数のエージェントで開発をオーケストレーションしている場合は、それらの sync / deploy / install 呼び出しを 1 つのキューに流し込み、同時ではなく一度に 1 つだけ実行されるようにしてください。
|
||||
|
||||
## 失敗の切り分け
|
||||
|
||||
問題が発生したときは、メタデータの差分と名前付きエラーによって、どこで失敗したかを特定できます。
|
||||
|
||||
* **マニフェストビルドエラー** — CLI が同期前に失敗します(`MANIFEST_BUILD_FAILED`、`TYPECHECK_FAILED`)。アプリのソースを修正してください。
|
||||
* **同期 / マイグレーションエラー** — ビルドは成功しますが、差分の適用が失敗し、エンティティと `universalIdentifier` が示されます。そのコンフリクトしているメタデータを修正してください。
|
||||
* **アプリコードの実行時エラー** — 同期自体は成功しているものの、ロジック関数やコンポーネントが実行時に正しく動作しません。[function logs](/l/ja/developers/extend/apps/operations/cli) を確認してください。
|
||||
* **ローカルインスタンスの状態** — 上記のいずれにも当てはまらないのにワークスペースの見た目が依然としておかしい場合は、リカバリーラダーに従って下の段階へ進んでください。
|
||||
@@ -0,0 +1,301 @@
|
||||
---
|
||||
title: テスト
|
||||
description: Vitest のセットアップ、実際の Twenty サーバーに対する統合テスト、型チェック、および GitHub Actions を使った CI。
|
||||
icon: flask
|
||||
---
|
||||
|
||||
SDK は、テストコードからアプリのビルド、デプロイ、インストール、アンインストールを可能にするプログラム用 API を提供します。 [Vitest](https://vitest.dev/) と型付き API クライアントを組み合わせることで、実際の Twenty サーバーに対してエンドツーエンドで動作を検証する統合テストを作成できます。
|
||||
|
||||
## npm パッケージの使用
|
||||
|
||||
アプリで任意の npm パッケージをインストールして使用できます。 ロジック関数とフロントコンポーネントはどちらも [esbuild](https://esbuild.github.io/) でバンドルされ、依存関係はすべて出力にインライン化されます—実行時に `node_modules` は不要です。
|
||||
|
||||
### パッケージのインストール
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add axios
|
||||
```
|
||||
|
||||
次に、コードでインポートします:
|
||||
|
||||
```ts src/logic-functions/fetch-data.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||||
import axios from 'axios';
|
||||
|
||||
const handler = async (): Promise<any> => {
|
||||
const { data } = await axios.get('https://api.example.com/data');
|
||||
|
||||
return { data };
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: '...',
|
||||
name: 'fetch-data',
|
||||
description: 'Fetches data from an external API',
|
||||
timeoutSeconds: 10,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
フロントコンポーネントでも同様に機能します:
|
||||
|
||||
```tsx src/front-components/chart.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk/define';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
const DateWidget = () => {
|
||||
return <p>Today is {format(new Date(), 'MMMM do, yyyy')}</p>;
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '...',
|
||||
name: 'date-widget',
|
||||
component: DateWidget,
|
||||
});
|
||||
```
|
||||
|
||||
### バンドルの仕組み
|
||||
|
||||
ビルドステップでは esbuild を使用して、各ロジック関数および各フロントコンポーネントごとに自己完結した単一ファイルを生成します。 インポートされたパッケージはすべてバンドルにインライン化されます。
|
||||
|
||||
**ロジック関数**は Node.js 環境で実行されます。 Node の組み込みモジュール(`fs`、`path`、`crypto`、`http` など) は利用可能で、インストールは不要です。
|
||||
|
||||
**フロントコンポーネント**は Web Worker で実行されます。 Node の組み込みモジュールは利用できません—ブラウザー環境で動作するブラウザー API と npm パッケージのみが使用できます。
|
||||
|
||||
どちらの環境でも、`twenty-client-sdk/core` および `twenty-client-sdk/metadata` が事前提供モジュールとして利用可能です—これらはバンドルされず、実行時にサーバーによって解決されます。
|
||||
|
||||
## セットアップ
|
||||
|
||||
スキャフォルドされたアプリにはすでに Vitest が含まれています。 手動で設定する場合は、依存関係をインストールしてください:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add -D vitest vite-tsconfig-paths
|
||||
```
|
||||
|
||||
アプリのルートに `vitest.config.ts` を作成します:
|
||||
|
||||
```ts vitest.config.ts
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tsconfigPaths({
|
||||
projects: ['tsconfig.spec.json'],
|
||||
ignoreConfigErrors: true,
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
testTimeout: 120_000,
|
||||
hookTimeout: 120_000,
|
||||
include: ['src/**/*.integration-test.ts'],
|
||||
setupFiles: ['src/__tests__/setup-test.ts'],
|
||||
env: {
|
||||
TWENTY_API_URL: 'http://localhost:2020',
|
||||
TWENTY_API_KEY: 'your-api-key',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
テストを実行する前にサーバーに到達可能であることを検証するセットアップファイルを作成します:
|
||||
|
||||
```ts src/__tests__/setup-test.ts
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { beforeAll } from 'vitest';
|
||||
|
||||
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
|
||||
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
|
||||
|
||||
beforeAll(async () => {
|
||||
// Verify the server is running
|
||||
const response = await fetch(`${TWENTY_API_URL}/healthz`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
|
||||
'Start the server before running integration tests.',
|
||||
);
|
||||
}
|
||||
|
||||
// Write a temporary config for the SDK
|
||||
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(TEST_CONFIG_DIR, 'config.json'),
|
||||
JSON.stringify({
|
||||
remotes: {
|
||||
local: {
|
||||
apiUrl: process.env.TWENTY_API_URL,
|
||||
apiKey: process.env.TWENTY_API_KEY,
|
||||
},
|
||||
},
|
||||
defaultRemote: 'local',
|
||||
}, null, 2),
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
## プログラム用 SDK API
|
||||
|
||||
`twenty-sdk/cli` サブパスは、テストコードから直接呼び出せる関数をエクスポートします:
|
||||
|
||||
| 関数 | 説明 |
|
||||
| -------------- | ------------------------------- |
|
||||
| `appBuild` | アプリをビルドし、必要に応じて tarball にパッケージ化 |
|
||||
| `appDeploy` | tarball をサーバーにアップロード |
|
||||
| `appInstall` | アクティブなワークスペースにアプリをインストール |
|
||||
| `appUninstall` | アクティブなワークスペースからアプリをアンインストール |
|
||||
|
||||
各関数は、`success: boolean` と `data` または `error` のいずれかを含む結果オブジェクトを返します。
|
||||
|
||||
## 統合テストの作成
|
||||
|
||||
アプリをビルド、デプロイ、インストールし、その後ワークスペースに表示されることを検証する完全な例を次に示します:
|
||||
|
||||
```ts src/__tests__/app-install.integration-test.ts
|
||||
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config';
|
||||
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
const APP_PATH = process.cwd();
|
||||
|
||||
describe('App installation', () => {
|
||||
beforeAll(async () => {
|
||||
const buildResult = await appBuild({
|
||||
appPath: APP_PATH,
|
||||
tarball: true,
|
||||
onProgress: (message: string) => console.log(`[build] ${message}`),
|
||||
});
|
||||
|
||||
if (!buildResult.success) {
|
||||
throw new Error(`Build failed: ${buildResult.error?.message}`);
|
||||
}
|
||||
|
||||
const deployResult = await appDeploy({
|
||||
tarballPath: buildResult.data.tarballPath!,
|
||||
onProgress: (message: string) => console.log(`[deploy] ${message}`),
|
||||
});
|
||||
|
||||
if (!deployResult.success) {
|
||||
throw new Error(`Deploy failed: ${deployResult.error?.message}`);
|
||||
}
|
||||
|
||||
const installResult = await appInstall({ appPath: APP_PATH });
|
||||
|
||||
if (!installResult.success) {
|
||||
throw new Error(`Install failed: ${installResult.error?.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await appUninstall({ appPath: APP_PATH });
|
||||
});
|
||||
|
||||
it('should find the installed app in the workspace', async () => {
|
||||
const metadataClient = new MetadataApiClient();
|
||||
|
||||
const result = await metadataClient.query({
|
||||
findManyApplications: {
|
||||
id: true,
|
||||
name: true,
|
||||
universalIdentifier: true,
|
||||
},
|
||||
});
|
||||
|
||||
const installedApp = result.findManyApplications.find(
|
||||
(app: { universalIdentifier: string }) =>
|
||||
app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
);
|
||||
|
||||
expect(installedApp).toBeDefined();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## テストの実行
|
||||
|
||||
ローカルの Twenty サーバーが起動していることを確認し、次を実行します:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn test
|
||||
```
|
||||
|
||||
また、開発中はウォッチモードでも実行できます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn test:watch
|
||||
```
|
||||
|
||||
## 型チェック
|
||||
|
||||
テストを実行せずに、アプリの型チェックのみを実行することもできます:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty dev:typecheck
|
||||
```
|
||||
|
||||
これは `tsc --noEmit` を実行し、型エラーを報告します。
|
||||
|
||||
## GitHub Actions による CI
|
||||
|
||||
スキャフォルダーは、すぐに使える GitHub Actions ワークフローを `.github/workflows/ci.yml` に生成します。 `main` へのプッシュやプルリクエストのたびに、統合テストを自動実行します。
|
||||
|
||||
ワークフローの内容:
|
||||
|
||||
1. コードをチェックアウトする
|
||||
2. `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` アクションを使って一時的な Twenty サーバーを起動する
|
||||
3. `yarn install --immutable` で依存関係をインストールする
|
||||
4. アクションの出力から注入された `TWENTY_API_URL` と `TWENTY_API_KEY` を用いて `yarn test` を実行する
|
||||
|
||||
```yaml .github/workflows/ci.yml
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request: {}
|
||||
|
||||
env:
|
||||
TWENTY_VERSION: latest
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Spawn Twenty instance
|
||||
id: twenty
|
||||
uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
|
||||
with:
|
||||
twenty-version: ${{ env.TWENTY_VERSION }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Run integration tests
|
||||
run: yarn test
|
||||
env:
|
||||
TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
|
||||
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
|
||||
```
|
||||
|
||||
シークレットを設定する必要はありません。`spawn-twenty-docker-image` アクションがランナー内で一時的な Twenty サーバーを直接起動し、接続情報を出力します。 `GITHUB_TOKEN` シークレットは GitHub によって自動的に提供されます。
|
||||
|
||||
`latest` の代わりに特定の Twenty バージョンを固定するには、ワークフローの先頭にある `TWENTY_VERSION` 環境変数を変更します。
|
||||
@@ -17,7 +17,7 @@ Twenty は、お使いのデータモデルに特化した API を生成しま
|
||||
* **カスタムドキュメント**:ワークスペースのデータモデルに特化して生成
|
||||
|
||||
<Note>
|
||||
API キー作成後、**Settings → API & Webhooks** でパーソナライズされた API ドキュメントを利用できます。 Twenty はカスタムデータモデルに合致する API を生成するため、ドキュメントはお使いのワークスペース専用です。
|
||||
API キー作成後、**Settings → API & Webhooks** でパーソナライズされた API ドキュメントを利用できます。 Twenty はカスタムデータモデルに合致する API を生成するため、ドキュメントはお使いのワークスペース専用です。
|
||||
</Note>
|
||||
|
||||
## 2 つの API タイプ
|
||||
@@ -81,14 +81,14 @@ Authorization: Bearer YOUR_API_KEY
|
||||
<VimeoEmbed videoId="928786722" title="API キーの作成" />
|
||||
|
||||
<Warning>
|
||||
API キーは機密データへのアクセスを許可します。 信頼できないサービスと共有しないでください。 漏洩した場合は、直ちに無効化して新しいものを生成してください。
|
||||
API キーは機密データへのアクセスを許可します。 信頼できないサービスと共有しないでください。 漏洩した場合は、直ちに無効化して新しいものを生成してください。
|
||||
</Warning>
|
||||
|
||||
### API キーにロールを割り当てる
|
||||
|
||||
セキュリティを高めるため、アクセスを制限する特定のロールを割り当ててください:
|
||||
|
||||
1. **設定 → 役割** に移動
|
||||
1. **設定 → メンバー → 役割** に移動します
|
||||
2. 割り当てるロールをクリック
|
||||
3. **割り当て** タブを開く
|
||||
4. **API Keys** の下で、**+ Assign to API key** をクリック
|
||||
@@ -143,5 +143,5 @@ REST と GraphQL の両方がバッチ操作をサポートしています:
|
||||
| **バッチサイズ** | 1 回の呼び出しあたり 60 レコード |
|
||||
|
||||
<Tip>
|
||||
バッチ操作を使用してスループットを最大化しましょう—個別のリクエストではなく、1 回の API 呼び出しで最大 60 レコードを処理できます。
|
||||
バッチ操作を使用してスループットを最大化しましょう—個別のリクエストではなく、1 回の API 呼び出しで最大 60 レコードを処理できます。
|
||||
</Tip>
|
||||
|
||||
@@ -62,7 +62,7 @@ Twenty は次のイベントタイプに対してウェブフックを送信し
|
||||
| `タイムスタンプ` | イベントが発生した時刻(UTC) |
|
||||
|
||||
<Note>
|
||||
受信を確認するために、**2xx HTTP ステータス**(200~299)で応答してください。 2xx 以外の応答は配信失敗として記録されます。
|
||||
受信を確認するために、**2xx HTTP ステータス**(200~299)で応答してください。 2xx 以外の応答は配信失敗として記録されます。
|
||||
</Note>
|
||||
|
||||
## ウェブフックの検証
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
title: 拡張
|
||||
description: API、Webhook、カスタムアプリで Twenty の機能を拡張できます。
|
||||
redirect: /developers/introduction
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -16,20 +15,18 @@ Twenty は拡張性を念頭に設計されています。 当社の API、Webho
|
||||
|
||||
* **API**: REST または GraphQL を使用して、プログラムから CRM データをクエリおよび変更します。
|
||||
* **ウェブフック**: Twenty でイベントが発生したときにリアルタイム通知を受信します。
|
||||
* **アプリ**: Twenty の機能を拡張するカスタムアプリケーションを構築 - 近日公開!
|
||||
* **アプリ**: Twenty の機能を拡張するカスタムアプリケーションを構築する
|
||||
|
||||
## 始めに
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="API" icon="コード" href="/l/ja/developers/api">
|
||||
<Card title="API" icon="コード" href="/l/ja/developers/extend/api">
|
||||
プログラムから Twenty に接続
|
||||
</Card>
|
||||
|
||||
<Card title="ウェブフック" icon="bell" href="/l/ja/developers/webhooks">
|
||||
<Card title="ウェブフック" icon="bell" href="/l/ja/developers/extend/webhooks">
|
||||
イベントの通知をリアルタイムで受け取る
|
||||
</Card>
|
||||
|
||||
<Card title="アプリ" icon="puzzle-piece" href="/l/ja/developers/apps/apps">
|
||||
カスタマイズをコードとして構築(アルファ版)
|
||||
<Card title="アプリ" icon="puzzle-piece" href="/l/ja/developers/extend/apps/getting-started">
|
||||
カスタマイズをコードとして構築する
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
---
|
||||
title: OAuth
|
||||
icon: key
|
||||
description: PKCE を用いた認可コードフロー、およびサーバー間アクセス向けのクライアントクレデンシャル。
|
||||
---
|
||||
|
||||
Twenty は、ユーザー向けアプリでは認可コード + PKCE、サーバー間アクセスではクライアントクレデンシャルを用いた OAuth 2.0 を実装しています。 クライアントは [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) に従って動的に登録され、ダッシュボードでの手動セットアップは不要です。
|
||||
|
||||
## OAuth を使用するタイミング
|
||||
|
||||
| シナリオ | 認証方法 |
|
||||
| ---------------------- | ---------------------------------------------------------------- |
|
||||
| 社内向けスクリプト、自動化 | [API キー](/l/ja/developers/extend/api#authentication) |
|
||||
| ユーザーの代理として動作する外部アプリ | **OAuth — 認可コード** |
|
||||
| サーバー間、ユーザーコンテキストなし | **OAuth — クライアントクレデンシャル** |
|
||||
| UI 拡張機能を備えた Twenty アプリ | [アプリ](/l/ja/developers/extend/apps/getting-started)(OAuth は自動で処理されます) |
|
||||
|
||||
## クライアントを登録
|
||||
|
||||
Twenty は [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) に準拠した**動的クライアント登録**をサポートします。 手動セットアップは不要—プログラムで登録します:
|
||||
|
||||
```bash
|
||||
POST /oauth/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"client_name": "My Integration",
|
||||
"redirect_uris": ["https://myapp.com/callback"],
|
||||
"grant_types": ["authorization_code"],
|
||||
"token_endpoint_auth_method": "client_secret_post"
|
||||
}
|
||||
```
|
||||
|
||||
**レスポンス:**
|
||||
|
||||
```json
|
||||
{
|
||||
"client_id": "abc123",
|
||||
"client_secret": "secret456",
|
||||
"client_name": "My Integration",
|
||||
"redirect_uris": ["https://myapp.com/callback"]
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
`client_secret` は安全に保管してください—後から再取得することはできません。
|
||||
</Warning>
|
||||
|
||||
## スコープ
|
||||
|
||||
| スコープ | アクセス |
|
||||
| --------- | ---------------------------------------- |
|
||||
| `api` | Core および Metadata API への読み取り/書き込みのフルアクセス |
|
||||
| `profile` | 認証済みユーザーのプロフィール情報を読み取る |
|
||||
|
||||
スコープはスペース区切りの文字列で指定します: `scope=api profile`
|
||||
|
||||
## 認可コードフロー
|
||||
|
||||
アプリが Twenty ユーザーの代理として動作する場合にこのフローを使用します。
|
||||
|
||||
### 1. ユーザーを認可にリダイレクト
|
||||
|
||||
```
|
||||
GET /oauth/authorize?
|
||||
client_id=YOUR_CLIENT_ID&
|
||||
response_type=code&
|
||||
redirect_uri=https://myapp.com/callback&
|
||||
scope=api&
|
||||
state=random_state_value&
|
||||
code_challenge=CHALLENGE&
|
||||
code_challenge_method=S256
|
||||
```
|
||||
|
||||
| パラメーター | 必須 | 説明 |
|
||||
| ----------------------- | --- | ------------------------------------------------- |
|
||||
| `client_id` | はい | 登録済みのクライアント ID |
|
||||
| `response_type` | はい | `code` である必要があります |
|
||||
| `redirect_uri` | はい | 登録済みのリダイレクト URI と一致している必要があります |
|
||||
| `scope` | いいえ | スペース区切りのスコープ(既定は `api`) |
|
||||
| `state` | 推奨 | CSRF 攻撃を防ぐためのランダムな文字列 |
|
||||
| `code_challenge` | 推奨 | PKCE チャレンジ(ベリファイアの SHA-256 ハッシュを base64url エンコード) |
|
||||
| `code_challenge_method` | 推奨 | PKCE 使用時は `S256` である必要があります |
|
||||
|
||||
ユーザーに同意画面が表示され、アクセスを許可または拒否します。
|
||||
|
||||
### 2. コールバックを処理
|
||||
|
||||
認可後、Twenty はあなたの `redirect_uri` にリダイレクトします:
|
||||
|
||||
```
|
||||
https://myapp.com/callback?code=AUTH_CODE&state=random_state_value
|
||||
```
|
||||
|
||||
`state` が送信した値と一致することを確認します。
|
||||
|
||||
### 3. コードをトークンに交換
|
||||
|
||||
```bash
|
||||
POST /oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=authorization_code&
|
||||
code=AUTH_CODE&
|
||||
redirect_uri=https://myapp.com/callback&
|
||||
client_id=YOUR_CLIENT_ID&
|
||||
client_secret=YOUR_CLIENT_SECRET&
|
||||
code_verifier=YOUR_PKCE_VERIFIER
|
||||
```
|
||||
|
||||
**レスポンス:**
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbG...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"refresh_token": "dGhpcyBpcyBh..."
|
||||
}
|
||||
```
|
||||
|
||||
### 4. アクセストークンを使用
|
||||
|
||||
```bash
|
||||
GET /rest/companies
|
||||
Authorization: Bearer ACCESS_TOKEN
|
||||
```
|
||||
|
||||
### 5. 期限切れ時にリフレッシュ
|
||||
|
||||
```bash
|
||||
POST /oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=refresh_token&
|
||||
refresh_token=YOUR_REFRESH_TOKEN&
|
||||
client_id=YOUR_CLIENT_ID&
|
||||
client_secret=YOUR_CLIENT_SECRET
|
||||
```
|
||||
|
||||
## クライアントクレデンシャルフロー
|
||||
|
||||
ユーザー操作のないサーバー間統合向け:
|
||||
|
||||
```bash
|
||||
POST /oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=client_credentials&
|
||||
client_id=YOUR_CLIENT_ID&
|
||||
client_secret=YOUR_CLIENT_SECRET&
|
||||
scope=api
|
||||
```
|
||||
|
||||
返されるトークンは特定のユーザーに紐づかないワークスペースレベルのアクセス権を持ちます。
|
||||
|
||||
## サーバーディスカバリー
|
||||
|
||||
Twenty は標準のディスカバリーエンドポイントで OAuth 設定を公開しています:
|
||||
|
||||
```
|
||||
GET /.well-known/oauth-authorization-server
|
||||
```
|
||||
|
||||
これにより、すべてのエンドポイント、対応するグラントタイプ、スコープ、および機能が返されます—汎用的な OAuth クライアントを構築するのに役立ちます。
|
||||
|
||||
## API エンドポイント概要
|
||||
|
||||
| エンドポイント | 目的 |
|
||||
| ----------------------------------------- | ----------------- |
|
||||
| `/.well-known/oauth-authorization-server` | サーバーメタデータのディスカバリー |
|
||||
| `/oauth/register` | 動的クライアント登録 |
|
||||
| `/oauth/authorize` | ユーザー認可 |
|
||||
| `/oauth/token` | トークンの交換とリフレッシュ |
|
||||
|
||||
| 環境 | ベース URL |
|
||||
| ---------- | ------------------------ |
|
||||
| **クラウド** | `https://api.twenty.com` |
|
||||
| **セルフホスト** | `https://{your-domain}` |
|
||||
|
||||
## OAuth と API キー
|
||||
|
||||
| | APIキー | OAuth |
|
||||
| --------------- | -------------- | ----------------- |
|
||||
| **セットアップ** | 設定で生成 | クライアントを登録し、フローを実装 |
|
||||
| **ユーザーコンテキスト** | なし(ワークスペースレベル) | 特定ユーザーの権限 |
|
||||
| **最適な用途** | スクリプト、社内ツール | 外部アプリ、マルチユーザー統合 |
|
||||
| **トークンローテーション** | 手動 | リフレッシュトークンにより自動 |
|
||||
| **スコープによるアクセス** | API へのフルアクセス | スコープによりきめ細かな制御 |
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: ウェブフック
|
||||
icon: satellite-dish
|
||||
description: レコードが変更されたときに通知を受け取ります — 作成・更新・削除のたびに、HTTP POST をお使いのエンドポイントに送信します。
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
レコードが作成・更新・削除されるたびに、Twenty はお使いの URL に HTTP POST を送信します。 カスタムオブジェクトを含む、すべてのオブジェクトタイプが対象です。
|
||||
|
||||
## Webhookを作成
|
||||
|
||||
1. **Settings → APIs & Webhooks → Webhooks**に移動
|
||||
2. **+ Webhookを作成**をクリック
|
||||
3. ウェブフックの URL を入力(外部からアクセス可能である必要があります)
|
||||
4. **保存**をクリック
|
||||
|
||||
ウェブフックは直ちに有効化され、通知の送信を開始します。
|
||||
|
||||
<VimeoEmbed videoId="928786708" title="ウェブフックの作成" />
|
||||
|
||||
### Webhookを管理
|
||||
|
||||
**編集**: ウェブフックをクリック → URL を更新 → **保存**
|
||||
|
||||
**削除**: ウェブフックをクリック → **削除** → 確認
|
||||
|
||||
## イベント
|
||||
|
||||
Twenty は次のイベントタイプに対してウェブフックを送信します。
|
||||
|
||||
| イベント | 例 |
|
||||
| ----------- | ---------------------------------------------------------- |
|
||||
| **レコードの作成** | `person.created`, `company.created`, `note.created` |
|
||||
| **レコードの更新** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **レコードの削除** | `person.deleted`, `company.deleted` |
|
||||
|
||||
すべてのイベントタイプはウェブフックの URL に送信されます。 イベントのフィルタリングは将来のリリースで追加される可能性があります。
|
||||
|
||||
## ペイロード形式
|
||||
|
||||
各ウェブフックは JSON ボディを含む HTTP POST を送信します。
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "person.created",
|
||||
"data": {
|
||||
"id": "abc12345",
|
||||
"firstName": "Alice",
|
||||
"lastName": "Doe",
|
||||
"email": "alice@example.com",
|
||||
"createdAt": "2025-02-10T15:30:45Z",
|
||||
"createdBy": "user_123"
|
||||
},
|
||||
"timestamp": "2025-02-10T15:30:50Z"
|
||||
}
|
||||
```
|
||||
|
||||
| フィールド | 説明 |
|
||||
| ----------- | --------------------------- |
|
||||
| `event` | 何が起きたか(例: `person.created`) |
|
||||
| `data` | 作成/更新/削除された完全なレコード |
|
||||
| `timestamp` | イベントが発生した時刻(UTC) |
|
||||
|
||||
<Note>
|
||||
受信を確認するために、**2xx HTTP ステータス**(200~299)で応答してください。 2xx 以外の応答は配信失敗として記録されます。
|
||||
</Note>
|
||||
|
||||
## ウェブフックの検証
|
||||
|
||||
Twenty はセキュリティのために各ウェブフックリクエストに署名します。 リクエストが正当であることを確認するために署名を検証してください。
|
||||
|
||||
### ヘッダー
|
||||
|
||||
| ヘッダー | 説明 |
|
||||
| ---------------------------- | -------------- |
|
||||
| `X-Twenty-Webhook-Signature` | HMAC SHA256 署名 |
|
||||
| `X-Twenty-Webhook-Timestamp` | リクエストのタイムスタンプ |
|
||||
|
||||
### 検証手順
|
||||
|
||||
1. `X-Twenty-Webhook-Timestamp` からタイムスタンプを取得
|
||||
2. 次の文字列を作成: `{timestamp}:{JSON payload}`
|
||||
3. ウェブフックシークレットを使用して HMAC SHA256 を計算
|
||||
4. `X-Twenty-Webhook-Signature` と比較
|
||||
|
||||
### 例(Node.js)
|
||||
|
||||
```javascript
|
||||
const crypto = require("crypto");
|
||||
|
||||
const timestamp = req.headers["x-twenty-webhook-timestamp"];
|
||||
const payload = JSON.stringify(req.body);
|
||||
const secret = "your-webhook-secret";
|
||||
|
||||
const stringToSign = `${timestamp}:${payload}`;
|
||||
const expectedSignature = crypto
|
||||
.createHmac("sha256", secret)
|
||||
.update(stringToSign)
|
||||
.digest("hex");
|
||||
|
||||
const receivedSignature = req.headers["x-twenty-webhook-signature"];
|
||||
const isValid = crypto.timingSafeEqual(
|
||||
Buffer.from(expectedSignature, "hex"),
|
||||
Buffer.from(receivedSignature, "hex")
|
||||
);
|
||||
```
|
||||
|
||||
## ウェブフック vs ワークフロー
|
||||
|
||||
| メソッド | 方向 | ユースケース |
|
||||
| ----------------------- | --- | --------------------------- |
|
||||
| **ウェブフック** | OUT | あらゆるレコード変更を外部システムへ自動通知 |
|
||||
| **ワークフロー + HTTP リクエスト** | OUT | カスタムロジック(フィルター、変換)でデータを外部送信 |
|
||||
| **ワークフローのウェブフックトリガー** | IN | 外部システムから Twenty にデータを受信 |
|
||||
|
||||
外部データの受信については、[Webhook トリガーを設定](/l/ja/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger)を参照してください。
|
||||
@@ -1,33 +1,28 @@
|
||||
---
|
||||
title: 始めに
|
||||
description: Twenty 開発者ドキュメントへようこそ。Twenty の拡張、セルフホスティング、貢献のためのリソースです。
|
||||
title: 開発者
|
||||
description: アプリの構築、API の利用、セルフホスト、コードベースへの貢献が可能です。
|
||||
---
|
||||
|
||||
import { CardTitle } from "/snippets/card-title.mdx"
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card href="/l/ja/developers/api" icon="code">
|
||||
<CardGroup cols={3}>
|
||||
<Card href="/l/ja/developers/extend/apps/getting-started" img="/images/user-guide/halftone/dev-apps.png">
|
||||
<CardTitle>アプリ</CardTitle>
|
||||
カスタムオブジェクト、サーバーサイドロジック、UI コンポーネント、AI エージェントによって Twenty を拡張できます — これらはすべて TypeScript パッケージです。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/extend/api" img="/images/user-guide/halftone/dev-api.png">
|
||||
<CardTitle>API</CardTitle>
|
||||
REST または GraphQL で CRM データをクエリおよび変更します。
|
||||
REST と GraphQL の API、Webhook、OAuth。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/webhooks" icon="bell">
|
||||
<CardTitle>Webhooks</CardTitle>
|
||||
イベント発生時にリアルタイムで通知を受け取ります。
|
||||
<Card href="/l/ja/developers/self-host/capabilities/docker-compose" img="/images/user-guide/halftone/dev-self-host.png">
|
||||
<CardTitle>セルフホスト</CardTitle>
|
||||
独自のインフラ上で Twenty を実行できます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/apps/apps" icon="puzzle-piece">
|
||||
<CardTitle>Apps</CardTitle>
|
||||
Twenty の機能を拡張するカスタムアプリケーションを構築します。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/self-host/self-host" icon="desktop">
|
||||
<CardTitle>Self-Host</CardTitle>
|
||||
ご自身のインフラストラクチャで Twenty をデプロイおよび管理します。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/contribute/contribute" icon="github">
|
||||
<CardTitle>Contribute</CardTitle>
|
||||
オープンソースコミュニティに参加し、Twenty に貢献しましょう。
|
||||
<Card href="/l/ja/developers/contribute/capabilities/local-setup" img="/images/user-guide/halftone/dev-contribute.png">
|
||||
<CardTitle>貢献</CardTitle>
|
||||
モノレポをローカルにセットアップして、PR を提出しましょう。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
---
|
||||
title: 1-クリック w/ Docker Compose
|
||||
title: Docker Compose
|
||||
icon: docker
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Dockerコンテナは本番ホスティングまたはセルフホスティング用です。貢献するには、[ローカルセットアップ](/l/ja/developers/contribute/capabilities/local-setup)を確認してください。
|
||||
Docker コンテナーは、本番環境でのホスティングまたはセルフホスティング向けです。 コントリビュートする場合は、[ローカルセットアップ](/l/ja/developers/contribute/capabilities/local-setup)を確認してください。
|
||||
</Warning>
|
||||
|
||||
## 概要
|
||||
@@ -12,7 +13,7 @@ title: 1-クリック w/ Docker Compose
|
||||
|
||||
**重要:** このガイドで明示的に言及されている設定のみを変更してください。 他の構成を変更すると、問題が発生する可能性があります。 他の構成を変更すると、問題が発生する可能性があります。 他の構成を変更すると、問題が発生する可能性があります。
|
||||
|
||||
高度な構成については、[環境変数の設定](/l/ja/developers/self-host/capabilities/setup)を参照してください。 高度な構成については、[環境変数の設定](https://docs.twenty.com/l/ja/developers/self-hosting/setup)を参照してください。 高度な構成については、[環境変数の設定](https://docs.twenty.com/l/ja/developers/self-hosting/setup)を参照してください。 すべての環境変数は、サーバーレベルまたはワーカーレベルでdocker-compose.ymlファイルに宣言する必要があります。
|
||||
高度な構成については、[環境変数の設定](/l/ja/developers/self-host/capabilities/setup)を参照してください。 すべての環境変数は、変数に応じて、サーバーレベルまたはワーカーレベル(またはその両方)で、`docker-compose.yml`ファイルに宣言する必要があります。
|
||||
|
||||
## システム要件
|
||||
|
||||
@@ -50,7 +51,7 @@ VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.
|
||||
curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example
|
||||
```
|
||||
|
||||
2. **シークレットトークンを生成**
|
||||
2. **暗号化キーを生成する**
|
||||
|
||||
ユニークなランダム文字列を生成するには、次のコマンドを実行します:
|
||||
|
||||
@@ -58,16 +59,18 @@ VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
**重要:** この値を秘密にしてください/共有しないでください。
|
||||
**重要:** この値を秘密にしてください/共有しないでください。 `ENCRYPTION_KEY` を失うことは、データベースに保存されているすべてのシークレット(OAuth トークン、アプリケーション変数、TOTP シークレットなど)へのアクセスを失うことを意味します。
|
||||
|
||||
3. **`.env`を更新**
|
||||
|
||||
生成したトークンで.envファイルのプレースホルダー値を置き換えます:
|
||||
|
||||
```ini
|
||||
APP_SECRET=first_random_string
|
||||
ENCRYPTION_KEY=random_string
|
||||
```
|
||||
|
||||
ダウンタイムなしでキーをローテーションする手順については、[キーのローテーションガイド](/l/ja/developers/self-host/capabilities/key-rotation)を参照してください。
|
||||
|
||||
4. **Postgres パスワードを設定**
|
||||
|
||||
特殊文字を含まない強力なパスワードで、.envファイルの`PG_DATABASE_PASSWORD`値を更新します。
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: キーのローテーション
|
||||
icon: rotate
|
||||
---
|
||||
|
||||
Twenty には、互いに独立した 2 つのキー群があります。
|
||||
|
||||
* **JWT 署名キー** — `core."signingKey"` に保存された非対称 ES256 キーペア(`kid` タグ付き)で、アクセストークン / リフレッシュトークンの署名および検証に使用されます。
|
||||
* **保存データ暗号化キー** — `ENCRYPTION_KEY`。OAuth トークン、アプリケーション変数、署名キーの秘密鍵、機密性の高い設定値、および `enc:v2:` エンベロープ内の TOTP シークレットを暗号化するために使用されます。
|
||||
|
||||
`APP_SECRET` は後方互換性のために保持されているレガシーなシークレットです。`ENCRYPTION_KEY` が未設定の場合、保存データ暗号化 / セッション Cookie のフォールバックとして動作し、既存の HS256 アクセストークンの検証にも引き続き使用されます。 これは非推奨となる予定です。
|
||||
|
||||
## JWT 署名キー
|
||||
|
||||
各キーには、`publicKey`(過去に発行されたトークンを検証できるよう、無期限に保持)、暗号化された `privateKey`(そのキーが現行の間のみ使用)、`isCurrent` フラグ(同時にちょうど 1 行だけ true)、および任意の `revokedAt` が含まれます。
|
||||
|
||||
### 現在のキーをローテーションする
|
||||
|
||||
オプトインするには `SIGNING_KEY_ROTATION_DAYS` を設定します。日次の cron により、既存のキーがその閾値より古くなると、新しいキーが現行キーとして発行されます。 以前のキーは *失効しない* ため、そのキーで署名されたトークンは検証に通り続けます。 自動ローテーションを無効にするには、変数を未設定のままにします。
|
||||
|
||||
<Note>自動ローテーションは v2.6 以降で提供されています。</Note>
|
||||
|
||||
### キーを失効させる(漏えい時 / 緊急時のみ)
|
||||
|
||||
非現行の行で **Settings → Admin Panel → Signing keys → Revoke** を実行します。 暗号化された秘密情報を消去し、`revokedAt` を設定し、その `kid` で署名された既存トークンをすべて拒否します。
|
||||
|
||||
## `ENCRYPTION_KEY` をローテーションする
|
||||
|
||||
<Note>以下で説明する `secret-encryption:rotate` コマンドは v2.6+ で提供されています。</Note>
|
||||
|
||||
すべての暗号化された値は `enc:v2:\<keyId>:\<payload>` の形式でラップされます。ここで `\<keyId>` は、生のキーから導出された 8 文字の 16 進プレフィックスです。 ローテーションはオンラインで実行でき、中断しても再開可能です。
|
||||
|
||||
1. **新しいキーを生成**: `openssl rand -base64 32`.
|
||||
|
||||
2. `.env` に **両方のキーを並べて設定** し、再起動します:
|
||||
```ini
|
||||
ENCRYPTION_KEY=NEW_VALUE
|
||||
FALLBACK_ENCRYPTION_KEY=OLD_VALUE
|
||||
```
|
||||
新規書き込みは新しいキーを使用し、既存行はフォールバック経由で引き続き復号されます。
|
||||
|
||||
3. **既存行を再暗号化する**:
|
||||
|
||||
```bash
|
||||
docker exec -it {server_container} yarn command:prod secret-encryption:rotate
|
||||
```
|
||||
|
||||
このコマンドは 6 つのサイト(`connected-account-tokens`、`application-variable`、`application-registration-variable`、`signing-key-private-keys`、`sensitive-config-storage`、`totp-secrets`)を走査します。 SQL フィルターにより、すでに新しい `\<keyId>` になっている行はスキップされるため、このコマンドはべき等です。必要に応じて中断し、再実行できます。 いずれかの行で失敗した場合は非ゼロで終了します。再実行してリトライしてください。
|
||||
|
||||
| フラグ | 説明 |
|
||||
| ---------------------------------------- | ----------------------------------- |
|
||||
| `-s, --site \<site>` | 単一のサイトに限定します。 |
|
||||
| `-b, --batch-size \<n>` | バッチごとの行数(デフォルト `200`、最大 `5000`)。 |
|
||||
| `-d, --dry-run` | メモリ上で復号 + 再暗号化を行い、`UPDATE` は実行しません。 |
|
||||
|
||||
4. `--dry-run` で残り行数が 0 になったことを確認したら **フォールバックを削除** します。`FALLBACK_ENCRYPTION_KEY` を削除して再起動してください。
|
||||
|
||||
## レガシーな `APP_SECRET` サポート
|
||||
|
||||
これまで一度も `ENCRYPTION_KEY` を設定していない古いインスタンスは、`APP_SECRET` を保存データ暗号化キー(および、そこから導出されるセッション Cookie シークレット)として使用します。 この経路は後方互換性のために保持されていますが、**非推奨** です。専用の `ENCRYPTION_KEY` を設定し、上記のローテーション手順に従って移行してください。 `APP_SECRET` 自体はレガシーな HS256 アクセストークンを検証するために引き続き使用されます。
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
title: セットアップ
|
||||
icon: gear
|
||||
---
|
||||
|
||||
# 構成管理
|
||||
|
||||
<Warning>
|
||||
**初めてインストールしますか?** [Docker Compose インストールガイド](/l/ja/developers/self-host/capabilities/docker-compose)に従ってTwentyを起動し、その後はここに戻り構成してください。
|
||||
**初めてインストールしますか?** [Docker Compose インストールガイド](/l/ja/developers/self-host/capabilities/docker-compose)に従ってTwentyを起動し、その後はここに戻り構成してください。
|
||||
</Warning>
|
||||
|
||||
Twentyは、異なる展開ニーズに合わせて**2つの構成モード**を提供します:
|
||||
@@ -26,8 +27,8 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # default
|
||||
4. 変更はすぐに(マルチコンテナ展開の場合、15秒以内に)効果が出ます。
|
||||
|
||||
<Warning>
|
||||
**マルチコンテナ展開:** データベース構成を使用する場合 (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`)、サーバーとワーカーコンテナの両方が同じデータベースから読み込みます。 管理パネルの変更は両方に自動的に影響し、コンテナ間で環境変数を重複させる必要がなくなります(インフラストラクチャの変数を除く)。
|
||||
管理パネルの変更は両方に自動的に影響し、コンテナ間で環境変数を重複させる必要がなくなります(インフラストラクチャの変数を除く)。
|
||||
**マルチコンテナ展開:** データベース構成を使用する場合 (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`)、サーバーとワーカーコンテナの両方が同じデータベースから読み込みます。 管理パネルの変更は両方に自動的に影響し、コンテナ間で環境変数を重複させる必要がなくなります(インフラストラクチャの変数を除く)。
|
||||
管理パネルの変更は両方に自動的に影響し、コンテナ間で環境変数を重複させる必要がなくなります(インフラストラクチャの変数を除く)。
|
||||
</Warning>
|
||||
|
||||
**管理パネルを通じて構成できること:**
|
||||
@@ -42,13 +43,27 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # default
|
||||

|
||||
|
||||
<Warning>
|
||||
各変数は、**設定 → 管理パネル → 構成変数** で説明付きでドキュメント化されています。
|
||||
各変数は、**設定 → 管理パネル → 構成変数** で説明付きでドキュメント化されています。
|
||||
データベース接続 (`PG_DATABASE_URL`)、サーバーURL (`SERVER_URL`)、アプリの秘密キー (`APP_SECRET`) など、一部のインフラ設定は `.env` ファイルを介してのみ構成可能です。
|
||||
各変数は、**設定 → 管理パネル → 構成変数** で説明付きでドキュメント化されています。
|
||||
データベース接続 (`PG_DATABASE_URL`)、サーバーURL (`SERVER_URL`)、秘密情報 (`ENCRYPTION_KEY`, `FALLBACK_ENCRYPTION_KEY`) など、一部のインフラ設定は `.env` ファイルを介してのみ構成できます。
|
||||
|
||||
[ 完全な技術リファレンス →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts)
|
||||
[ 完全な技術リファレンス →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts)
|
||||
</Warning>
|
||||
|
||||
## 暗号鍵
|
||||
|
||||
Twenty は、環境変数でのみ設定可能な暗号鍵を 2 つ使用します:
|
||||
|
||||
| 変数 | 目的 | 必須 |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
||||
| `ENCRYPTION_KEY` | 保存時の秘密情報(OAuth トークン、アプリケーション変数、署名鍵の秘密鍵、TOTP シークレット、機密性の高い構成値)を暗号化するために使用されるプライマリキー。 | 新規インストールでは必須(レガシーインストールでは代わりに `APP_SECRET` に依存している場合があります — 下記参照) |
|
||||
| `FALLBACK_ENCRYPTION_KEY` | 検証専用のキー。 ローテーション時に、既存の行を復号可能なままにするため、*以前の* `ENCRYPTION_KEY` を設定します。 | ローテーション中のみ |
|
||||
|
||||
後方互換性のため、`ENCRYPTION_KEY` が未設定の場合、Twenty は保存時の暗号化に `APP_SECRET` をフォールバックとして使用します。これは、旧来のデプロイメントにおける従来の動作と一致します。 新規インストールでは、必ず専用の `ENCRYPTION_KEY` を設定してください。
|
||||
|
||||
`openssl rand -base64 32` で値を生成し、シークレットマネージャーやシールドされた構成など、安全な場所に保管してください。 `ENCRYPTION_KEY` を失うことは、データベースに保存されているすべての秘密情報へのアクセスを失うことを意味します。
|
||||
|
||||
ダウンタイムなしで `ENCRYPTION_KEY` をローテーションする方法については、[キー ローテーション ガイド](/l/ja/developers/self-host/capabilities/key-rotation) を参照してください。
|
||||
|
||||
## 2. 環境のみの構成
|
||||
|
||||
```bash
|
||||
@@ -95,7 +110,7 @@ DEFAULT_SUBDOMAIN=app # default value
|
||||
* サブドメインやカスタムドメインなどのワークスペース固有の設定がワークスペース設定で利用可能になります
|
||||
|
||||
<Warning>
|
||||
**環境専用の設定:** `IS_MULTIWORKSPACE_ENABLED` は `.env` ファイルでのみ設定でき、再起動が必要です。 管理パネルからは変更できません。
|
||||
**環境専用の設定:** `IS_MULTIWORKSPACE_ENABLED` は `.env` ファイルでのみ設定でき、再起動が必要です。 管理パネルからは変更できません。
|
||||
</Warning>
|
||||
|
||||
### マルチワークスペース向けの DNS 構成
|
||||
@@ -151,7 +166,7 @@ IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
|
||||
* `AUTH_GOOGLE_APIS_CALLBACK_URL=https://{your-domain}/auth/google-apis/get-access-token`
|
||||
|
||||
<Warning>
|
||||
**環境専用モード:** `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` を設定した場合、これらの変数を `.env` ファイルに追加してください。
|
||||
**環境専用モード:** `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` を設定した場合、これらの変数を `.env` ファイルに追加してください。
|
||||
</Warning>
|
||||
|
||||
**必要なスコープ** (自動的に構成される):
|
||||
@@ -170,7 +185,7 @@ IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
|
||||
## Microsoft 365 統合
|
||||
|
||||
<Warning>
|
||||
カレンダーおよびメッセージングAPIを使用するためには、[Microsoft 365 ライセンス](https://admin.microsoft.com/Adminportal/Home)が必要です。 They will not be able to sync their account on Twenty without one. それがない場合、Twenty でアカウントを同期できません。
|
||||
カレンダーおよびメッセージングAPIを使用するためには、[Microsoft 365 ライセンス](https://admin.microsoft.com/Adminportal/Home)が必要です。 They will not be able to sync their account on Twenty without one. それがない場合、Twenty でアカウントを同期できません。
|
||||
</Warning>
|
||||
|
||||
### Microsoft Azureでプロジェクトを作成
|
||||
@@ -213,7 +228,7 @@ Microsoft Azureコンソールで"権限"の欄で以下のAPIを有効にしま
|
||||
* `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://{your-domain}/auth/microsoft-apis/get-access-token`
|
||||
|
||||
<Warning>
|
||||
**環境専用モード:** `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` を設定した場合、これらの変数を `.env` ファイルに追加してください。
|
||||
**環境専用モード:** `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` を設定した場合、これらの変数を `.env` ファイルに追加してください。
|
||||
</Warning>
|
||||
|
||||
### スコープを構成
|
||||
@@ -258,82 +273,111 @@ yarn command:prod cron:workflow:automated-cron-trigger
|
||||
3. SMTP設定を構成:
|
||||
|
||||
<ArticleTabs label1="Gmail" label2="Office365" label3="Smtp4dev">
|
||||
<ArticleTab>
|
||||
[アプリパスワード](https://support.google.com/accounts/answer/185833)を準備する必要があります。
|
||||
|
||||
<ArticleTab>
|
||||
|
||||
[アプリパスワード](https://support.google.com/accounts/answer/185833)を準備する必要があります。
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.gmail.com
|
||||
* EMAIL_SMTP_PORT=465
|
||||
* EMAIL_SMTP_USER=gmail_email_address
|
||||
* EMAIL_SMTP_PASSWORD='gmail_app_password'
|
||||
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
|
||||
2FAを有効にしている場合、[アプリパスワード](https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9)を準備する必要があります。
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.office365.com
|
||||
* EMAIL_SMTP_PORT=587
|
||||
* EMAIL_SMTP_USER=office365_email_address
|
||||
* EMAIL_SMTP_PASSWORD='office365_password'
|
||||
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
|
||||
**smtp4dev** は開発とテストのためのフェイクSMTPメールサーバーです。
|
||||
* smtp4devイメージを実行: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev`
|
||||
* smtp4dev UIにアクセス: [http://localhost:8090](http://localhost:8090)
|
||||
* 次の変数を設定:
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.gmail.com
|
||||
* EMAIL_SMTP_PORT=465
|
||||
* EMAIL_SMTP_USER=gmail_email_address
|
||||
* EMAIL_SMTP_PASSWORD='gmail_app_password'
|
||||
* EMAIL_SMTP_HOST=localhost
|
||||
* EMAIL_SMTP_PORT=2525
|
||||
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
2FAを有効にしている場合、[アプリパスワード](https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9)を準備する必要があります。
|
||||
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.office365.com
|
||||
* EMAIL_SMTP_PORT=587
|
||||
* EMAIL_SMTP_USER=office365_email_address
|
||||
* EMAIL_SMTP_PASSWORD='office365_password'
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
**smtp4dev** は開発とテストのためのフェイクSMTPメールサーバーです。
|
||||
|
||||
* smtp4devイメージを実行: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev`
|
||||
* smtp4dev UIにアクセス: [http://localhost:8090](http://localhost:8090)
|
||||
* 次の変数を設定:
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=localhost
|
||||
* EMAIL_SMTP_PORT=2525
|
||||
</ArticleTab>
|
||||
</ArticleTabs>
|
||||
|
||||
<Warning>
|
||||
**環境専用モード:** `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` を設定した場合、これらの変数を `.env` ファイルに追加してください。
|
||||
**環境専用モード:** `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` を設定した場合、これらの変数を `.env` ファイルに追加してください。
|
||||
</Warning>
|
||||
|
||||
## Logic Functions
|
||||
|
||||
Twenty supports logic functions for workflows and custom logic. 実行環境は、`SERVERLESS_TYPE` 環境変数で設定されます。
|
||||
## S3 ストレージ
|
||||
|
||||
<Warning>
|
||||
**Security Notice:** The local driver (`SERVERLESS_TYPE=LOCAL`) runs code directly on the host in a Node.js process with no sandboxing. 開発では、信頼できるコードにのみ使用してください。 信頼できないコードを扱う本番デプロイでは、`SERVERLESS_TYPE=LAMBDA` または `SERVERLESS_TYPE=DISABLED` の使用を強く推奨します。
|
||||
デフォルトでは、Twenty はアップロードされたファイルをローカルのファイルシステムに保存します。 本番環境へのデプロイでは、S3 または S3 互換のサービス(MinIO、DigitalOcean Spaces など)を使用してください。 コンテナの再起動をまたいでファイルを永続化し、複数のサーバーインスタンスにまたがってスケールできるようにするため。
|
||||
</Warning>
|
||||
|
||||
### 利用可能なドライバー
|
||||
`STORAGE_TYPE=S_3` を設定し、管理パネルまたは `.env` から `STORAGE_S3_*` 変数を設定してください。 S3 変数の全一覧については、[config-variables.ts リファレンス](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts) を参照してください。
|
||||
|
||||
| ドライバー | 環境変数 | ユースケース | セキュリティレベル |
|
||||
| ------ | -------------------------- | -------------------------------- | --------------- |
|
||||
| 無効 | `SERVERLESS_TYPE=DISABLED` | Disable logic functions entirely | 該当なし |
|
||||
| ローカル | `SERVERLESS_TYPE=LOCAL` | 開発および信頼できる環境 | 低(サンドボックスなし) |
|
||||
| Lambda | `SERVERLESS_TYPE=LAMBDA` | 信頼できないコードを扱う本番環境 | 高(ハードウェアレベルの分離) |
|
||||
CORS に依存する機能(例: ブラウザ内でのファイルダウンロード)で S3 を使用する場合は、バケットの CORS 設定で Twenty フロントエンドのオリジンが許可されていることを確認してください。
|
||||
|
||||
### 推奨構成
|
||||
## ロジック関数とコードインタープリター
|
||||
|
||||
Twenty は、ワークフロー向けのロジック関数と、AI データ分析用のコードインタープリターをサポートします。 どちらもユーザー提供のコードを実行し、セキュリティのために明示的な設定が必要です。
|
||||
|
||||
### セキュリティのデフォルト
|
||||
|
||||
**本番環境 (NODE_ENV=production):** ロジック関数とコードインタープリターはいずれもデフォルトで**無効**です。 これらの機能が必要な場合は、`LOGIC_FUNCTION_TYPE` と `CODE_INTERPRETER_TYPE` で明示的に有効化する必要があります。
|
||||
|
||||
**開発環境 (NODE_ENV=development):** ローカルでの実行を容易にするため、どちらもデフォルトでは**LOCAL**です。
|
||||
|
||||
<Warning>
|
||||
**セキュリティに関する注意:** ローカルドライバー (`LOGIC_FUNCTION_TYPE=LOCAL` または `CODE_INTERPRETER_TYPE=LOCAL`) は、ホスト上の Node.js プロセス内でサンドボックスなしにコードを直接実行します。 開発では、信頼できるコードにのみ使用してください。 信頼できないコードを扱う本番デプロイでは、`LOGIC_FUNCTION_TYPE=LAMBDA` または `CODE_INTERPRETER_TYPE=E2B`(サンドボックス化あり)を使用するか、無効のままにしてください。
|
||||
</Warning>
|
||||
|
||||
### ロジック関数 - 利用可能なドライバー
|
||||
|
||||
| ドライバー | 環境変数 | ユースケース | セキュリティレベル |
|
||||
| ------ | ------------------------------ | ---------------- | --------------- |
|
||||
| 無効 | `LOGIC_FUNCTION_TYPE=DISABLED` | ロジック関数を完全に無効化する | 該当なし |
|
||||
| ローカル | `LOGIC_FUNCTION_TYPE=LOCAL` | 開発および信頼できる環境 | 低(サンドボックスなし) |
|
||||
| Lambda | `LOGIC_FUNCTION_TYPE=LAMBDA` | 信頼できないコードを扱う本番環境 | 高(ハードウェアレベルの分離) |
|
||||
|
||||
### ロジック関数 - 推奨構成
|
||||
|
||||
**開発向け:**
|
||||
|
||||
```bash
|
||||
SERVERLESS_TYPE=LOCAL # default
|
||||
LOGIC_FUNCTION_TYPE=LOCAL # default when NODE_ENV=development
|
||||
```
|
||||
|
||||
**本番向け(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
|
||||
LOGIC_FUNCTION_TYPE=LAMBDA
|
||||
LOGIC_FUNCTION_LAMBDA_REGION=us-east-1
|
||||
LOGIC_FUNCTION_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role
|
||||
LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID=your-access-key
|
||||
LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY=your-secret-key
|
||||
```
|
||||
|
||||
**To disable logic functions:**
|
||||
**ロジック関数を無効化するには:**
|
||||
|
||||
```bash
|
||||
SERVERLESS_TYPE=DISABLED
|
||||
LOGIC_FUNCTION_TYPE=DISABLED # default when NODE_ENV=production
|
||||
```
|
||||
|
||||
### コードインタープリター - 利用可能なドライバー
|
||||
|
||||
| ドライバー | 環境変数 | ユースケース | セキュリティレベル |
|
||||
| ----- | -------------------------------- | -------------- | --------------- |
|
||||
| 無効 | `CODE_INTERPRETER_TYPE=DISABLED` | AIコード実行を無効化 | 該当なし |
|
||||
| ローカル | `CODE_INTERPRETER_TYPE=LOCAL` | 開発専用 | 低(サンドボックスなし) |
|
||||
| E2B | `CODE_INTERPRETER_TYPE=E_2_B` | サンドボックス実行の本番環境 | 高(分離されたサンドボックス) |
|
||||
|
||||
<Note>
|
||||
When using `SERVERLESS_TYPE=DISABLED`, any attempt to execute a logic function will return an error. This is useful if you want to run Twenty without logic function capabilities.
|
||||
`LOGIC_FUNCTION_TYPE=DISABLED` または `CODE_INTERPRETER_TYPE=DISABLED` を使用している場合、実行しようとするとエラーが返されます。 これは、これらの機能なしで Twenty を実行したい場合に便利です。
|
||||
</Note>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: トラブルシューティング
|
||||
icon: wrench
|
||||
---
|
||||
|
||||
## トラブルシューティング
|
||||
@@ -57,7 +58,7 @@ Twentyのインストール中に、postgresデータベースを適切なスキ
|
||||
|
||||
#### 保存時のLintが機能しない
|
||||
|
||||
Oxc拡張子 (`oxc.oxc-vscode`)がインストールされている場合、これは標準設定で動作するはずです。 もしこれが機能しない場合、vscode設定で以下を追加してみてください(開発コンテナ範囲内で)。
|
||||
Oxc拡張機能 (`oxc.oxc-vscode`) がインストールされている場合、これは標準設定で動作するはずです。 eslint拡張子がインストールされている場合、これは標準設定で動作するはずです。 もしこれが機能しない場合、vscode設定で以下を追加してみてください(開発コンテナ範囲内で)。
|
||||
|
||||
```
|
||||
"editor.codeActionsOnSave": {
|
||||
@@ -173,6 +174,10 @@ plugins: [
|
||||
|
||||
adminパネルにアクセスするには、データベースコンテナで`UPDATE core."user" SET "canAccessFullAdminPanel" = TRUE WHERE email = 'you@yourdomain.com';`を実行します。
|
||||
|
||||
#### ワークフローを実行すると、エラー「ロジック関数の実行は無効です。 有効にするには LOGIC_FUNCTION_TYPE を LOCAL または LAMBDA に設定してください。」
|
||||
|
||||
本番環境では、ロジック関数はデフォルトで無効になっています。 有効にするには、`LOGIC_FUNCTION_TYPE` 環境変数を `LOCAL` または `LAMBDA` に設定します。 これは、環境変数経由、または管理画面のデータベース変数から設定できます。 詳細は[ロジック関数のセットアップガイド](/l/ja/developers/self-host/capabilities/setup#logic-functions-available-drivers)を参照してください。
|
||||
|
||||
### 1クリックでDocker構成
|
||||
|
||||
#### ログインできない
|
||||
|
||||
@@ -1,396 +1,102 @@
|
||||
---
|
||||
title: アップグレードガイド
|
||||
icon: arrow-up-right-dots
|
||||
---
|
||||
|
||||
## 一般ガイドライン
|
||||
|
||||
**アップグレードプロセスを開始する前に、必ずデータベースのバックアップを作成してください**。以下のコマンドを実行します:`docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql`.
|
||||
**アップグレードを開始する前に、必ずデータベースをバックアップしてください**。次を実行してください:
|
||||
|
||||
バックアップを復元するには、次のコマンドを実行します:`cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}`.
|
||||
```bash
|
||||
docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql
|
||||
```
|
||||
|
||||
バックアップから復元:
|
||||
|
||||
```bash
|
||||
cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}
|
||||
```
|
||||
|
||||
Docker Composeを使用している場合、次の手順に従います:
|
||||
|
||||
1. Twentyを実行しているホスト上のターミナルで、Twentyを停止します:`docker compose down`
|
||||
1. Twentyを停止:`docker compose down`
|
||||
2. `docker-compose.yml` と同じディレクトリにある `.env` ファイルの `TAG` 値を変更します
|
||||
3. Twentyを起動:`docker compose up -d`
|
||||
|
||||
2. docker-compose近くにある.envファイルの`TAG`値を変更してバージョンをアップグレードします。 ( `v0.53` のように `major.minor` バージョンを使用することをお勧めします ) ( `v0.53` のように `major.minor` バージョンを使用することをお勧めします ) ( `v0.53` のように `major.minor` バージョンを使用することをお勧めします )
|
||||
サーバーは起動時に、必要なアップグレード用のマイグレーションを自動的に実行します。 手動のコマンドは不要です。
|
||||
|
||||
3. `docker compose up -d` でTwentyを再起動します。
|
||||
## クロスバージョンのアップグレード (v1.22+)
|
||||
|
||||
例えば、v0.33.0 から v0.35.0 に数バージョンアップグレードしたい場合は、インスタンスを順次アップグレードする必要があります。この例では、まず v0.33.0 から v0.34.0 、次に v0.34.0から v0.35.0 にアップグレードします。
|
||||
**v1.22** 以降、Twenty はクロスバージョンのアップグレードをサポートします。 サポートされている任意のバージョンから、中間バージョンを段階的に経ることなく、最新リリースへ直接移行できます。
|
||||
|
||||
**各アップグレードしたバージョンの後に壊れていないバックアップを必ず確認してください。**
|
||||
たとえば、v1.22 から v2.0 へ直接アップグレードすることが完全にサポートされています。
|
||||
|
||||
## バージョン固有のアップグレード手順
|
||||
## v2.5 以降へのアップグレード — 保存データ暗号化エンベロープ
|
||||
|
||||
## v1.0
|
||||
**v2.5** 以降、Twenty は保存時のシークレット(OAuth トークン、アプリケーション変数、署名用の秘密鍵、機密な設定値、TOTP シークレット)を、バージョン付きの `enc:v2:` エンベロープ内に格納し、`ENCRYPTION_KEY`(`ENCRYPTION_KEY` が未設定の場合は `APP_SECRET`)で暗号化します。
|
||||
|
||||
こんにちは、Twenty v1.0! 🎉
|
||||
v2.5 での最初の起動時には、既存の行を新しいエンベロープに**バックフィル**する低速なアップグレードコマンドが実行されます。 これらは冪等であり、中断してサーバーを再起動しても処理が中断地点から再開されますが、大規模なデータベースでは時間がかかる場合があります。 `upgrade:status` で進行状況を監視できます。
|
||||
|
||||
## v0.60
|
||||
バックフィルが最初からそのキーの下に行を書き込めるよう、v2.5 へのアップグレード**前に**専用の `ENCRYPTION_KEY` を設定しておく必要があります。 バックフィル後にキーを切り替えるには、[ローテーション](/l/ja/developers/self-host/capabilities/key-rotation)が必要です。
|
||||
|
||||
### パフォーマンス向上
|
||||
## シークレットおよび署名鍵のローテーション
|
||||
|
||||
メタデータ API とのすべてのやり取りは、特にオブジェクトのメタデータ操作およびワークスペース作成の処理において、パフォーマンス向上のために最適化されました。
|
||||
`ENCRYPTION_KEY` のローテーション、JWT 署名鍵のローテーション、漏えいした署名鍵の失効といった日常的な運用タスクについては、専用の[鍵ローテーションガイド](/l/ja/developers/self-host/capabilities/key-rotation)を参照してください。
|
||||
|
||||
キャッシング戦略を見直し、可能な場合はデータベースクエリよりもキャッシュヒットを優先するようにしました。これにより、メタデータAPIオペレーションのパフォーマンスが大幅に向上しました。
|
||||
## アップグレードステータスの確認
|
||||
|
||||
アップグレード後にランタイムの問題が発生した場合は、キャッシュをフラッシュして最新の変更と同期させる必要があるかもしれません。 twenty-serverコンテナ内で次のコマンドを実行します: twenty-serverコンテナ内で次のコマンドを実行します:
|
||||
`upgrade:status` コマンドを使用すると、インスタンスとワークスペースのマイグレーションの現在の状態を確認できます。 アップグレードの問題をデバッグしたり、サポートリクエストを提出する際に役立ちます。
|
||||
|
||||
サーバーコンテナから実行します:
|
||||
|
||||
```bash
|
||||
yarn command:prod cache:flush
|
||||
docker exec -it {server_container_name_or_id} yarn command:prod upgrade:status
|
||||
```
|
||||
|
||||
### v0.55
|
||||
出力例:
|
||||
|
||||
Twentyインスタンスをv0.55イメージにアップグレードします。
|
||||
```sh
|
||||
APP_VERSION: v1.23.0
|
||||
|
||||
もうコマンドを実行する必要はありません。新しいイメージがすべての必要な移行を自動的に行います。
|
||||
Instance
|
||||
Inferred version: 1.23.0
|
||||
Latest command: 1.23.0_DropWorkspaceVersionColumnFastInstanceCommand_1785000000000
|
||||
Status: Up to date
|
||||
Executed by: v1.23.0
|
||||
At: 2026-04-16T11:43:58.823Z
|
||||
|
||||
### `User does not have permission` エラー
|
||||
Workspace
|
||||
Apple (20202020-1c25-4d02-bf25-6aeccf7ea419)
|
||||
Inferred version: 1.23.0
|
||||
Latest command: 1.23.0_UpdateGlobalObjectContextCommandMenuItemsCommand_1780000005000
|
||||
Status: Up to date
|
||||
Executed by: v1.23.0
|
||||
At: 2026-04-16T11:44:09.361Z
|
||||
|
||||
アップグレード後にほとんどの要求で承認エラーが発生した場合は、キャッシュをフラッシュして最新の権限を再計算する必要があるかもしれません。
|
||||
Summary
|
||||
Instance: Up to date
|
||||
Workspaces: 1 up to date, 0 behind, 0 failed (1 total)
|
||||
```
|
||||
|
||||
あなたの `twenty-server` コンテナ内で次のコマンドを実行します:
|
||||
### オプション
|
||||
|
||||
| フラグ | 説明 |
|
||||
| ------------------------- | --------------------------------------------- |
|
||||
| `-w, --workspace-id <id>` | 特定のワークスペースに絞り込みます。 複数回指定できます。 |
|
||||
| `-f, --failed-only` | 最新の状態のワークスペースを非表示にし、遅れているものと失敗したエントリのみを表示します。 |
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
一部のワークスペースでアップグレードが失敗した場合、サーバーは失敗したステップを超えて先に進みません。 サーバーを再起動すると(`docker compose up -d`)、中断した地点からアップグレードを再試行します。
|
||||
|
||||
問題を迅速に特定するには、次を実行します:
|
||||
|
||||
```bash
|
||||
yarn command:prod cache:flush
|
||||
docker exec -it {server_container_name_or_id} yarn command:prod upgrade:status --failed-only
|
||||
```
|
||||
|
||||
この問題は特定のTwentyバージョンに固有であり、今後のアップグレードでは不要なはずです。
|
||||
これにより、遅れている、または失敗しているワークスペースのみが表示され、各失敗のエラーメッセージも併せて表示されます。
|
||||
|
||||
### v0.54
|
||||
## v1.22 以前
|
||||
|
||||
バージョン `0.53` 以降、手動の操作は必要ありません。
|
||||
|
||||
#### メタデータスキーマ廃止
|
||||
|
||||
データの取得を簡素化するために、`metadata` スキーマを `core` スキーマにマージしました。
|
||||
`upgrade` コマンド内の `migrate` コマンドステップを統合しました。 サーバーまたはワーカーコンテナ内で `migrate` を手動で実行することはお勧めしません。
|
||||
|
||||
### v0.53以降
|
||||
|
||||
`0.53` 以降、アップグレードは `DockerFile` 内でプログラム的に行われます。これはつまり、これ以降は手動でコマンドを実行する必要がないことを意味します。
|
||||
|
||||
メジャーバージョンを飛ばさずに順次インスタンスをアップグレードし続けることを確認してください(e.g. `0.43.3`から`0.44.0`へのアップグレードは許可されていますが、`0.43.1`から`0.45.0`へのアップグレードはできません)。そうしないと、ワークスペースのバージョンが同期しないことが原因でランタイムエラーや機能の欠如が発生する可能性があります。
|
||||
|
||||
ワークスペースが正しく移行されたかを確認するには、データベース内の `core.workspace` テーブル内でそのバージョンを確認できます。
|
||||
|
||||
それは常にあなたの現在のTwentyのインスタンスの `major.minor` バージョンの範囲内である必要があります。あなたのインスタンスバージョンは、管理パネル( `/settings/admin-panel` にあり、データベース内でユーザーが `canAccessFullAdminPanel` プロパティを持っている場合にアクセス可能)にまたは `twenty-server` コンテナ内で `echo $APP_VERSION` を実行することで確認できます。
|
||||
|
||||
非同期化されたワークスペースバージョンを修正するには、対応するTwentyのバージョンから関連するアップグレードガイドに従って順次アップグレードし、それが目的のバージョンに到達するまで続ける必要があります。
|
||||
|
||||
#### `auditLog` の削除
|
||||
|
||||
監査ログ標準オブジェクトを削除しました。これにより、この移行後のバックアップサイズが大幅に縮小する可能性があります。
|
||||
|
||||
### v0.51からv0.52
|
||||
|
||||
Twentyインスタンスをv0.52イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
#### 私は `0.52.0` と `0.52.6` の間のバージョンでブロックされたワークスペースを持っています。
|
||||
|
||||
残念ながら、`0.52.0` と `0.52.6` はdockerHubから完全に削除されました。
|
||||
残念ながら、`0.52.0` と `0.52.6` はdockerHubから完全に削除されました。
|
||||
データベースでワークスペースバージョンを手動で `0.51.0` に更新し、twentyバージョン `0.52.11` を使用して、その直上のアップグレードガイドに従ってアップグレードする必要があります。
|
||||
残念ながら、`0.52.0` と `0.52.6` はdockerHubから完全に削除されました。
|
||||
データベースでワークスペースバージョンを手動で `0.51.0` に更新し、twentyバージョン `0.52.11` を使用して、その直上のアップグレードガイドに従ってアップグレードする必要があります。
|
||||
|
||||
### v0.50からv0.51
|
||||
|
||||
Twentyインスタンスをv0.51イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
### v0.44.0からv0.50.0
|
||||
|
||||
Twentyインスタンスをv0.50.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
#### Docker-compose.ymlの変更
|
||||
|
||||
このバージョンには、`worker` サービスが `server-local-data` ボリュームにアクセスできるようにするための `docker-compose.yml` の変更が含まれています。
|
||||
ローカルの `docker-compose.yml` を[v0.50.0 docker-compose.yml](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml)に更新してください。
|
||||
ローカルの `docker-compose.yml` を[v0.50.0 docker-compose.yml](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml)に更新してください。
|
||||
ローカルの `docker-compose.yml` を[v0.50.0 docker-compose.yml](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml)に更新してください。
|
||||
|
||||
### v0.43.0からv0.44.0
|
||||
|
||||
Twentyインスタンスをv0.44.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
### v0.42.0からv0.43.0
|
||||
|
||||
Twentyインスタンスをv0.43.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
本バージョンでは、`docker-compose.yml`において`postgres:16`イメージへの切り替えも行いました。
|
||||
|
||||
#### ( オプション 1 ) データベース移行
|
||||
|
||||
既存のpostgres-spiloイメージを保持するのは問題ありませんが、docker-compose.ymlでバージョンを0.43.0に固定する必要があります。
|
||||
|
||||
#### ( オプション 2 ) データベース移行
|
||||
|
||||
データベースを新しいpostgres:16イメージに移行したい場合は、次の手順に従ってください:
|
||||
|
||||
1. 古いpostgres-spiloコンテナからデータベースをダンプします。
|
||||
|
||||
```
|
||||
docker exec -it twenty-db-1 sh
|
||||
pg_dump -U {YOUR_POSTGRES_USER} -d {YOUR_POSTGRES_DB} > databases_backup.sql
|
||||
exit
|
||||
docker cp twenty-db-1:/home/postgres/databases_backup.sql .
|
||||
```
|
||||
|
||||
ダンプファイルが空でないことを確認してください。
|
||||
|
||||
2. docker-compose.ymlを、新しいpostgres:16イメージを使用するようにアップグレードします。[docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml)を参照してください。
|
||||
|
||||
3. データベースを新しいpostgres:16コンテナへ復元します。
|
||||
|
||||
```
|
||||
docker cp databases_backup.sql twenty-db-1:/databases_backup.sql
|
||||
docker exec -it twenty-db-1 sh
|
||||
psql -U {YOUR_POSTGRES_USER} -d {YOUR_POSTGRES_DB} -f databases_backup.sql
|
||||
exit
|
||||
```
|
||||
|
||||
### v0.41.0からv0.42.0
|
||||
|
||||
Twentyインスタンスをv0.42.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.42
|
||||
```
|
||||
|
||||
**環境変数**
|
||||
|
||||
* 削除: `FRONT_PORT`, `FRONT_PROTOCOL`, `FRONT_DOMAIN`, `PORT`
|
||||
* 追加: `FRONTEND_URL`, `NODE_PORT`, `MAX_NUMBER_OF_WORKSPACES_DELETED_PER_EXECUTION`, `MESSAGING_PROVIDER_MICROSOFT_ENABLED`, `CALENDAR_PROVIDER_MICROSOFT_ENABLED`, `IS_MICROSOFT_SYNC_ENABLED`
|
||||
|
||||
### v0.40.0からv0.41.0
|
||||
|
||||
Twentyインスタンスをv0.41.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.41
|
||||
```
|
||||
|
||||
**環境変数**
|
||||
|
||||
* 削除: `AUTH_MICROSOFT_TENANT_ID`
|
||||
|
||||
### v0.35.0からv0.40.0
|
||||
|
||||
Twentyインスタンスをv0.40.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.40
|
||||
```
|
||||
|
||||
**環境変数**
|
||||
|
||||
* 追加: `IS_EMAIL_VERIFICATION_REQUIRED`, `EMAIL_VERIFICATION_TOKEN_EXPIRES_IN`, `WORKFLOW_EXEC_THROTTLE_LIMIT`, `WORKFLOW_EXEC_THROTTLE_TTL`
|
||||
|
||||
### v0.34.0からv0.35.0
|
||||
|
||||
Twentyインスタンスをv0.35.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.35
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.35` はすべてのワークスペースのデータ移行を行います。
|
||||
|
||||
**環境変数**
|
||||
|
||||
* `ENABLE_DB_MIGRATIONS` を `DISABLE_DB_MIGRATIONS` に置き換えました(デフォルト値は`false`で、設定する必要はないと思われます)
|
||||
|
||||
### v0.33.0からv0.34.0
|
||||
|
||||
Twentyインスタンスをv0.34.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.34
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.34` はすべてのワークスペースのデータ移行を行います。
|
||||
|
||||
**環境変数**
|
||||
|
||||
* 削除: `FRONT_BASE_URL`
|
||||
* 追加: `FRONT_DOMAIN`, `FRONT_PROTOCOL`, `FRONT_PORT`
|
||||
|
||||
フロントエンドURLの取り扱いを更新しました。
|
||||
フロントエンドURLの取り扱いを更新しました。
|
||||
フロントエンドURLの取り扱いを更新しました。
|
||||
フロントエンドURLは、`FRONT_DOMAIN`、`FRONT_PROTOCOL`、`FRONT_PORT` 変数を使用して設定できます。
|
||||
FRONT_DOMAIN が設定されていない場合、フロントエンドURLは `SERVER_URL` に戻されます。
|
||||
FRONT_DOMAIN が設定されていない場合、フロントエンドURLは `SERVER_URL` に戻されます。
|
||||
FRONT_DOMAIN が設定されていない場合、フロントエンドURLは `SERVER_URL` に戻されます。
|
||||
|
||||
### v0.32.0からv0.33.0
|
||||
|
||||
Twentyインスタンスをv0.33.0イメージにアップグレードします。
|
||||
|
||||
```
|
||||
yarn command:prod cache:flush
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.33
|
||||
```
|
||||
|
||||
`yarn command:prod cache:flush` コマンドはRedisキャッシュをフラッシュします。
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.33` はすべてのワークスペースのデータ移行を行います。
|
||||
`yarn database:migrate:prod` コマンドはデータベースへの移行を適用します。
|
||||
The `yarn command:prod upgrade-0.22` command will apply specific data transformations to adapt to the new object defaultRequestInstrumentationOptions.
|
||||
|
||||
このバージョンから、DB用のtwenty-postgresイメージは廃止され、twenty-postgres-spiloが代わりに使用されるようになりました。
|
||||
このバージョンから、DB用のtwenty-postgresイメージは廃止され、twenty-postgres-spiloが代わりに使用されるようになりました。
|
||||
このバージョンから、DB用のtwenty-postgresイメージは廃止され、twenty-postgres-spiloが代わりに使用されるようになりました。
|
||||
twenty-postgresイメージを使い続けたい場合は、docker-compose.yml内の`twentycrm/twenty-postgres:${TAG}`を`twentycrm/twenty-postgres`に置き換えるだけで済みます。
|
||||
|
||||
### v0.31.0からv0.32.0
|
||||
|
||||
Twentyインスタンスをv0.32.0イメージにアップグレードします。
|
||||
|
||||
**スキーマとデータ移行**
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.32
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.32` はすべてのワークスペースのデータ移行を行います。
|
||||
|
||||
**環境変数**
|
||||
|
||||
Redis接続の取り扱いを更新しました。
|
||||
|
||||
* 削除: `REDIS_HOST`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`
|
||||
* 追加: `REDIS_URL`
|
||||
|
||||
個々のRedis接続パラメーターの代わりに、新しい `REDIS_URL` 変数を使用するように `.env` ファイルを更新してください。
|
||||
|
||||
JWTトークンの取り扱いも簡素化しました。
|
||||
|
||||
* 削除: `ACCESS_TOKEN_SECRET`, `LOGIN_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `FILE_TOKEN_SECRET`
|
||||
* 追加: `APP_SECRET`
|
||||
|
||||
個々のトークンシークレットの代わりに、新しい `APP_SECRET` 変数を使用するように `.env` ファイルを更新してください(以前と同じシークレットを使用するか、新しいランダム文字列を生成して使用することができます)
|
||||
|
||||
**接続アカウント**
|
||||
|
||||
Googleメールとカレンダーを同期するために接続されたアカウントを使用する場合は、Google管理コンソールで[People API](https://developers.google.com/people)をアクティブにする必要があります。
|
||||
|
||||
### v0.30.0からv0.31.0
|
||||
|
||||
Twentyインスタンスをv0.31.0イメージにアップグレードします。
|
||||
|
||||
**スキーマとデータ移行**:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.31
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.31` はすべてのワークスペースのデータ移行を行います。
|
||||
|
||||
### v0.24.0からv0.30.0
|
||||
|
||||
Twentyインスタンスをv0.30.0イメージにアップグレードします。
|
||||
|
||||
**破壊的な変更**:
|
||||
パフォーマンスを向上させるために、TwentyはRedisキャッシュの設定を必要とするようになりました。 これを反映した[docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml)を更新しました。
|
||||
構成を更新し、環境変数を適切に更新してください:
|
||||
構成を更新し、環境変数を適切に更新してください:
|
||||
構成を更新し、環境変数を適切に更新してください:
|
||||
|
||||
```
|
||||
REDIS_HOST={your-redis-host}
|
||||
REDIS_PORT={your-redis-port}
|
||||
CACHE_STORAGE_TYPE=redis
|
||||
```
|
||||
|
||||
**スキーマとデータ移行**:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.30
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.30` はすべてのワークスペースのデータ移行を行います。
|
||||
|
||||
### v0.23.0からv0.24.0
|
||||
|
||||
Twentyインスタンスをv0.24.0イメージにアップグレードします。
|
||||
|
||||
次のコマンドを実行してください:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.24
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベース構造(コアおよびメタデータスキーマ)の移行を適用します
|
||||
`yarn command:prod upgrade-0.24` はすべてのワークスペースのデータ移行を行います。
|
||||
|
||||
### v0.22.0からv0.23.0
|
||||
|
||||
Twentyインスタンスをv0.23.0イメージにアップグレードします。
|
||||
|
||||
次のコマンドを実行してください:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.23
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベースへの移行を適用します。
|
||||
`yarn command:prod upgrade-0.23` は、アクティビティをタスク/ノートに移行するなど、データ移行を担当します。
|
||||
`yarn command:prod upgrade-0.23` は、アクティビティをタスク/ノートに移行するなど、データ移行を担当します。
|
||||
|
||||
### v0.21.0からv0.22.0
|
||||
|
||||
Twentyインスタンスをv0.22.0イメージにアップグレードします。
|
||||
|
||||
次のコマンドを実行してください:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.22
|
||||
```
|
||||
|
||||
`yarn database:migrate:prod` コマンドはデータベースへの移行を適用します。
|
||||
`yarn command:prod upgrade-0.23` は、アクティビティをタスク/ノートに移行するなど、データ移行を担当します。
|
||||
`yarn command:prod upgrade-0.22` コマンドは、新しいオブジェクトの defaultRequestInstrumentationOptions に適合させるため、特定のデータ変換を適用します。
|
||||
インスタンスが v1.22 より前の場合は、v1.22 に到達するまで、各メジャーのタグ付きバージョンを順に(v1.6 から v1.7、次に v1.7 から v1.8、…)段階的にアップグレードする必要があります。 そこからは、最新バージョンに直接アップグレードできます。
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: AI
|
||||
description: Twenty はどのように AI を活用して、CRM の利用体験を向上させるか。
|
||||
icon: robot
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
Twenty は AI を CRM に直接統合し、単なる見せかけではなく、あなたのデータモデルと権限システムの中で機能するツールとして提供します。
|
||||
|
||||
## AI チャットボット
|
||||
|
||||
データについて自然言語で質問できます。 AI チャットボットは CRM のレコードに対してクエリを実行し、情報を要約し、複雑なフィルターを作成しなくても、探しているものを見つけるのを支援します。
|
||||
|
||||
<VimeoEmbed videoId="1185511734" title="AI チャットボット" />
|
||||
|
||||
## AIエージェント
|
||||
|
||||
AI エージェントはチャットにとどまらず、自律的に複数ステップのタスクを実行できます。
|
||||
|
||||
* 外部ソースのデータでレコードを充実させる
|
||||
* フォローアップメールを下書きして送信する
|
||||
* パイプラインの健全性を分析し、リスクの高い案件にフラグを立てる
|
||||
* 受信データを処理し、適切なチームへ振り分ける
|
||||
|
||||
エージェントは既存のワークフロー内で動作するため、AI を手動承認、条件付きロジック、外部 API コールと組み合わせることができます。
|
||||
|
||||
## 権限と安全性
|
||||
|
||||
Twenty の AI は、あなたの権限モデルを順守します。 エージェントは、ユーザー(またはロール)に閲覧権限があるオブジェクトとフィールドにしかアクセスできません。 AI が関与している場合でも、機密データは保護されたままです。
|
||||
|
||||
<Card title="AI の完全ガイド" icon="arrow-right" href="/l/ja/user-guide/ai/overview">
|
||||
チャットボット、エージェント、権限管理に関する詳細なリファレンス。
|
||||
</Card>
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: アプリ
|
||||
icon: cube
|
||||
description: コードで Twenty を拡張しましょう。カスタムオブジェクト、サーバーサイドロジック、UI コンポーネント、AI エージェントをすべて TypeScript パッケージとして追加できます。
|
||||
---
|
||||
|
||||
ほとんどの CRM は設定パネルを提供します。 Twenty はプラットフォームを提供します。 アプリは、開発者が UI で提供される範囲を超えて Twenty を拡張する方法です。データモデル、サーバーサイドロジック、UI コンポーネント、AI 機能をコードとして定義し、それらを 1 つ以上のワークスペースにデプロイできます。
|
||||
|
||||
## アプリが存在する理由
|
||||
|
||||
ワークフローはノーコードの自動化を担います。 しかし、コードが必要なものもあります。たとえば、カスタムの料金算出エンジン、独自のエンリッチメントパイプライン、レコードの更新ごとに実行されるコンプライアンスチェック、社内ツールからデータを取得するカスタム UI パネルなどです。
|
||||
|
||||
アプリを使えば、これらを一級の拡張機能として構築できます。外部から API と通信する壊れやすいスクリプトではなく、型システム、権限モデル、UI へ完全にアクセスできる、プラットフォーム上で実行されるコードとして実現できます。
|
||||
|
||||
## アプリで定義できるもの
|
||||
|
||||
アプリは、`twenty-sdk` を使って **エンティティ** を宣言する TypeScript パッケージです。
|
||||
|
||||
| エンティティ | 機能 |
|
||||
| ---------------- | -------------------------------------------------------------------------- |
|
||||
| **オブジェクトとフィールド** | 既存オブジェクト上の新しいデータテーブルとフィールド — 組み込みのものと同様に扱われます |
|
||||
| **ロジック関数** | HTTP ルート、cron スケジュール、またはデータベースイベントによってトリガーされるサーバーサイドの TypeScript |
|
||||
| **フロントコンポーネント** | サンドボックス化された React コンポーネントで、Twenty の UI(サイドパネル、ウィジェット、コマンドメニュー)内でレンダリングされます |
|
||||
| **スキルとエージェント** | AI 機能 — 再利用可能な指示と自律型アシスタント |
|
||||
| **ビューとナビゲーション** | 事前設定済みのリストビューとサイドバーのメニュー項目 |
|
||||
|
||||
すべてはビルド時に AST 解析によって検出されます — 設定ファイルも登録用のボイラープレートも不要です。 任意の `.ts` ファイルに `export default defineObject(...)` を記述するだけで、SDK がそれを検出します。
|
||||
|
||||
## 実行方式
|
||||
|
||||
* **ロジック関数**は、ホストからサンドボックス化された分離された Node.js プロセスで実行されます。 それらは、アプリのロール権限の範囲にスコープされた型付き API クライアントを通じてデータにアクセスします。
|
||||
* **フロントコンポーネント**は Remote DOM を用いた Web Workers 内で実行されます—メインページからサンドボックス化されつつ、ネイティブの DOM 要素(iframe ではありません)をレンダリングします。
|
||||
* **権限**は API レベルで強制されます。 アプリは自分のロールで許可されたものだけを参照できます。
|
||||
|
||||
## 開発者エクスペリエンス
|
||||
|
||||
ローカル環境で TypeScript プロジェクトとしてアプリを作成します。 CLI がソースファイルを監視し、実行中の Twenty サーバーとライブ同期します — ファイルを編集すると、1 秒以内に UI に変更が反映されます。 スキーマが変更されると、型付き API クライアントは自動的に再生成されます。 準備ができたら、`yarn twenty app:publish --private` で本番サーバーにプッシュするか、`yarn twenty app:publish` でアプリを npm と Twenty マーケットプレイスに掲載します。
|
||||
|
||||
<Card title="最初のアプリを構築する" icon="arrow-right" href="/l/ja/developers/extend/apps/getting-started">
|
||||
3 フェーズのウォークスルー — 雛形の作成、ローカルサーバーの実行、変更内容の同期。
|
||||
</Card>
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: カレンダーとメール
|
||||
description: メールとカレンダーをTwentyと同期します。
|
||||
icon: envelope
|
||||
---
|
||||
|
||||
Twentyは既存のツールと連携し、手動でデータ入力を行わなくてもCRMが常に最新の状態に保たれるようにします。
|
||||
|
||||
## メール同期
|
||||
|
||||
**Google Workspace** または **Microsoft 365** のメールボックスを接続します。 接続後:
|
||||
|
||||
* メールは、自動的に該当する会社および担当者レコードに関連付けられます
|
||||
* 各レコードのタイムラインでメールスレッド全体を確認できます
|
||||
* Twentyから直接メールを送信できます
|
||||
* 1ユーザーあたり複数のメールボックスに対応しています
|
||||
|
||||
インポートする内容を制御できます。日付範囲や送信者でフィルタリングして、不要なメールの取り込みを防げます。
|
||||
|
||||
## カレンダー同期
|
||||
|
||||
接続したアカウントからカレンダーイベントが自動的に同期されます。 イベントは関連するCRMレコードに表示され、各担当者や会社とのやり取りを完全に把握できます。
|
||||
|
||||
## 統合
|
||||
|
||||
メールとカレンダーにとどまらず、Twentyは次の方法で外部ツールと連携します。
|
||||
|
||||
| メソッド | ユースケース |
|
||||
| ------------------- | -------------------------------------- |
|
||||
| **API** | GraphQL または REST API を使ってカスタム連携を構築できます |
|
||||
| **ウェブフック** | レコードが変更された際に、リアルタイム通知を外部システムへ送信できます |
|
||||
| **Zapier** | コード不要で5,000以上のアプリと連携できます |
|
||||
| **ワークフローHTTPアクション** | 自動化されたワークフローの一部として、任意の外部APIを呼び出せます |
|
||||
|
||||
## カスタムアプリ
|
||||
|
||||
開発者はTwenty上に本格的なアプリを構築し、カスタムUIやサーバーサイドロジック、深い連携機能を追加できます。 アプリはコミュニティ向けに公開することも、非公開のままにすることもできます。
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="カレンダーとメールのガイド" icon="envelope" href="/l/ja/user-guide/calendar-emails/overview">
|
||||
メール同期とカレンダー同期を設定し、問題をトラブルシュートします。
|
||||
</Card>
|
||||
<Card title="APIと拡張機能" icon="plug" href="/l/ja/developers/extend/api">
|
||||
Twenty APIを使ってカスタム連携を構築します。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: ダッシュボード
|
||||
description: カスタムダッシュボードを使用してパフォーマンスを追跡し、CRMデータを可視化します。
|
||||
icon: chart-bar
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
ダッシュボードを使うと、パイプラインの健全性、チームのパフォーマンス、収益トレンドなど、ビジネス指標をリアルタイムで可視化し、追跡したいあらゆる項目を確認できます。
|
||||
|
||||
<VimeoEmbed videoId="1185511768" title="ダッシュボード" />
|
||||
|
||||
## ウィジェット
|
||||
|
||||
各ダッシュボードはウィジェットで構成されています。 ウィジェットは、CRMデータに紐づいた単一のチャートまたは指標です。 次の項目を設定できます:
|
||||
|
||||
* **チャートタイプ** — 棒グラフ、折れ線グラフ、円グラフ、数値など
|
||||
* **データソース** — データモデル内の任意のオブジェクト(標準またはカスタム)
|
||||
* **フィルター** — 特定のレコード、日付範囲、セグメントに絞り込み
|
||||
* **集計** — 任意の数値フィールドに対する件数、合計、平均、最小、最大
|
||||
* **グルーピング** — 選択したフィールド、日付、またはリレーション別に内訳表示
|
||||
|
||||
## 追跡できる内容
|
||||
|
||||
* ステージ別のパイプラインの価値
|
||||
* クローズ済み案件数の推移
|
||||
* ソース別の平均案件規模
|
||||
* タスク完了率
|
||||
* 任意のオブジェクトに対するカスタム指標
|
||||
|
||||
## 共有
|
||||
|
||||
ダッシュボードはワークスペースレベルで管理され、チームの全員が閲覧できます。 ウィジェットをグリッドレイアウト上に配置してサイズを変更し、チームに最適なビューを作成できます。
|
||||
|
||||
<Card title="ダッシュボードの詳細ガイド" icon="arrow-right" href="/l/ja/user-guide/dashboards/overview">
|
||||
ダッシュボードの作成、ウィジェットの設定、およびチャート設定に関する詳細なリファレンスです。
|
||||
</Card>
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: データモデル
|
||||
description: Twenty が、オブジェクト、フィールド、リレーションを使ってデータをどのように構造化するかを理解しましょう。
|
||||
icon: database
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
Twenty では、すべてが **オブジェクト** と **フィールド** を中心に構築されています。これらがデータモデルの構成要素です。
|
||||
|
||||
## オブジェクト
|
||||
|
||||
オブジェクトは、データを保持するテーブルです。 Twenty には標準オブジェクトがあらかじめ用意されています:
|
||||
|
||||
* **Companies** — ビジネス上の取引先となる組織
|
||||
* **People** — 個々の連絡先
|
||||
* **Opportunities** — パイプライン上の商談
|
||||
* **Tasks** — チーム向けのアクション項目
|
||||
* **Notes** — レコードに紐づけられた自由形式テキスト
|
||||
|
||||
また、ビジネスニーズに応じて、プロジェクト、サポートチケット、商品、契約など、あらゆるものに対して **カスタムオブジェクト** を作成できます。
|
||||
|
||||
## フィールド
|
||||
|
||||
フィールドは、各オブジェクトに属するプロパティです。 Twenty は、幅広いフィールドタイプをサポートしています:
|
||||
|
||||
| カテゴリ | タイプ |
|
||||
| ------ | ----------------------------------------- |
|
||||
| **基本** | テキスト、数値、ブール値、日付、通貨、評価、選択 |
|
||||
| **複合** | 住所(番地、市区町村、都道府県、郵便番号)、氏名、リンク、電話番号、メールアドレス |
|
||||
| **特殊** | リレーション、ファイル添付、JSON、アクター(作成/更新したユーザー) |
|
||||
|
||||
すべてのオブジェクトには、自動的にシステムフィールド `id`、`createdAt`、`updatedAt`、`createdBy`、`position` が付与されます。
|
||||
|
||||
## 関連
|
||||
|
||||
オブジェクト同士は、リレーションを通じて接続されます。 1 つの Company には多数の People が紐づき、1 つの Opportunity は 1 つの Company に属する、といった形です。 あらゆるオブジェクト間で、カスタムリレーションを作成できます。多対多リレーションも含まれます。
|
||||
|
||||
## これが強力な理由
|
||||
|
||||
あらかじめ定義されたオブジェクトとフィールドに制限される従来型の CRM とは異なり、Twenty では、ビジネスの実態に合わせて、データを自在にモデリングできます。 カスタムオブジェクトも組み込みオブジェクトと同等の優先度で扱われ、API エンドポイント、ビュー、権限、ワークフロートリガーなどを利用できます。
|
||||
|
||||
<Card title="データモデルを詳しく見る" icon="arrow-right" href="/l/ja/user-guide/data-model/overview">
|
||||
オブジェクト、フィールド、リレーション、およびその設定方法に関する完全なリファレンス。
|
||||
</Card>
|
||||
|
||||
<VimeoEmbed videoId="1185511827" title="データモデル" />
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: 用語集
|
||||
icon: book
|
||||
description: Twenty 全体を通して使用される主要な用語。
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
API(アプリケーションプログラミングインターフェース)は、Twentyを他のソフトウェアシステムと接続し、カスタム統合を構築することを可能にします。
|
||||
|
||||
## アプリ
|
||||
|
||||
アプリは、データモデルやロジック関数を定義できる、コードとして構築されたカスタム拡張機能です。 これにより、開発者は複数のワークスペースにデプロイ可能な再利用可能なカスタマイズを作成できます。
|
||||
|
||||
## コードアクション
|
||||
|
||||
コードアクションは、データの変換、計算、組み込みアクションでは実現できない複雑なロジックの実行のために、カスタムの JavaScript を記述できるワークフローのステップです。
|
||||
|
||||
## コマンドメニュー
|
||||
|
||||
コマンドメニューは、`Cmd + K`(Macの場合)、`Ctrl + K`(Windowsの場合)で開くことができるクイックアクセスインターフェースであり、操作を実行したり、レコードを作成したり、ワークスペースを効率的に移動したりすることを可能にします。
|
||||
|
||||
## Company と People
|
||||
|
||||
CRMには基本的な2つの種類のレコードがあります:
|
||||
|
||||
* `Company`は企業や組織を表します。
|
||||
* `People`はあなたの企業の現在および潜在的な顧客を表します。
|
||||
|
||||
## カスタムフィールド
|
||||
|
||||
カスタムフィールドは、ビジネスのニーズとプロセスに特化した情報をキャプチャするために作成されたデータフィールドです。
|
||||
|
||||
## データモデル
|
||||
|
||||
データモデルとは、CRMで情報がどのように編成されているかを定義する構造であり、どのオブジェクトが存在するか、それらのプロパティ(フィールド)、およびそれらがどのように関連しているかを含みます。
|
||||
|
||||
## お気に入り
|
||||
|
||||
お気に入りとは、すぐにアクセスできるようにマークされたレコードであり、サイドバーに表示され、重要なデータへの即時ナビゲーションを可能にします。
|
||||
|
||||
## フィールド
|
||||
|
||||
フィールドとは、エンティティに特定のデータが保存される特定の領域を指します。
|
||||
|
||||
## イテレータ
|
||||
|
||||
イテレータは、アイテムの配列をループし、リスト内の各アイテムに対して後続のアクションを実行するワークフローアクションです。
|
||||
|
||||
## カンバン
|
||||
|
||||
`Kanban`は、カードと列を使ってビジネスプロセスを視覚的に追跡する方法です。 各列はプロセスの各ステージ(例:新規、進行中、成約、失注)を表し、進行に応じてレコードをこれらのステージへ移動します。
|
||||
|
||||
## オブジェクト
|
||||
|
||||
オブジェクトは、CRMで特定の種類のエンティティを表すデータ構造です(例:People、Companies、Opportunities)。 オブジェクトには、標準(ビルトイン)とカスタム(ユーザー作成)があります。
|
||||
|
||||
## 商談
|
||||
|
||||
Twenty CRMの商談は、アカウントや連絡先との潜在的な取引や販売を指します。
|
||||
|
||||
## レコード
|
||||
|
||||
レコードとは、特定のオブジェクトのインスタンスを示します。例:特定のアカウントまたは連絡先。
|
||||
|
||||
## リレーションフィールド
|
||||
|
||||
リレーションフィールドは異なるオブジェクト間の接続を作成し、レコード同士をリンクできます(例:人を会社に関連付ける)。
|
||||
|
||||
## 標準フィールド
|
||||
|
||||
標準フィールドは、デフォルトでオブジェクトに付属する事前に構築されたデータフィールドであり、すべてのワークスペースで共通の機能を提供します。
|
||||
|
||||
## タスク
|
||||
|
||||
Twenty CRMのタスクは、連絡先、アカウント、または商談に関連する、担当者に割り当てられるアクティビティです。
|
||||
|
||||
## トリガー
|
||||
|
||||
トリガーはワークフローの開始点、すなわち自動化を開始するイベントまたは条件です。 例としては、レコードの作成、レコードの更新、ウェブフック、スケジュールされた時刻などがあります。
|
||||
|
||||
## ビュー
|
||||
|
||||
ビューを使用してレコードの表示をカスタマイズし、フィルター、レイアウト、並べ替えオプションを各ビューに設定することができます。
|
||||
|
||||
## アップサート
|
||||
|
||||
アップサートは「update」と「insert」を組み合わせた操作で、一致が見つかった場合は既存のレコードを更新し、一致が存在しない場合は新しいレコードを作成します。
|
||||
|
||||
## ウェブフック
|
||||
|
||||
ウェブフックは、特定のイベントが発生したときにTwentyから他のアプリケーションへ送信される自動メッセージで、リアルタイムのデータ同期を可能にします。
|
||||
|
||||
## ワークフロー
|
||||
|
||||
ワークフローは特定の条件に基づいてアクションをトリガーする自動プロセスであり、繰り返しのタスクやビジネスプロセスを自動化するのに役立ちます。
|
||||
|
||||
## ワークスペース
|
||||
|
||||
`Workspace`は通常、Twentyを使用している企業を表します。 そこには、あなたやチームメンバーがTwentyに追加したすべてのレコードとデータが保存されます。
|
||||
ワークスペースには、通常、従業員のメールアドレスに使われる会社のドメイン名が1つ設定されています。
|
||||
|
||||
## ワークスペースメンバー
|
||||
|
||||
ワークスペースメンバーは、ワークスペースにアクセスできるあなたのチームのTwentyユーザーです。 レコードの所有者または担当者として割り当てることができます。
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: レイアウト
|
||||
description: Twentyでのレコードのナビゲーション、閲覧、表示方法。
|
||||
icon: table-columns
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## メインレイアウト
|
||||
|
||||
画面の中央は、あなたのレコードが存在する場所です。人物、企業、商談、タスク、メモ、ダッシュボード、ワークフロー、および任意のカスタムオブジェクトがここにあります。 ここでレコードを表示、編集、削除でき、新しいビューを作成できます。
|
||||
|
||||
<img src="/images/user-guide/home/main-layout.png" style={{width:'100%'}} />
|
||||
|
||||
## ナビゲーションバー
|
||||
|
||||
左のサイドバーには、次のものがあります。
|
||||
|
||||
* **ワークスペース切り替え** — ワークスペースを切り替えるか、新しいワークスペースを作成します(上部のドロップダウン)
|
||||
* **検索** — `/` を押してすぐにフォーカスし、すべてのオブジェクトを横断して検索します
|
||||
* **設定** — 左上からアクセス
|
||||
* **お気に入り** — 固定されたビュー。ユーザーごとに固有
|
||||
* **オブジェクトショートカット** — 人物、企業、商談 などへのクイックアクセス
|
||||
* **ワークフロー** — 自動化を作成
|
||||
|
||||
ドラッグして項目の順序を変更し、関連するオブジェクトをグループ化するフォルダーを作成し、使用しないものは非表示にします。
|
||||
|
||||
<img src="/images/user-guide/home/navigation-bar.png" style={{width:'100%'}} />
|
||||
|
||||
## コマンドメニュー
|
||||
|
||||
`Cmd+K`(Mac)または `Ctrl+K`(Windows)を押すか、右上の三点リーダーをクリックします。 ここから次のことができます:
|
||||
|
||||
* 新しいレコードを作成
|
||||
* CSVでデータをインポートおよびエクスポート
|
||||
* 新しいビューを作成
|
||||
* 削除されたレコードにアクセスできます(Twenty はソフト削除とハード削除をサポートしています)
|
||||
* ワークスペースをナビゲートするためのキーボードショートカットを確認
|
||||
|
||||
<img src="/images/user-guide/home/command-menu.png" style={{width:'100%'}} />
|
||||
|
||||
## 検索
|
||||
|
||||
コマンドメニュー、ナビゲーションバー上部、または `/` を押すことでアクセスできます。 検索はすべてのオブジェクトを横断して機能します。
|
||||
|
||||
<img src="/images/user-guide/home/search-bar.png" style={{width:'100%'}} />
|
||||
|
||||
## サイドパネル
|
||||
|
||||
レコードをクリックすると、右側にサイドパネルが開きます。現在のページから離れることなく、レコードの主要な情報をすばやく概観できます。 **Open** をクリックして完全なレコードページに移動します。
|
||||
|
||||
<img src="/images/user-guide/home/side-panel.png" style={{width:'100%'}} />
|
||||
|
||||
## ビュー
|
||||
|
||||
すべてのオブジェクトで複数のビューをサポートしており、オブジェクトごとに無制限に作成できます。 左上のドロップダウンを使用してビューを切り替えます。
|
||||
|
||||
* **テーブル** — 表計算シート形式の行と列。グループ化、インライン編集、列のカスタマイズが可能
|
||||
* **カンバン** — 選択フィールド別に整理されたカードをドラッグ&ドロップで操作。パイプラインに最適
|
||||
* **カレンダー** — 日付フィールドに基づいてレコードをプロットし、時間軸に沿った計画が可能
|
||||
|
||||
各ビューは、フィルター、並べ替え、フィールドの可視性を個別に保存します。 ビューをワークスペースで共有することも、非公開のままにすることもできます。 ビューをお気に入りに登録して、サイドバーからすばやくアクセスできます。
|
||||
|
||||
<img src="/images/user-guide/home/view-menu.png" style={{width:'100%'}} />
|
||||
|
||||
## レコードページ
|
||||
|
||||
レコードを開くと、詳細ページは設定可能な**タブ**と**ウィジェット**で構成されます。 グリッド上でウィジェットを追加、削除、並べ替え、サイズ変更できます。フィールド、関連レコード、メール、タイムライン、タスク、ノート、ファイル、チャート、iframe などがあります。 各オブジェクトタイプには固有のレイアウトがあります。
|
||||
|
||||
<Card title="レイアウトの完全ガイド" icon="arrow-right" href="/l/ja/user-guide/layout/overview">
|
||||
ナビゲーション、ビュー、レコードページについての詳細なリファレンスとハウツー。
|
||||
</Card>
|
||||
|
||||
<VimeoEmbed videoId="1185511790" title="レイアウト" />
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: ワークフロー
|
||||
description: Twenty のビジュアルワークフロービルダーで、ビジネスプロセスを自動化しましょう。
|
||||
icon: bolt
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
ワークフローを使うと、繰り返しの作業を自動化し、コードを書くことなく(もちろん、必要であればコードを書くこともできます)Twenty を外部ツールと連携できます。
|
||||
|
||||
<VimeoEmbed videoId="1185511711" title="ワークフロー" />
|
||||
|
||||
## ワークフローの仕組み
|
||||
|
||||
すべてのワークフローは、次の 3 つの要素で構成されます。
|
||||
|
||||
1. **トリガー** — ワークフローを開始する条件
|
||||
2. **ステップ** — 次に行われる処理(1 つ以上のアクションの連続)
|
||||
3. **変数** — ステップ間を流れるデータ
|
||||
|
||||
## トリガー
|
||||
|
||||
| トリガー | 実行タイミング |
|
||||
| ------------ | ----------------------------------------- |
|
||||
| **レコードイベント** | レコードが作成、更新、削除、またはアップサートされたとき |
|
||||
| **手動** | ユーザーがボタンをクリックしたとき(単一レコード、複数レコード、またはグローバル) |
|
||||
| **スケジュール** | 一定間隔での実行(cron 構文) |
|
||||
| **ウェブフック** | 外部システムが HTTP POST を送信したとき |
|
||||
|
||||
## アクション
|
||||
|
||||
ワークフローでは、次のあらゆる組み合わせを連結できます。
|
||||
|
||||
* **レコード操作** — レコードの作成、更新、検索、削除、またはアップサート
|
||||
* **メール送信** — 接続済みアカウントからメールを送信または下書き保存
|
||||
* **HTTP request** — 任意の外部 API を呼び出す
|
||||
* **Code** — 複雑なロジックのためにカスタム JavaScript を実行
|
||||
* **ブランチ** — if/else 条件でワークフローのパスを分岐
|
||||
* **Iterator** — データ配列をループ処理
|
||||
* **AI Agent** — AI エージェントにデータ処理を自律的に実行させる
|
||||
* **遅延** — 続行する前に待機
|
||||
* **フォーム** — ワークフローの途中でユーザー入力を収集
|
||||
|
||||
## 構築できるもの
|
||||
|
||||
* 案件が特定のステージに到達したときに Slack アラートを送信
|
||||
* 新しいコンタクトに外部 API のデータを自動で付加
|
||||
* 停滞している商談を検知して担当者に通知
|
||||
* Twenty と課金システム間でデータを同期
|
||||
* レコードデータから PDF や請求書を生成
|
||||
* 特定の条件に一致する受信メールに自動返信
|
||||
|
||||
<Card title="ワークフローの完全ガイド" icon="arrow-right" href="/l/ja/user-guide/workflows/overview">
|
||||
トリガー、アクション、変数、および実際の自動化レシピに関する詳細なリファレンス。
|
||||
</Card>
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: なぜ Twenty なのか
|
||||
icon: heart
|
||||
---
|
||||
|
||||
import { CardTitle } from "/snippets/card-title.mdx"
|
||||
|
||||
あなたはこれまで、導入は簡単だが変更がほぼ不可能なソフトウェアか、高い柔軟性はあるもののセットアップに数カ月かかるソフトウェアか、そのどちらかを選ばざるをえませんでした。 Twenty は第3の選択肢です。**本番環境ですぐ使える、使いながら自在に作り替えられる CRM。**
|
||||
|
||||
## Twenty が特別な理由
|
||||
|
||||
Twenty は「設定して使うプロダクト」ではなく、「その上に構築できるプラットフォーム」です。
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card img="/images/user-guide/halftone/ai.png">
|
||||
<CardTitle>エージェントのために構築</CardTitle>
|
||||
エージェントは、適切な権限であなたのデータモデル内で動作します。 スキル、ツール、MCP。
|
||||
</Card>
|
||||
<Card img="/images/user-guide/halftone/permissions.png">
|
||||
<CardTitle>セキュアな拡張性</CardTitle>
|
||||
セキュアな基盤の上で、vibe-coded なツールと同等の柔軟性を実現します。
|
||||
</Card>
|
||||
<Card img="/images/user-guide/halftone/dev-api.png">
|
||||
<CardTitle>モダンなスタック</CardTitle>
|
||||
React、TypeScript。 あなたのチームは、すでに Twenty を拡張する方法を知っています。 独自仕様の言語も、ゲートキーピングもありません。
|
||||
</Card>
|
||||
<Card img="/images/user-guide/halftone/dev-self-host.png">
|
||||
<CardTitle>ロックインなし</CardTitle>
|
||||
オープンソースのコア、セルフホスト可能、いつでもデータをエクスポートできます。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Twenty は誰のためのものか
|
||||
|
||||
* **Salesforce を置き換えたい大企業** — チームはツールを活用するよりも、その制約と戦うことに多くの時間を費やしています。
|
||||
ロックインされていると感じ、コストは上がり続けています。
|
||||
* **技術系ファウンダーが率いるスタートアップ** — スプレッドシートや Notion では、すでに限界を迎えています。 大きな野心を持ち、それに合わせてスケールできる CRM を求めています。
|
||||
* **優位性を求める GTM チーム** — 独自のリードスコアリングや独自のエンリッチメント、独自のアウトバウンドワークフローを構築したいチーム。
|
||||
* **プライバシーを重視する組織** — セルフホストし、データを最初から最後まで自分たちで保有する必要があります。 それが規制上の理由であれ、契約上の理由であれ、あるいは単なる信念であれ。
|
||||
* **Salesforce パートナー** —
|
||||
あなたの開発者は、クライアントにライセンス費用の値上げを説明することにうんざりしています。
|
||||
より良い DX を実現し、クライアント向けプロジェクトを、より低いライセンスコストで、より速く提供したいと考えています。
|
||||
* **Web 開発代理店** — チームは TypeScript、React、PostgreSQL に精通しています。 Twenty は、APEX を学んだり独自の認定を取得したりすることなく、CRM 市場への扉を開きます。
|
||||
|
||||
## Twenty が向かない場合
|
||||
|
||||
* **一切気にせずに済む CRM を求めるチーム。** Twenty は、自分たちのツールに常に向き合い、時間をかけて作り込んでいくチームにこそ報いるプロダクトです。 完全マネージドなものを望むのであれば、Pipedrive や HubSpot の方が適しているでしょう。
|
||||
* **今すぐ何百もの事前構築済み連携が必要な企業。** Twenty のエコシステムは急速に成長していますが、Salesforce や HubSpot ほどの広がりには、まだ達していません。 足りない部分を自分で構築することに抵抗がなければ、きっと気に入っていただけます。 そうでなければ、もう 1 年お待ちください。
|
||||
* **ツールを実際に使う現場ではなく、役員会でツールが選定される組織。** 私たちは高級ディナーやエグゼクティブ向けブリーフィングは行いません。 自分たちのツールを自分たちで選ぶ権限を持つチームとともに勝ちたいと考えています。
|
||||
|
||||
<Card title="準備はできましたか?" icon="rocket" href="/l/ja/getting-started/quickstart">
|
||||
クラウド環境でもセルフホスト環境でも、5 分以内に Twenty をセットアップしましょう。
|
||||
</Card>
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: 主な機能
|
||||
description: Twenty が実現できるすべてのことを紹介します — カスタムデータモデルから AI を活用した自動化まで。
|
||||
icon: star
|
||||
---
|
||||
|
||||
import { CardTitle } from "/snippets/card-title.mdx"
|
||||
|
||||
Twenty は、フル機能を備えた CRM プラットフォームです。 それを使って、次のようなものを構築できます。
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card href="/l/ja/user-guide/data-model/overview" img="/images/user-guide/halftone/data-model.png">
|
||||
<CardTitle>カスタムデータモデル</CardTitle>
|
||||
ビジネスに必要なデータ構造を、正確に定義できます。
|
||||
<br />カスタムオブジェクトを作成し、20 を超えるフィールドタイプでカスタムフィールドを追加し、任意のオブジェクト同士の関係を構築できます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/views-pipelines/overview" img="/images/user-guide/halftone/layout.png">
|
||||
<CardTitle>ビューとパイプライン</CardTitle>
|
||||
テーブルビュー、かんばんボード、カレンダービューを切り替えて利用できます。
|
||||
<br />AND/OR ロジックでフィルターし、複数フィールドでソートし、レコードをグループ化して、カスタムビューとして保存できます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/workflows/overview" img="/images/user-guide/halftone/workflows.png">
|
||||
<CardTitle>ワークフローと自動化</CardTitle>
|
||||
コードを書かずに、あらゆるビジネスプロセスを自動化できます。
|
||||
<br />レコードの変更、スケジュール、手動アクション、受信 Webhook をトリガーにしてワークフローを実行できます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/calendar-emails/overview" img="/images/user-guide/halftone/calendar-emails.png">
|
||||
<CardTitle>カレンダーとメールの同期</CardTitle>
|
||||
Google Workspace または Microsoft 365 アカウントを接続できます。
|
||||
<br />メールとカレンダーイベントは、関連する CRM レコード上に自動で表示されます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/ai/overview" img="/images/user-guide/halftone/ai.png">
|
||||
<CardTitle>AI</CardTitle>
|
||||
CRM 内で自律的に動作する AI エージェントが、質問への回答、レコードの拡充、権限モデルに基づく複数ステップのタスクの実行を行います。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/dashboards/overview" img="/images/user-guide/halftone/dashboards.png">
|
||||
<CardTitle>ダッシュボードとレポート</CardTitle>
|
||||
リアルタイムウィジェットでカスタムダッシュボードを構築できます。
|
||||
<br />設定可能なチャートとフィルターにより、パイプラインの指標、チームのパフォーマンス、ビジネスの KPI を追跡できます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/permissions-access/overview" img="/images/user-guide/halftone/permissions.png">
|
||||
<CardTitle>権限とアクセス制御</CardTitle>
|
||||
オブジェクト、フィールド、個々のレコードなど、あらゆるレベルでロールベースのアクセス制御を行えます。
|
||||
<br />SAML または OIDC を使って SSO を構成できます。 監査ログで、誰が何をしたかを追跡できます。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/extend/api" img="/images/user-guide/halftone/dev-api.png">
|
||||
<CardTitle>API と拡張性</CardTitle>
|
||||
カスタムデータモデルに適応する、開発者優先の API を提供します。
|
||||
<br />GraphQL と REST の両方のエンドポイントを備え、ワークスペースごとに自動生成されたドキュメントが用意されています。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/user-guide/data-migration/overview" img="/images/user-guide/halftone/data-migration.png">
|
||||
<CardTitle>データのインポートとエクスポート</CardTitle>
|
||||
CSV ファイルまたは API 経由でデータをインポートできます。
|
||||
<br />フィールドマッピング、重複検出、エラーハンドリングが組み込まれています。 必要なときにいつでもデータをエクスポートでき、ロックインはありません。
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ja/developers/self-host/self-host" img="/images/user-guide/halftone/dev-self-host.png">
|
||||
<CardTitle>セルフホスティング</CardTitle>
|
||||
単一の Docker Compose コマンドで、独自のインフラ上に Twenty を実行できます。
|
||||
<br />データを完全に制御でき、アップデートも自社のスケジュールで行えます。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: クイックスタート
|
||||
description: クラウド環境でもセルフホスト環境でも、5分以内に Twenty をセットアップして稼働させましょう。
|
||||
icon: play
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## サインアップ
|
||||
|
||||
<Steps>
|
||||
<Step title="アカウントを作成する">
|
||||
[app.twenty.com](https://app.twenty.com) にアクセスし、Google、Microsoft、またはメールでサインアップします。
|
||||
</Step>
|
||||
<Step title="トライアルを選択">
|
||||
**30日間**(カードあり)または **7日間**(カードなし)から選択します。 どちらもフルアクセスが含まれます — 連絡先は無制限、メール連携、カスタムオブジェクト、API を利用できます。 プランや請求サイクルはいつでも変更できます。
|
||||
</Step>
|
||||
<Step title="ワークスペースの作成">
|
||||
Stripe で支払いが確認されたら、ワークスペース名とユーザープロファイルを設定します。 いつでもキャンセルできます。
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<VimeoEmbed videoId="1185227242" title="ワークスペースを作成" />
|
||||
|
||||
## ワークスペースを構成する
|
||||
|
||||
ログインできたら、Twenty を自分用にするための 3 ステップがあります。
|
||||
|
||||
### 1. メールボックスを接続する
|
||||
|
||||
**Settings → Accounts** に移動し、Google または Microsoft アカウントを接続します。 Twenty がメールとカレンダーの予定をインポートし、やり取りから自動的に連絡先を作成します。 ほかのプロバイダーを利用していますか? 同じページから、SMTP でメールボックスを、CalDAV でカレンダーを追加できます。
|
||||
|
||||
<Note>ここから始めましょう — メールボックスを接続すると、他のカスタマイズを行う前に、実データによる即時の価値をチームにもたらせます。</Note>
|
||||
|
||||
<VimeoEmbed videoId="1185416821" title="メールボックスを接続する" />
|
||||
|
||||
### 2. データモデルを設計する
|
||||
|
||||
**Settings → Data Model** に移動して、カスタムオブジェクトとフィールドを作成します。 知っておくべきこと:
|
||||
|
||||
* カスタムオブジェクトとフィールドは **すべてのプランで無制限** です — アップセルはありません。
|
||||
* **People、Companies、Opportunities** は、同期されたメールとミーティングが表示されるオブジェクトです。 これらをベースとして使用し、メール履歴が残らない別オブジェクトを作成するのではなく、フィールド(例:`Person Type` フィールド)を追加して分類しましょう。
|
||||
* 2 人の People が同じメールアドレスを共有することはできません。 2 つの Companies が同じドメインを共有することはできません。
|
||||
* 不要な標準フィールドや標準オブジェクトは無効化でき、削除せずにビューからフィールドを非表示にできます。
|
||||
|
||||
[データモデルリファレンス →](/l/ja/user-guide/data-model/overview)
|
||||
|
||||
<VimeoEmbed videoId="1185416793" title="データモデルを設計する" />
|
||||
|
||||
### 3. データのインポート
|
||||
|
||||
Command メニュー(`Cmd+K` / `Ctrl+K`)を使用して、CSV 経由で People、Companies、Opportunities、または任意のカスタムオブジェクトをインポートします。 まずサンプルファイルをダウンロードして、想定される形式を確認してください。 ファイルは 1 万件までに制限し、インポート前にメールアドレスやドメインの重複を削除してください。
|
||||
|
||||
[データ移行ガイド →](/l/ja/user-guide/data-migration/overview)
|
||||
|
||||
<VimeoEmbed videoId="1185416775" title="データのインポート" />
|
||||
|
||||
## 次のステップ
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="レイアウトを理解する" icon="table-columns" href="/l/ja/getting-started/core-concepts/layout">
|
||||
ナビゲーション、ビュー、コマンドメニュー、サイドパネル。
|
||||
</Card>
|
||||
<Card title="ワークフローを構築" icon="bolt" href="/l/ja/user-guide/workflows/overview">
|
||||
ビジネスプロセスを自動化します。
|
||||
</Card>
|
||||
<Card title="ビューを作成" icon="table" href="/l/ja/user-guide/layout/overview">
|
||||
テーブル、かんばん、カレンダー — データをフィルターおよびソートします。
|
||||
</Card>
|
||||
<Card title="API を探索" icon="plug" href="/l/ja/developers/extend/api">
|
||||
テナントごとのスキーマによる REST と GraphQL。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,24 +1,27 @@
|
||||
{
|
||||
"tabs": {
|
||||
"gettingStarted": {
|
||||
"label": "始めに",
|
||||
"groups": {
|
||||
"welcome": {
|
||||
"label": "ようこそ"
|
||||
},
|
||||
"coreConcepts": {
|
||||
"label": "基本概念"
|
||||
}
|
||||
}
|
||||
},
|
||||
"userGuide": {
|
||||
"label": "ユーザーガイド",
|
||||
"groups": {
|
||||
"discoverTwenty": {
|
||||
"label": "Twenty を知る",
|
||||
"groups": {
|
||||
"gettingStartedCapabilities": {
|
||||
"label": "機能"
|
||||
},
|
||||
"gettingStartedHowTos": {
|
||||
"label": "ハウツー"
|
||||
}
|
||||
}
|
||||
"userGuideOverview": {
|
||||
"label": "概要"
|
||||
},
|
||||
"dataModel": {
|
||||
"label": "データモデル",
|
||||
"groups": {
|
||||
"dataModelCapabilities": {
|
||||
"label": "機能"
|
||||
"dataModelReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"dataModelHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -28,8 +31,8 @@
|
||||
"dataMigration": {
|
||||
"label": "データ移行",
|
||||
"groups": {
|
||||
"dataMigrationCapabilities": {
|
||||
"label": "機能"
|
||||
"dataMigrationReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"dataMigrationHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -39,8 +42,8 @@
|
||||
"calendarEmails": {
|
||||
"label": "カレンダーとメール",
|
||||
"groups": {
|
||||
"calendarEmailsCapabilities": {
|
||||
"label": "機能"
|
||||
"calendarEmailsReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"calendarEmailsHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -50,8 +53,8 @@
|
||||
"workflows": {
|
||||
"label": "ワークフロー",
|
||||
"groups": {
|
||||
"workflowsCapabilities": {
|
||||
"label": "機能"
|
||||
"workflowsReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"workflowsHowTos": {
|
||||
"label": "ハウツー",
|
||||
@@ -75,21 +78,26 @@
|
||||
"ai": {
|
||||
"label": "AI",
|
||||
"groups": {
|
||||
"aiCapabilities": {
|
||||
"label": "機能"
|
||||
"aiReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"aiHowTos": {
|
||||
"label": "ハウツー"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewsPipelines": {
|
||||
"label": "ビューとパイプライン",
|
||||
"layout": {
|
||||
"label": "レイアウト",
|
||||
"groups": {
|
||||
"viewsPipelinesCapabilities": {
|
||||
"label": "機能"
|
||||
"layoutReference": {
|
||||
"label": "リファレンス",
|
||||
"groups": {
|
||||
"layoutViews": {
|
||||
"label": "ビュー"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewsPipelinesHowTos": {
|
||||
"layoutHowTos": {
|
||||
"label": "ハウツー"
|
||||
}
|
||||
}
|
||||
@@ -97,8 +105,8 @@
|
||||
"dashboards": {
|
||||
"label": "ダッシュボード",
|
||||
"groups": {
|
||||
"dashboardsCapabilities": {
|
||||
"label": "機能"
|
||||
"dashboardsReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"dashboardsHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -108,8 +116,8 @@
|
||||
"permissionsAccess": {
|
||||
"label": "権限とアクセス",
|
||||
"groups": {
|
||||
"permissionsAccessCapabilities": {
|
||||
"label": "機能"
|
||||
"permissionsAccessReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"permissionsAccessHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -119,8 +127,8 @@
|
||||
"billing": {
|
||||
"label": "請求",
|
||||
"groups": {
|
||||
"billingCapabilities": {
|
||||
"label": "機能"
|
||||
"billingReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"billingHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -130,8 +138,8 @@
|
||||
"settings": {
|
||||
"label": "設定",
|
||||
"groups": {
|
||||
"settingsCapabilities": {
|
||||
"label": "機能"
|
||||
"settingsReference": {
|
||||
"label": "リファレンス"
|
||||
},
|
||||
"settingsHowTos": {
|
||||
"label": "ハウツー"
|
||||
@@ -143,59 +151,40 @@
|
||||
"developers": {
|
||||
"label": "開発者",
|
||||
"groups": {
|
||||
"developersGroup": {
|
||||
"label": "開発者"
|
||||
"developersOverview": {
|
||||
"label": "概要"
|
||||
},
|
||||
"extend": {
|
||||
"label": "拡張",
|
||||
"apps": {
|
||||
"label": "アプリ",
|
||||
"groups": {
|
||||
"extendCapabilities": {
|
||||
"label": "機能"
|
||||
"appsGettingStarted": {
|
||||
"label": "始めに"
|
||||
},
|
||||
"appsConfig": {
|
||||
"label": "設定"
|
||||
},
|
||||
"appsData": {
|
||||
"label": "データ"
|
||||
},
|
||||
"appsLogic": {
|
||||
"label": "ロジック"
|
||||
},
|
||||
"appsLayout": {
|
||||
"label": "レイアウト"
|
||||
},
|
||||
"appsOperations": {
|
||||
"label": "オペレーション"
|
||||
}
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"label": "API"
|
||||
},
|
||||
"selfHost": {
|
||||
"label": "セルフホスト",
|
||||
"groups": {
|
||||
"selfHostCapabilities": {
|
||||
"label": "機能"
|
||||
}
|
||||
}
|
||||
"label": "セルフホスト"
|
||||
},
|
||||
"contribute": {
|
||||
"label": "貢献",
|
||||
"groups": {
|
||||
"contributeCapabilities": {
|
||||
"label": "機能",
|
||||
"groups": {
|
||||
"frontendDevelopment": {
|
||||
"label": "フロントエンド開発",
|
||||
"groups": {
|
||||
"twentyUi": {
|
||||
"label": "Twenty UI",
|
||||
"groups": {
|
||||
"display": {
|
||||
"label": "表示"
|
||||
},
|
||||
"feedback": {
|
||||
"label": "フィードバック"
|
||||
},
|
||||
"input": {
|
||||
"label": "入力"
|
||||
},
|
||||
"navigation": {
|
||||
"label": "ナビゲーション"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"backendDevelopment": {
|
||||
"label": "バックエンド開発"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"label": "貢献"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: アプリツールチップ
|
||||
icon: message
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -9,46 +10,54 @@ title: アプリツールチップ
|
||||
ユーザーが要素とやり取りするときに追加情報を表示する短いメッセージ。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { AppTooltip } from "@/ui/display/tooltip/AppTooltip";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<p id="hoverText" style={{ display: "inline-block" }}>
|
||||
Customer Insights
|
||||
</p>
|
||||
<AppTooltip
|
||||
className
|
||||
anchorSelect="#hoverText"
|
||||
content="Explore customer behavior and preferences"
|
||||
delayHide={0}
|
||||
offset={6}
|
||||
noArrow={false}
|
||||
isOpen={true}
|
||||
place="bottom"
|
||||
positionStrategy="absolute"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { AppTooltip } from "@/ui/display/tooltip/AppTooltip";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<p id="hoverText" style={{ display: "inline-block" }}>
|
||||
Customer Insights
|
||||
</p>
|
||||
<AppTooltip
|
||||
className
|
||||
anchorSelect="#hoverText"
|
||||
content="Explore customer behavior and preferences"
|
||||
delayHide={0}
|
||||
offset={6}
|
||||
noArrow={false}
|
||||
isOpen={true}
|
||||
place="bottom"
|
||||
positionStrategy="absolute"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ---------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加のスタイリング用のオプションのCSSクラス |
|
||||
| anchorSelect | CSSセレクタ | ツールチップのアンカー(ツールチップをトリガーする要素)のセレクタ |
|
||||
| content | string | ツールチップ内に表示したいコンテンツ |
|
||||
| delayHide | 数 | アンカーからカーソルが離れた後、ツールチップを非表示にする前の遅延(秒) |
|
||||
| offset | 数 | ツールチップの位置を決めるためのオフセット(ピクセル) |
|
||||
| noArrow | ブール型 | `true`の場合、ツールチップの矢印を非表示にします |
|
||||
| isOpen | ブール型 | `true`の場合、ツールチップはデフォルトで開かれています |
|
||||
| place | `react-tooltip`からの`PlacesType`文字列 | ツールチップの配置を指定します。 値は`bottom`、`left`、`right`、`top`、`top-start`、`top-end`、`right-start`、`right-end`、`bottom-start`、`bottom-end`、`left-start`、`left-end`などがあります |
|
||||
| positionStrategy | `react-tooltip`からの`PositionStrategy`文字列 | ツールチップの位置戦略。 2つの値があります: `absolute` と `fixed` |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ---------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加のスタイリング用のオプションのCSSクラス |
|
||||
| anchorSelect | CSSセレクタ | ツールチップのアンカー(ツールチップをトリガーする要素)のセレクタ |
|
||||
| content | string | ツールチップ内に表示したいコンテンツ |
|
||||
| delayHide | 数 | アンカーからカーソルが離れた後、ツールチップを非表示にする前の遅延(秒) |
|
||||
| offset | 数 | ツールチップの位置を決めるためのオフセット(ピクセル) |
|
||||
| noArrow | ブール型 | `true`の場合、ツールチップの矢印を非表示にします |
|
||||
| isOpen | ブール型 | `true`の場合、ツールチップはデフォルトで開かれています |
|
||||
| place | `react-tooltip`からの`PlacesType`文字列 | ツールチップの配置を指定します。 値は`bottom`、`left`、`right`、`top`、`top-start`、`top-end`、`right-start`、`right-end`、`bottom-start`、`bottom-end`、`left-start`、`left-end`などがあります |
|
||||
| positionStrategy | `react-tooltip`からの`PositionStrategy`文字列 | ツールチップの位置戦略。 2つの値があります: `absolute` と `fixed` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ツールチップを伴うオーバーフローテキスト
|
||||
@@ -56,22 +65,30 @@ title: アプリツールチップ
|
||||
オーバーフローテキストを処理し、テキストがオーバーフローしたときにツールチップを表示します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
<Tab title="使用方法">
|
||||
|
||||
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.';
|
||||
```jsx
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
|
||||
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
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.';
|
||||
|
||||
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----- | ------ | -------------------------- |
|
||||
| テキスト | string | オーバーフローテキストエリア内に表示したいコンテンツ |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----- | ------ | -------------------------- |
|
||||
| テキスト | string | オーバーフローテキストエリア内に表示したいコンテンツ |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: チェックマーク
|
||||
image: '""'
|
||||
icon: circle-check
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -10,19 +10,25 @@ image: '""'
|
||||
成功したまたは完了したアクションを示します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Checkmark } from 'twenty-ui/display';
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <Checkmark />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Checkmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <Checkmark />;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
`div` 要素の全てのプロパティを受け取る他、`React.ComponentPropsWithoutRef<'div'>`を拡張します。
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
`div` 要素の全てのプロパティを受け取る他、`React.ComponentPropsWithoutRef<'div'>`を拡張します。
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## アニメーション付きチェックマーク
|
||||
@@ -30,29 +36,38 @@ image: '""'
|
||||
アニメーション機能を追加したチェックマークアイコンを示します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { AnimatedCheckmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AnimatedCheckmark
|
||||
isAnimating={true}
|
||||
color="green"
|
||||
duration={0.5}
|
||||
size={30}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
```jsx
|
||||
import { AnimatedCheckmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AnimatedCheckmark
|
||||
isAnimating={true}
|
||||
color="green"
|
||||
duration={0.5}
|
||||
size={30}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ---------- | ------ | --------------------- | ------ |
|
||||
| アニメーションの有無 | ブール型 | チェックマークのアニメーションを制御します | 偽 |
|
||||
| カラー | string | チェックマークの色 | |
|
||||
| 継続時間 | 数 | アニメーションの持続時間(秒) | 0.5秒 |
|
||||
| サイズ | 数 | チェックマークのサイズ | 28ピクセル |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ---------- | ------ | --------------------- | ------ |
|
||||
| アニメーションの有無 | ブール型 | チェックマークのアニメーションを制御します | 偽 |
|
||||
| カラー | string | チェックマークの色 | |
|
||||
| 継続時間 | 数 | アニメーションの持続時間(秒) | 0.5秒 |
|
||||
| サイズ | 数 | チェックマークのサイズ | 28ピクセル |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,40 +9,48 @@ title: チップ
|
||||
ラベル、オプションの左および右コンポーネント、さまざまなスタイルオプションを使用してラベルやタグを表示するクリック可能または非クリック可能なコンテナとして使用できるビジュアル要素。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Clickable Chip"
|
||||
clickable={true}
|
||||
variant="highlighted"
|
||||
accent="text-primary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
<Tab title="使用方法">
|
||||
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ----------------------- | -------------------------------------------------------- |
|
||||
| linkToEntity | string | エンティティへのリンク |
|
||||
| entityId | string | エンティティの一意識別子 |
|
||||
| 名前 | string | エンティティの名前 |
|
||||
| pictureUrl | string | 写真", |
|
||||
| avatarType | アバタータイプ | 表示したいアバターのタイプ。 オプションは2つ:`rounded` と `squared` |
|
||||
| バリアント | `EntityChipVariant` 列挙型 | 表示したいエンティティチップのバリアント。 オプションは2つ:`regular` と `transparent` |
|
||||
| 左アイコン | アイコンコンポーネント | アイコンを表す React コンポーネント。 チップの左側に表示されます |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Clickable Chip"
|
||||
clickable={true}
|
||||
variant="highlighted"
|
||||
accent="text-primary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ----------------------- | -------------------------------------------------------- |
|
||||
| linkToEntity | string | エンティティへのリンク |
|
||||
| entityId | string | エンティティの一意識別子 |
|
||||
| 名前 | string | エンティティの名前 |
|
||||
| pictureUrl | string | 写真", |
|
||||
| avatarType | アバタータイプ | 表示したいアバターのタイプ。 オプションは2つ:`rounded` と `squared` |
|
||||
| バリアント | `EntityChipVariant` 列挙型 | 表示したいエンティティチップのバリアント。 オプションは2つ:`regular` と `transparent` |
|
||||
| 左アイコン | アイコンコンポーネント | アイコンを表す React コンポーネント。 チップの左側に表示されます |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 例
|
||||
@@ -99,39 +107,47 @@ export const MyComponent = () => {
|
||||
エンティティに関する情報を表示するチップ風の要素。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Chip
|
||||
linkToEntity="/entity-link"
|
||||
entityId="entityTest"
|
||||
name="Entity name"
|
||||
pictureUrl=""
|
||||
avatarType="rounded"
|
||||
variant="regular"
|
||||
LeftIcon={IconTwentyStar}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ----------------------- | -------------------------------------------------------- |
|
||||
| linkToEntity | string | エンティティへのリンク |
|
||||
| entityId | string | エンティティの一意識別子 |
|
||||
| 名前 | string | エンティティの名前 |
|
||||
| pictureUrl | string | 写真", |
|
||||
| avatarType | アバタータイプ | 表示したいアバターのタイプ。 オプションは2つ:`rounded` と `squared` |
|
||||
| バリアント | `EntityChipVariant` 列挙型 | 表示したいエンティティチップのバリアント。 オプションは2つ:`regular` と `transparent` |
|
||||
| 左アイコン | アイコンコンポーネント | アイコンを表す React コンポーネント。 チップの左側に表示されます |
|
||||
</Tab>
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Chip
|
||||
linkToEntity="/entity-link"
|
||||
entityId="entityTest"
|
||||
name="Entity name"
|
||||
pictureUrl=""
|
||||
avatarType="rounded"
|
||||
variant="regular"
|
||||
LeftIcon={IconTwentyStar}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ----------------------- | -------------------------------------------------------- |
|
||||
| linkToEntity | string | エンティティへのリンク |
|
||||
| entityId | string | エンティティの一意識別子 |
|
||||
| 名前 | string | エンティティの名前 |
|
||||
| pictureUrl | string | 写真", |
|
||||
| avatarType | アバタータイプ | 表示したいアバターのタイプ。 オプションは2つ:`rounded` と `squared` |
|
||||
| バリアント | `EntityChipVariant` 列挙型 | 表示したいエンティティチップのバリアント。 オプションは2つ:`regular` と `transparent` |
|
||||
| 左アイコン | アイコンコンポーネント | アイコンを表す React コンポーネント。 チップの左側に表示されます |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: アイコン
|
||||
icon: icons
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -13,35 +14,44 @@ title: アイコン
|
||||
アプリ全体で React 向けの Tabler Icons を使用しています。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="インストール">
|
||||
<br />
|
||||
|
||||
```
|
||||
yarn add @tabler/icons-react
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="インストール">
|
||||
<br />
|
||||
|
||||
<Tab title="プロパティ">
|
||||
各アイコンをコンポーネントとしてインポートできます。 例はこちら:
|
||||
```
|
||||
yarn add @tabler/icons-react
|
||||
```
|
||||
|
||||
<br />
|
||||
</Tab>
|
||||
|
||||
```jsx
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
<Tab title="プロパティ">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconArrowLeft color="red" size={48} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
各アイコンをコンポーネントとしてインポートできます。 例はこちら:
|
||||
<br />
|
||||
|
||||
```jsx
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconArrowLeft color="red" size={48} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | ------ | ------------------- | ------------ |
|
||||
| サイズ | 数 | アイコンの高さと幅(ピクセル単位) | 24 |
|
||||
| カラー | string | アイコンの色 | currentColor |
|
||||
| ストローク | 数 | アイコンのストローク幅(ピクセル単位) | 2 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | ------ | ------------------- | ------------ |
|
||||
| サイズ | 数 | アイコンの高さと幅(ピクセル単位) | 24 |
|
||||
| カラー | string | アイコンの色 | currentColor |
|
||||
| ストローク | 数 | アイコンのストローク幅(ピクセル単位) | 2 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## カスタムアイコン
|
||||
@@ -53,20 +63,29 @@ Tablerアイコンに加えて、アプリにはいくつかのカスタムア
|
||||
アドレス帳のアイコンを表示します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconAddressBook } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconAddressBook size={24} stroke={2} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
```jsx
|
||||
import { IconAddressBook } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconAddressBook size={24} stroke={2} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | --- | ------------------- | ----- |
|
||||
| サイズ | 数 | アイコンの高さと幅(ピクセル単位) | 24 |
|
||||
| ストローク | 数 | アイコンのストローク幅(ピクセル単位) | 2 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | --- | ------------------- | ----- |
|
||||
| サイズ | 数 | アイコンの高さと幅(ピクセル単位) | 24 |
|
||||
| ストローク | 数 | アイコンのストローク幅(ピクセル単位) | 2 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
title: Soon Pill
|
||||
---
|
||||
|
||||
|
||||
近日公開を示す小さなバッジ(ピル)です。
|
||||
|
||||
```jsx
|
||||
|
||||
@@ -1,34 +1,43 @@
|
||||
---
|
||||
title: タグ
|
||||
icon: tag
|
||||
---
|
||||
|
||||
|
||||
コンテンツを視覚的に分類またはラベル付けするためのコンポーネント。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Tag } from "@/ui/display/tag/components/Tag";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Tag
|
||||
className
|
||||
color="red"
|
||||
text="緊急"
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
```jsx
|
||||
import { Tag } from "@/ui/display/tag/components/Tag";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Tag
|
||||
className
|
||||
color="red"
|
||||
text="緊急"
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | ------------------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのための任意の名前 |
|
||||
| カラー | string | タグの色。 オプションは次のとおりです: `緑`, `トルコ石`, `空`, `青`, `紫`, `ピンク`, `赤`, `オレンジ`, `黄`, `灰色` |
|
||||
| テキスト | string | タグの内容 |
|
||||
| onClick | 関数 | ユーザーがタグをクリックすると呼び出される任意の関数 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | ------------------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのための任意の名前 |
|
||||
| カラー | string | タグの色。 オプションは次のとおりです: `緑`, `トルコ石`, `空`, `青`, `紫`, `ピンク`, `赤`, `オレンジ`, `黄`, `灰色` |
|
||||
| テキスト | string | タグの内容 |
|
||||
| onClick | 関数 | ユーザーがタグをクリックすると呼び出される任意の関数 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,22 +9,28 @@ title: ブロックエディター
|
||||
ブロックベースのリッチテキストエディター[BlockNote](https://www.blocknotejs.org/)を使用して、ユーザーがコンテンツのブロックを編集および表示できるようにします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { useBlockNote } from "@blocknote/react";
|
||||
import { BlockEditor } from "@/ui/input/editor/components/BlockEditor";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const BlockNoteEditor = useBlockNote();
|
||||
```jsx
|
||||
import { useBlockNote } from "@blocknote/react";
|
||||
import { BlockEditor } from "@/ui/input/editor/components/BlockEditor";
|
||||
|
||||
return <BlockEditor editor={BlockNoteEditor} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const BlockNoteEditor = useBlockNote();
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----- | ----------------- | -------------------- |
|
||||
| エディター | `BlockNoteEditor` | ブロックエディターインスタンスまたは構成 |
|
||||
</Tab>
|
||||
return <BlockEditor editor={BlockNoteEditor} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----- | ----------------- | -------------------- |
|
||||
| エディター | `BlockNoteEditor` | ブロックエディターインスタンスまたは構成 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: ボタン
|
||||
icon: hand-pointer
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -11,428 +12,511 @@ title: ボタン
|
||||
## ボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="タイトル"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="default"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="タイトル"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="default"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --------------------- | ------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプションクラス名 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| fullWidth | ブール型 | ボタンがコンテナの全幅を占めるかどうかを定義します |
|
||||
| バリアント | string | ボタンの視覚スタイルのバリアント。 オプションは `primary`、`secondary`、 `tertiary` を含みます |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| "位置" | string | ボタンの兄弟関係における位置。 オプションは `standalone`、`left`、`right`、 `middle` を含みます |
|
||||
| accent | string | ボタンのアクセント色。 オプションは `default`、`blue`、`danger` を含みます |
|
||||
| 間もなく | ブール型 | ボタンが「soon」としてマークされているかどうかを示します(たとえば、今後の機能のために)。 |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを指定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを決定します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --------------------- | ------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプションクラス名 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| fullWidth | ブール型 | ボタンがコンテナの全幅を占めるかどうかを定義します |
|
||||
| バリアント | string | ボタンの視覚スタイルのバリアント。 オプションは `primary`、`secondary`、 `tertiary` を含みます |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| "位置" | string | ボタンの兄弟関係における位置。 オプションは `standalone`、`left`、`right`、 `middle` を含みます |
|
||||
| accent | string | ボタンのアクセント色。 オプションは `default`、`blue`、`danger` を含みます |
|
||||
| 間もなく | ブール型 | ボタンが「soon」としてマークされているかどうかを示します(たとえば、今後の機能のために)。 |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを指定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを決定します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ボタングループ
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
import { ButtonGroup } from "@/ui/input/button/components/ButtonGroup";
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
import { ButtonGroup } from "@/ui/input/button/components/ButtonGroup";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ButtonGroup variant="primary" size="large" accent="blue" className>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="ボタン 1"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="ボタン 2"
|
||||
fullWidth={false}
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
position="left"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="ボタン 3"
|
||||
fullWidth={false}
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
position="right"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ButtonGroup variant="primary" size="large" accent="blue" className>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="ボタン 1"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="ボタン 2"
|
||||
fullWidth={false}
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
position="left"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="ボタン 3"
|
||||
fullWidth={false}
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
position="right"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --------- | ---------------------------------------------------------------------- |
|
||||
| バリアント | string | グループ内のボタンの視覚スタイルのバリアント。 オプションは `primary`、`secondary`、 `tertiary` を含みます |
|
||||
| サイズ | string | グループ内のボタンのサイズ。 オプションは2つ:`medium` と `small` |
|
||||
| accent | string | グループ内のボタンのアクセント色。 オプションは `default`、`blue`、 `danger` を含みます |
|
||||
| className | string | 追加のスタイリングのためのオプションクラス名 |
|
||||
| children | ReactNode | グループ内の個々のボタンを表す React エレメントの配列 |
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --------- | ---------------------------------------------------------------------- |
|
||||
| バリアント | string | グループ内のボタンの視覚スタイルのバリアント。 オプションは `primary`、`secondary`、 `tertiary` を含みます |
|
||||
| サイズ | string | グループ内のボタンのサイズ。 オプションは2つ:`medium` と `small` |
|
||||
| accent | string | グループ内のボタンのアクセント色。 オプションは `default`、`blue`、 `danger` を含みます |
|
||||
| className | string | 追加のスタイリングのためのオプションクラス名 |
|
||||
| children | ReactNode | グループ内の個々のボタンを表す React エレメントの配列 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 浮動ボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
title="タイトル"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----------- | --------------------- | ------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| "位置" | string | ボタンの兄弟関係における位置。 オプションは `standalone`、`left`、`middle`、 `right` を含みます |
|
||||
| applyShadow | ブール型 | ボタンにシャドウを適用するかどうかを決定します |
|
||||
| applyBlur | ブール型 | ボタンにぼかし効果を適用するかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
title="タイトル"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----------- | --------------------- | ------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| "位置" | string | ボタンの兄弟関係における位置。 オプションは `standalone`、`left`、`middle`、 `right` を含みます |
|
||||
| applyShadow | ブール型 | ボタンにシャドウを適用するかどうかを決定します |
|
||||
| applyBlur | ブール型 | ボタンにぼかし効果を適用するかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 浮動ボタングループ
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { FloatingButtonGroup } from "@/ui/input/button/components/FloatingButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButtonGroup size="small">
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconClipboardText}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconCheckbox}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
/>
|
||||
</FloatingButtonGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { FloatingButtonGroup } from "@/ui/input/button/components/FloatingButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButtonGroup size="small">
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconClipboardText}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconCheckbox}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
/>
|
||||
</FloatingButtonGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| -------- | --------- | ------------------------------------ | ----- |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` | small |
|
||||
| children | ReactNode | グループ内の個々のボタンを表す React エレメントの配列 | |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| -------- | --------- | ------------------------------------ | ----- |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` | small |
|
||||
| children | ReactNode | グループ内の個々のボタンを表す React エレメントの配列 | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 浮動アイコンボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { FloatingIconButton } from "@/ui/input/button/components/FloatingIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingIconButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
isActive={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { FloatingIconButton } from "@/ui/input/button/components/FloatingIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingIconButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("クリック")}
|
||||
isActive={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----------- | --------------------- | ------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| "位置" | string | ボタンの兄弟関係における位置。 オプションは `standalone`、`left`、`right`、 `middle` を含みます |
|
||||
| applyShadow | ブール型 | ボタンにシャドウを適用するかどうかを決定します |
|
||||
| applyBlur | ブール型 | ボタンにぼかし効果を適用するかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
| isActive | ブール型 | ボタンがアクティブな状態にあるかどうかを決定します |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----------- | --------------------- | ------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| "位置" | string | ボタンの兄弟関係における位置。 オプションは `standalone`、`left`、`right`、 `middle` を含みます |
|
||||
| applyShadow | ブール型 | ボタンにシャドウを適用するかどうかを決定します |
|
||||
| applyBlur | ブール型 | ボタンにぼかし効果を適用するかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
| isActive | ブール型 | ボタンがアクティブな状態にあるかどうかを決定します |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 浮動アイコンボタングループ
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { FloatingIconButtonGroup } from "@/ui/input/button/components/FloatingIconButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const iconButtons = [
|
||||
{
|
||||
Icon: IconClipboardText,
|
||||
onClick: () => console.log("ボタン 1 がクリックされました"),
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => console.log("ボタン 2 がクリックされました"),
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
```jsx
|
||||
import { FloatingIconButtonGroup } from "@/ui/input/button/components/FloatingIconButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
return (
|
||||
<FloatingIconButtonGroup
|
||||
className
|
||||
size="small"
|
||||
iconButtons={iconButtons} />
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const iconButtons = [
|
||||
{
|
||||
Icon: IconClipboardText,
|
||||
onClick: () => console.log("ボタン 1 がクリックされました"),
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => console.log("ボタン 2 がクリックされました"),
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<FloatingIconButtonGroup
|
||||
className
|
||||
size="small"
|
||||
iconButtons={iconButtons} />
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| iconButtons | array | グループ内のアイコンボタンをそれぞれ表すオブジェクトの配列。 各オブジェクトには、ボタンに表示したいアイコンコンポーネント、ユーザーがボタンをクリックした時に呼び出される関数、およびボタンをアクティブにするかどうかが含まれる必要があります。 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| iconButtons | array | グループ内のアイコンボタンをそれぞれ表すオブジェクトの配列。 各オブジェクトには、ボタンに表示したいアイコンコンポーネント、ユーザーがボタンをクリックした時に呼び出される関数、およびボタンをアクティブにするかどうかが含まれる必要があります。 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ライトボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { LightButton } from "@/ui/input/button/components/LightButton";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <LightButton
|
||||
className
|
||||
icon={null}
|
||||
title="タイトル"
|
||||
accent="secondary"
|
||||
active={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={()=>console.log('クリック')}
|
||||
/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { LightButton } from "@/ui/input/button/components/LightButton";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <LightButton
|
||||
className
|
||||
icon={null}
|
||||
title="タイトル"
|
||||
accent="secondary"
|
||||
active={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={()=>console.log('クリック')}
|
||||
/>;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| アイコン | `React.ReactNode` | ボタンに表示したいアイコン |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| accent | string | ボタンのアクセント色。 オプションは `secondary`、 `tertiary` を含みます |
|
||||
| アクティブ | ブール型 | ボタンがアクティブな状態にあるかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| アイコン | `React.ReactNode` | ボタンに表示したいアイコン |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| accent | string | ボタンのアクセント色。 オプションは `secondary`、 `tertiary` を含みます |
|
||||
| アクティブ | ブール型 | ボタンがアクティブな状態にあるかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ライトアイコンボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { LightIconButton } from "@/ui/input/button/components/LightIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<LightIconButton
|
||||
className
|
||||
testId="test1"
|
||||
Icon={IconSearch}
|
||||
title="タイトル"
|
||||
size="small"
|
||||
accent="secondary"
|
||||
active={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { LightIconButton } from "@/ui/input/button/components/LightIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<LightIconButton
|
||||
className
|
||||
testId="test1"
|
||||
Icon={IconSearch}
|
||||
title="タイトル"
|
||||
size="small"
|
||||
accent="secondary"
|
||||
active={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("クリック")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --------------------- | ------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| testId | string | ボタンのテスト識別子 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| accent | string | ボタンのアクセント色。 オプションは `secondary`、 `tertiary` を含みます |
|
||||
| アクティブ | ブール型 | ボタンがアクティブな状態にあるかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --------------------- | ------------------------------------------------ |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| testId | string | ボタンのテスト識別子 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| サイズ | string | ボタンのサイズ。 オプションは2つ:`small` と `medium` |
|
||||
| accent | string | ボタンのアクセント色。 オプションは `secondary`、 `tertiary` を含みます |
|
||||
| アクティブ | ブール型 | ボタンがアクティブな状態にあるかどうかを決定します |
|
||||
| disabled | ブール型 | ボタンが無効かどうかを決定します |
|
||||
| focus | ブール型 | ボタンにフォーカスがあるかどうかを示します |
|
||||
| onClick | function | ユーザーがボタンをクリックしたときにトリガーされるコールバック関数 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## メインボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { MainButton } from "@/ui/input/button/components/MainButton";
|
||||
import { IconCheckbox } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<MainButton
|
||||
title="チェックボックス"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
soon={false}
|
||||
Icon={IconCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { MainButton } from "@/ui/input/button/components/MainButton";
|
||||
import { IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<MainButton
|
||||
title="チェックボックス"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
soon={false}
|
||||
Icon={IconCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------------- | -------------------------------- | ------------------------------------------------------ |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| fullWidth | ブール型 | ボタンがコンテナの全幅を占めるかどうかを定義します |
|
||||
| バリアント | string | ボタンの視覚スタイルのバリアント。 オプションは `primary` と `secondary` を含みます |
|
||||
| 間もなく | ブール型 | ボタンが「soon」としてマークされているかどうかを示します(たとえば、今後の機能のために)。 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| React `button` props | `React.ComponentProps<'button'>` | 標準的なHTMLボタンのプロパティすべてがサポートされています |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------------- | -------------------------------- | ------------------------------------------------------ |
|
||||
| タイトル | string | ボタンのテキスト内容 |
|
||||
| fullWidth | ブール型 | ボタンがコンテナの全幅を占めるかどうかを定義します |
|
||||
| バリアント | string | ボタンの視覚スタイルのバリアント。 オプションは `primary` と `secondary` を含みます |
|
||||
| 間もなく | ブール型 | ボタンが「soon」としてマークされているかどうかを示します(たとえば、今後の機能のために)。 |
|
||||
| アイコン | `React.ComponentType` | ボタン内に表示されるオプションのアイコンコンポーネント |
|
||||
| React `button` props | `React.ComponentProps<'button'>` | 標準的なHTMLボタンのプロパティすべてがサポートされています |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 丸型アイコンボタン
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { RoundedIconButton } from "@/ui/input/button/components/RoundedIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RoundedIconButton
|
||||
Icon={IconSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { RoundedIconButton } from "@/ui/input/button/components/RoundedIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RoundedIconButton
|
||||
Icon={IconSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------------- | ----------------------------------------------- | -- |
|
||||
| アイコン | `React.ComponentType` | |
|
||||
| React `button` props | `React.ButtonHTMLAttributes<HTMLButtonElement>` | |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------------- | ----------------------------------------------- | -- |
|
||||
| アイコン | `React.ComponentType` | |
|
||||
| React `button` props | `React.ButtonHTMLAttributes<HTMLButtonElement>` | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: チェックボックス
|
||||
image: '""'
|
||||
icon: square-check
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -10,35 +10,41 @@ image: '""'
|
||||
ユーザーが、複数のオプションから複数の値を選択する必要がある場合に使用されます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Checkbox } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={true}
|
||||
indeterminate={false}
|
||||
onChange={() => console.log("onChange function fired")}
|
||||
onCheckedChange={() => console.log("onCheckedChange function fired")}
|
||||
variant="primary"
|
||||
size="small"
|
||||
shape="squared"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Checkbox } from "twenty-ui/display";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------------- | ------ | ------------------------------------------------------------------------- |
|
||||
| checked | ブール型 | チェックボックスがチェックされているかどうかを示します |
|
||||
| 不確定 | ブール型 | チェックボックスが不確定な状態(チェック済みでも未チェックでもない)かどうかを示します |
|
||||
| onChange | 関数 | チェックボックスの状態が変化したときにトリガーされるコールバック関数 |
|
||||
| onCheckedChange | 関数 | `checked` 状態が変化したときにトリガーされるコールバック関数 |
|
||||
| バリアント | string | ボックスのビジュアルスタイルのバリエーション。 オプションには `primary`、 `secondary`、 `tertiary` が含まれます |
|
||||
| サイズ | string | チェックボックスのサイズ。 オプションは2つ:`small` と `large` |
|
||||
| 形状 | string | チェックボックスの形状。 オプションは2つ:`squared` と `rounded` |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={true}
|
||||
indeterminate={false}
|
||||
onChange={() => console.log("onChange function fired")}
|
||||
onCheckedChange={() => console.log("onCheckedChange function fired")}
|
||||
variant="primary"
|
||||
size="small"
|
||||
shape="squared"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------------- | ------ | ------------------------------------------------------------------------- |
|
||||
| checked | ブール型 | チェックボックスがチェックされているかどうかを示します |
|
||||
| 不確定 | ブール型 | チェックボックスが不確定な状態(チェック済みでも未チェックでもない)かどうかを示します |
|
||||
| onChange | 関数 | チェックボックスの状態が変化したときにトリガーされるコールバック関数 |
|
||||
| onCheckedChange | 関数 | `checked` 状態が変化したときにトリガーされるコールバック関数 |
|
||||
| バリアント | string | ボックスのビジュアルスタイルのバリエーション。 オプションには `primary`、 `secondary`、 `tertiary` が含まれます |
|
||||
| サイズ | string | チェックボックスのサイズ。 オプションは2つ:`small` と `large` |
|
||||
| 形状 | string | チェックボックスの形状。 オプションは2つ:`squared` と `rounded` |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: カラースキーム
|
||||
icon: palette
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -11,28 +12,36 @@ title: カラースキーム
|
||||
異なる配色を表し、ライトおよびダークテーマに特化されています。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { ColorSchemeCard } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ColorSchemeCard
|
||||
variant="Dark"
|
||||
selected={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ColorSchemeCard } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ColorSchemeCard
|
||||
variant="Dark"
|
||||
selected={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| -------- | --------------------------------------- | --------------------------------------------------- | ----- |
|
||||
| バリアント | string | 配色のバリエーション。 オプションには `ダーク`、`ライト`、および `システム` が含まれています | ライト |
|
||||
| 選択済み | ブール型 | `true` の場合、選択されているカラースキームを示すチェックマークを表示します | |
|
||||
| 追加のプロパティ | `React.ComponentPropsWithoutRef<'div'>` | 標準の HTML `div` エレメントのプロパティ | |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| -------- | --------------------------------------- | --------------------------------------------------- | ----- |
|
||||
| バリアント | string | 配色のバリエーション。 オプションには `ダーク`、`ライト`、および `システム` が含まれています | ライト |
|
||||
| 選択済み | ブール型 | `true` の場合、選択されているカラースキームを示すチェックマークを表示します | |
|
||||
| 追加のプロパティ | `React.ComponentPropsWithoutRef<'div'>` | 標準の HTML `div` エレメントのプロパティ | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 配色ピッカー
|
||||
@@ -40,23 +49,31 @@ title: カラースキーム
|
||||
ユーザーが異なる配色を選択できます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { ColorSchemePicker } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ColorSchemePicker
|
||||
value="Dark"
|
||||
onChange
|
||||
/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ColorSchemePicker } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ColorSchemePicker
|
||||
value="Dark"
|
||||
onChange
|
||||
/>;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | --------- | ------------------------------ |
|
||||
| 値 | `カラースキーム` | 現在選択されている配色 |
|
||||
| onChange | 関数 | ユーザーが配色を選択したときにトリガーされるコールバック関数 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | --------- | ------------------------------ |
|
||||
| 値 | `カラースキーム` | 現在選択されている配色 |
|
||||
| onChange | 関数 | ユーザーが配色を選択したときにトリガーされるコールバック関数 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,40 +9,49 @@ title: アイコンピッカー
|
||||
ドロップダウンベースのアイコンピッカーで、ユーザーがリストからアイコンを選択できます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { IconPicker } from "@/ui/input/components/IconPicker";
|
||||
|
||||
export const MyComponent = () => {
|
||||
<Tab title="使用方法">
|
||||
|
||||
const [selectedIcon, setSelectedIcon] = useState("");
|
||||
const handleIconChange = ({ iconKey, Icon }) => {
|
||||
console.log("Selected Icon:", iconKey);
|
||||
setSelectedIcon(iconKey);
|
||||
};
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { IconPicker } from "@/ui/input/components/IconPicker";
|
||||
|
||||
return (
|
||||
<IconPicker
|
||||
disabled={false}
|
||||
onChange={handleIconChange}
|
||||
selectedIconKey={selectedIcon}
|
||||
variant="primary"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------------- | ------ | ------------------------------------------------------------------------------ |
|
||||
| 無効 | ブール型 | `true` に設定されるとアイコンピッカーは無効になります |
|
||||
| onChange | 関数 | ユーザーがアイコンを選択したときにトリガーされるコールバック関数。 それは `iconKey` と `Icon` プロパティを持つオブジェクトを受け取ります |
|
||||
| selectedIconKey | string | 最初に選択されたアイコンのキー |
|
||||
| onClickOutside | 関数 | ユーザーがドロップダウン外をクリックしたときにトリガーされるコールバック関数 |
|
||||
| onClose | 関数 | ドロップダウンが閉じられたときにトリガーされるコールバック関数 |
|
||||
| onOpen | 関数 | ドロップダウンが開かれたときにトリガーされるコールバック関数 |
|
||||
| バリアント | string | クリック可能なアイコンのビジュアルスタイルバリアント。 オプションには `primary`、 `secondary`、 `tertiary` が含まれます |
|
||||
</Tab>
|
||||
const [selectedIcon, setSelectedIcon] = useState("");
|
||||
const handleIconChange = ({ iconKey, Icon }) => {
|
||||
console.log("選択されたアイコン:", iconKey);
|
||||
setSelectedIcon(iconKey);
|
||||
};
|
||||
|
||||
return (
|
||||
<IconPicker
|
||||
disabled={false}
|
||||
onChange={handleIconChange}
|
||||
selectedIconKey={selectedIcon}
|
||||
variant="primary"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------------- | ------ | ------------------------------------------------------------------------------ |
|
||||
| 無効 | ブール型 | `true` に設定されるとアイコンピッカーは無効になります |
|
||||
| onChange | 関数 | ユーザーがアイコンを選択したときにトリガーされるコールバック関数。 それは `iconKey` と `Icon` プロパティを持つオブジェクトを受け取ります |
|
||||
| selectedIconKey | string | 最初に選択されたアイコンのキー |
|
||||
| onClickOutside | 関数 | ユーザーがドロップダウン外をクリックしたときにトリガーされるコールバック関数 |
|
||||
| onClose | 関数 | ドロップダウンが閉じられたときにトリガーされるコールバック関数 |
|
||||
| onOpen | 関数 | ドロップダウンが開かれたときにトリガーされるコールバック関数 |
|
||||
| バリアント | string | クリック可能なアイコンのビジュアルスタイルバリアント。 オプションには `primary`、 `secondary`、 `tertiary` が含まれます |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,25 +9,32 @@ title: 画像入力
|
||||
ユーザーが画像をアップロードおよび削除できるようにします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { ImageInput } from "@/ui/input/components/ImageInput";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ImageInput/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ImageInput } from "@/ui/input/components/ImageInput";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 写真 | string | 画像ソースURL |
|
||||
| onUpload | 関数 | 新しい画像がアップロードされたときに呼び出される関数。 新しい画像がアップロードされたときに呼び出される関数。 パラメーターとして `File` オブジェクトを受け取ります 新しい画像がアップロードされたときに呼び出される関数。 パラメーターとして `File` オブジェクトを受け取ります |
|
||||
| onRemove | 関数 | ユーザーが削除ボタンをクリックしたときに呼び出される関数 |
|
||||
| onAbort | 関数 | 画像のアップロード中に中止ボタンをクリックしたときに呼び出される関数 |
|
||||
| isUploading | ブール型 | 画像が現在アップロード中であるかどうかを示します |
|
||||
| errorMessage | string | 画像入力の下に表示されるオプションのエラーメッセージ |
|
||||
| disabled | ブール型 | `true` の場合、入力全体が無効化され、ボタンをクリックできなくなります。 |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return <ImageInput/>;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 写真 | string | 画像ソースURL |
|
||||
| onUpload | 関数 | 新しい画像がアップロードされたときに呼び出される関数。 新しい画像がアップロードされたときに呼び出される関数。 パラメーターとして `File` オブジェクトを受け取ります 新しい画像がアップロードされたときに呼び出される関数。 パラメーターとして `File` オブジェクトを受け取ります |
|
||||
| onRemove | 関数 | ユーザーが削除ボタンをクリックしたときに呼び出される関数 |
|
||||
| onAbort | 関数 | 画像のアップロード中に中止ボタンをクリックしたときに呼び出される関数 |
|
||||
| isUploading | ブール型 | 画像が現在アップロード中であるかどうかを示します |
|
||||
| errorMessage | string | 画像入力の下に表示されるオプションのエラーメッセージ |
|
||||
| disabled | ブール型 | `true` の場合、入力全体が無効化され、ボタンをクリックできなくなります。 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: ラジオ
|
||||
icon: circle-dot
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -9,50 +10,58 @@ title: ラジオ
|
||||
ユーザーが一連の選択肢の中から 1 つのオプションのみを選ぶことができるときに使用されます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Radio } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import { Radio } from "twenty-ui/display";
|
||||
|
||||
const handleRadioChange = (event) => {
|
||||
console.log("Radio button changed:", event.target.checked);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
|
||||
const handleCheckedChange = (checked) => {
|
||||
console.log("Checked state changed:", checked);
|
||||
};
|
||||
const handleRadioChange = (event) => {
|
||||
console.log("Radio button changed:", event.target.checked);
|
||||
};
|
||||
|
||||
const handleCheckedChange = (checked) => {
|
||||
console.log("Checked state changed:", checked);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Radio
|
||||
checked={true}
|
||||
value="Option 1"
|
||||
onChange={handleRadioChange}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
size="large"
|
||||
disabled={false}
|
||||
labelPosition="right"
|
||||
/>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Radio
|
||||
checked={true}
|
||||
value="Option 1"
|
||||
onChange={handleRadioChange}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
size="large"
|
||||
disabled={false}
|
||||
labelPosition="right"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
```
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------------- | ----------------- | ------------------------------------------------ |
|
||||
| スタイル | `React.CSS` プロパティ | コンポーネントの追加インラインスタイル |
|
||||
| className | string | 追加のスタイリング用のオプションのCSSクラス |
|
||||
| checked | ブール型 | ラジオボタンが選択されているかどうかを示す |
|
||||
| 値 | string | ラジオボタンに関連付けられたラベルまたはテキスト |
|
||||
| onChange | 関数 | 選択したラジオボタンが変更されたときに呼ばれる関数 |
|
||||
| onCheckedChange | 関数 | ラジオボタンの「checked」状態が変更されたときに呼び出される関数 |
|
||||
| サイズ | string | ラジオボタンのサイズ。 オプションには、`large` と `small` があります |
|
||||
| disabled | ブール型 | 「true」の場合、ラジオボタンは無効になり、クリックできなくなります |
|
||||
| ラベルの位置 | string | ラジオボタンに対するラベルテキストの位置。 2 つのオプション:`left` と `right` |
|
||||
</Tab>
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------------- | ----------------- | ------------------------------------------------ |
|
||||
| スタイル | `React.CSS` プロパティ | コンポーネントの追加インラインスタイル |
|
||||
| className | string | 追加のスタイリング用のオプションのCSSクラス |
|
||||
| checked | ブール型 | ラジオボタンが選択されているかどうかを示す |
|
||||
| 値 | string | ラジオボタンに関連付けられたラベルまたはテキスト |
|
||||
| onChange | 関数 | 選択したラジオボタンが変更されたときに呼ばれる関数 |
|
||||
| onCheckedChange | 関数 | ラジオボタンの「checked」状態が変更されたときに呼び出される関数 |
|
||||
| サイズ | string | ラジオボタンのサイズ。 オプションには、`large` と `small` があります |
|
||||
| disabled | ブール型 | 「true」の場合、ラジオボタンは無効になり、クリックできなくなります |
|
||||
| ラベルの位置 | string | ラジオボタンに対するラベルテキストの位置。 2 つのオプション:`left` と `right` |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ラジオグループ
|
||||
@@ -60,37 +69,45 @@ title: ラジオ
|
||||
関連するラジオボタンをまとめます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { Radio, RadioGroup } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { Radio, RadioGroup } from "twenty-ui/display";
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState("Option 1");
|
||||
export const MyComponent = () => {
|
||||
|
||||
const handleChange = (event) => {
|
||||
setSelectedValue(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<RadioGroup value={selectedValue} onChange={handleChange}>
|
||||
<Radio value="Option 1" />
|
||||
<Radio value="Option 2" />
|
||||
<Radio value="Option 3" />
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
const [selectedValue, setSelectedValue] = useState("Option 1");
|
||||
|
||||
```
|
||||
</Tab>
|
||||
const handleChange = (event) => {
|
||||
setSelectedValue(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<RadioGroup value={selectedValue} onChange={handleChange}>
|
||||
<Radio value="Option 1" />
|
||||
<Radio value="Option 2" />
|
||||
<Radio value="Option 3" />
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------- | ----------------- | ---------------------------------------------------------------------------------- |
|
||||
| 値 | string | 現在選択されているラジオボタンの値 |
|
||||
| onChange | 機能 | ラジオボタンが変更されたときにトリガーされるコールバック関数 |
|
||||
| onValueChange | 機能 | グループ内の選択された値が変更されたときにトリガーされるコールバック関数。 |
|
||||
| children | `React.ReactNode` | Allows you to pass React components (such as Radio) as children to the Radio Group |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------- | ----------------- | ---------------------------------------------------------------------------------- |
|
||||
| 値 | string | 現在選択されているラジオボタンの値 |
|
||||
| onChange | 機能 | ラジオボタンが変更されたときにトリガーされるコールバック関数 |
|
||||
| onValueChange | 機能 | グループ内の選択された値が変更されたときにトリガーされるコールバック関数。 |
|
||||
| children | `React.ReactNode` | Allows you to pass React components (such as Radio) as children to the Radio Group |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,39 +9,46 @@ title: 選択する
|
||||
ユーザーが事前定義されたオプションのリストから値を選択できるようにします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
<Tab title="使用方法">
|
||||
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
```jsx
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
|
||||
return (
|
||||
<Select
|
||||
className
|
||||
disabled={false}
|
||||
label="Select an option"
|
||||
options={[
|
||||
{ value: 'option1', label: 'Option A', Icon: IconTwentyStar },
|
||||
{ value: 'option2', label: 'Option B', Icon: IconTwentyStar },
|
||||
]}
|
||||
value="option1"
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<Select
|
||||
className
|
||||
disabled={false}
|
||||
label="オプションを選択"
|
||||
options={[
|
||||
{ value: 'option1', label: 'オプションA', Icon: IconTwentyStar },
|
||||
{ value: 'option2', label: 'オプションB', Icon: IconTwentyStar },
|
||||
]}
|
||||
value="option1"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加のスタイリング用のオプションのCSSクラス |
|
||||
| disabled | ブール型 | `true` に設定すると、コンポーネントとのユーザーインタラクションが無効になります。 |
|
||||
| ラベル | string | `選択` コンポーネントの目的を説明するラベル |
|
||||
| onChange | 関数 | 選択された値が変更されたときに呼び出される機能 |
|
||||
| オプション | 配列 | Represents the options available for the `Selected` component. 各オブジェクトが `value`(一意の識別子)、`label`(一意の識別子)、およびオプションの `アイコン` を持つオブジェクトの配列です。 |
|
||||
| 値 | string | 現在選択されている値を表します。 その `options` 配列の `value` プロパティのいずれかと一致する必要があります。 |
|
||||
</Tab>
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加のスタイリング用のオプションのCSSクラス |
|
||||
| disabled | ブール型 | `true` に設定すると、コンポーネントとのユーザーインタラクションが無効になります。 |
|
||||
| ラベル | string | `選択` コンポーネントの目的を説明するラベル |
|
||||
| onChange | 関数 | 選択された値が変更されたときに呼び出される機能 |
|
||||
| オプション | 配列 | Represents the options available for the `Selected` component. 各オブジェクトが `value`(一意の識別子)、`label`(一意の識別子)、およびオプションの `アイコン` を持つオブジェクトの配列です。 |
|
||||
| 値 | string | 現在選択されている値を表します。 その `options` 配列の `value` プロパティのいずれかと一致する必要があります。 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -11,50 +11,61 @@ title: テキスト
|
||||
ユーザーがテキストを入力および編集できるようにします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { TextInput } from "@/ui/input/components/TextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("入力が変更されました:", text);
|
||||
};
|
||||
<Tab title="使用方法">
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("キーが押されました:", event.key);
|
||||
};
|
||||
```jsx
|
||||
import { TextInput } from "@/ui/input/components/TextInput";
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
className
|
||||
label="ユーザー名"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="無効なユーザー名"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("入力が変更されました:", text);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("キーが押されました:", event.key);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
| ラベル | string | 入力のラベルを表します。 |
|
||||
| onChange | function | 入力値が変わるときに呼び出される関数 |
|
||||
| fullWidth | ブール型 | Indicates whether the input should take up 100% of the width |
|
||||
| disableHotkeys | ブール型 | Indicates whether hotkeys are enabled for the input |
|
||||
| エラー | string | 表示されるエラーメッセージを表します。 指定された場合、入力の右側にアイコンエラーも追加されます。 |
|
||||
| onKeyDown | 機能 | 入力フィールドにフォーカスがあるときにキーが押されたときに呼び出されます。 入力フィールドにフォーカスがあるときにキーが押されたときに呼び出されます。 Receives a `React.KeyboardEvent` as an argument |
|
||||
| 右アイコン | アイコンコンポーネント | 入力の右側に表示されるオプションのアイコンコンポーネント |
|
||||
return (
|
||||
<TextInput
|
||||
className
|
||||
label="ユーザー名"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="無効なユーザー名"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
| ラベル | string | 入力のラベルを表します。 |
|
||||
| onChange | function | 入力値が変わるときに呼び出される関数 |
|
||||
| fullWidth | ブール型 | Indicates whether the input should take up 100% of the width |
|
||||
| disableHotkeys | ブール型 | Indicates whether hotkeys are enabled for the input |
|
||||
| エラー | string | 表示されるエラーメッセージを表します。 指定された場合、入力の右側にアイコンエラーも追加されます。 |
|
||||
| onKeyDown | 機能 | 入力フィールドにフォーカスがあるときにキーが押されたときに呼び出されます。 入力フィールドにフォーカスがあるときにキーが押されたときに呼び出されます。 Receives a `React.KeyboardEvent` as an argument |
|
||||
| 右アイコン | アイコンコンポーネント | 入力の右側に表示されるオプションのアイコンコンポーネント |
|
||||
|
||||
|
||||
|
||||
|
||||
コンポーネントは、他の HTML 入力要素のプロパティも受け入れます。
|
||||
|
||||
</Tab>
|
||||
|
||||
コンポーネントは、他の HTML 入力要素のプロパティも受け入れます。
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## オートサイズテキスト入力
|
||||
@@ -62,37 +73,48 @@ title: テキスト
|
||||
コンテンツに基づいて高さを自動調整するテキスト入力コンポーネント。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { AutosizeTextInput } from "@/ui/input/components/AutosizeTextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("onValidate function fired")}
|
||||
minRows={1}
|
||||
placeholder="Write a comment"
|
||||
onFocus={() => console.log("onFocus function fired")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="Task: "
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
```jsx
|
||||
import { AutosizeTextInput } from "@/ui/input/components/AutosizeTextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("onValidate 関数が発火しました")}
|
||||
minRows={1}
|
||||
placeholder="コメントを書く"
|
||||
onFocus={() => console.log("onFocus 関数が発火しました")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="タスク: "
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ---------- | ------ | ------------------------------------------------------------------------ |
|
||||
| onValidate | 機能 | ユーザーが入力を検証するときにトリガーされるコールバック関数 |
|
||||
| 最小行数 | 数 | The minimum number of rows for the text area |
|
||||
| プレースホルダー | string | The placeholder text you want to display when the text area is empty |
|
||||
| onFocus | 機能 | テキストエリアにフォーカスが当たったときにトリガーされるコールバック関数 |
|
||||
| バリアント | string | The variant of the input. オプションには `default`、 `icon`、および `button` が含まれます。 |
|
||||
| ボタンタイトル | string | ボタンのタイトル(ボタンのバリアントの場合のみ適用可能) |
|
||||
| 値 | string | テキストエリアの初期値 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ---------- | ------ | ------------------------------------------------------------------------ |
|
||||
| onValidate | 機能 | ユーザーが入力を検証するときにトリガーされるコールバック関数 |
|
||||
| 最小行数 | 数 | The minimum number of rows for the text area |
|
||||
| プレースホルダー | string | The placeholder text you want to display when the text area is empty |
|
||||
| onFocus | 機能 | テキストエリアにフォーカスが当たったときにトリガーされるコールバック関数 |
|
||||
| バリアント | string | The variant of the input. オプションには `default`、 `icon`、および `button` が含まれます。 |
|
||||
| ボタンタイトル | string | ボタンのタイトル(ボタンのバリアントの場合のみ適用可能) |
|
||||
| 値 | string | テキストエリアの初期値 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## テキストエリア
|
||||
@@ -100,31 +122,40 @@ title: テキスト
|
||||
複数行のテキスト入力を作成できます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { TextArea } from "@/ui/input/components/TextArea";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('On change function fired')}
|
||||
placeholder="Enter text here"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { TextArea } from "@/ui/input/components/TextArea";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | ------ | ---------------------------------- |
|
||||
| 無効 | ブール型 | テキストエリアが無効かどうかを示します |
|
||||
| 最小行数 | 数 | テキストエリアの最小可視行数。 |
|
||||
| onChange | 機能 | テキストエリアの内容が変更されたときにトリガーされるコールバック関数 |
|
||||
| プレースホルダー | string | テキストエリアが空のときに表示されるプレースホルダーテキスト |
|
||||
| 値 | string | テキストエリアの現在の値 |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('On change function fired')}
|
||||
placeholder="Enter text here"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | ------ | ---------------------------------- |
|
||||
| 無効 | ブール型 | テキストエリアが無効かどうかを示します |
|
||||
| 最小行数 | 数 | テキストエリアの最小可視行数。 |
|
||||
| onChange | 機能 | テキストエリアの内容が変更されたときにトリガーされるコールバック関数 |
|
||||
| プレースホルダー | string | テキストエリアが空のときに表示されるプレースホルダーテキスト |
|
||||
| 値 | string | テキストエリアの現在の値 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
---
|
||||
title: 切り替え
|
||||
icon: toggle-on
|
||||
---
|
||||
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { Toggle } from "twenty-ui/input";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Toggle
|
||||
value = {true}
|
||||
onChange = {()=>console.log('On Change event')}
|
||||
color="green"
|
||||
toggleSize = "medium"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Toggle } from "twenty-ui/input";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| -------- | ------ | --------------------------------------------------------------------------- | ----- |
|
||||
| 値 | ブール型 | トグルの現在の状態 | `偽` |
|
||||
| onChange | 機能 | トグルスイッチの状態が変化した際にトリガーされるコールバック関数 | |
|
||||
| カラー | string | トグルの色\ | 青色 |
|
||||
| トグルサイズ | string | トグルのサイズは、高さと幅の両方に影響します。 トグルのサイズは、高さと幅の両方に影響します。 オプションは2つ:`small` と `medium` | 中 |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Toggle
|
||||
value = {true}
|
||||
onChange = {()=>console.log('On Change event')}
|
||||
color="green"
|
||||
toggleSize = "medium"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| -------- | ------ | --------------------------------------------------------------------------- | ----- |
|
||||
| 値 | ブール型 | トグルの現在の状態 | `偽` |
|
||||
| onChange | 機能 | トグルスイッチの状態が変化した際にトリガーされるコールバック関数 | |
|
||||
| カラー | string | トグルの色\ | 青色 |
|
||||
| トグルサイズ | string | トグルのサイズは、高さと幅の両方に影響します。 トグルのサイズは、高さと幅の両方に影響します。 オプションは2つ:`small` と `medium` | 中 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: 概要
|
||||
icon: palette
|
||||
description: Twenty CRMのためのコンポーネントライブラリ
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: ナビゲーション
|
||||
image: '""'
|
||||
icon: compass
|
||||
---
|
||||
|
||||
<Frame>
|
||||
|
||||
@@ -9,32 +9,38 @@ title: ブレッドクラム
|
||||
ブレッドクラムナビゲーションバーをレンダリングします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Breadcrumb } from "@/ui/navigation/bread-crumb/components/Breadcrumb";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const breadcrumbLinks = [
|
||||
{ children: "Home", href: "/" },
|
||||
{ children: "Category", href: "/category" },
|
||||
{ children: "Subcategory", href: "/category/subcategory" },
|
||||
{ children: "Current Page" },
|
||||
];
|
||||
```jsx
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Breadcrumb } from "@/ui/navigation/bread-crumb/components/Breadcrumb";
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Breadcrumb className links={breadcrumbLinks} />
|
||||
</BrowserRouter>
|
||||
)
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const breadcrumbLinks = [
|
||||
{ children: "Home", href: "/" },
|
||||
{ children: "Category", href: "/category" },
|
||||
{ children: "Subcategory", href: "/category/subcategory" },
|
||||
{ children: "Current Page" },
|
||||
];
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加のスタイリングのためのオプションクラス名 |
|
||||
| リンク | 配列 | 各オブジェクトはブレッドクラムリンクを表します。 各オブジェクトには `children` プロパティ(リンクのテキストコンテンツ)とオプションの `href` プロパティ(リンクをクリックしたときに移動するURL)が含まれています。 |
|
||||
</Tab>
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Breadcrumb className links={breadcrumbLinks} />
|
||||
</BrowserRouter>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | 追加のスタイリングのためのオプションクラス名 |
|
||||
| リンク | 配列 | 各オブジェクトはブレッドクラムリンクを表します。 各オブジェクトには `children` プロパティ(リンクのテキストコンテンツ)とオプションの `href` プロパティ(リンクをクリックしたときに移動するURL)が含まれています。 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: リンク
|
||||
icon: link
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -11,40 +12,49 @@ title: リンク
|
||||
連絡先情報を表示するためのスタイライズされたリンクコンポーネントです。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
<Tab title="使用方法">
|
||||
|
||||
import { ContactLink } from 'twenty-ui/navigation';
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('Contact link clicked!', event);
|
||||
};
|
||||
import { ContactLink } from 'twenty-ui/navigation';
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ContactLink
|
||||
className
|
||||
href="mailto:example@example.com"
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
example@example.com
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('Contact link clicked!', event);
|
||||
};
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ContactLink
|
||||
className
|
||||
href="mailto:example@example.com"
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
example@example.com
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------------- | ---------------------------- |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| onClick | function | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------------- | ---------------------------- |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| onClick | function | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 生リンク
|
||||
@@ -52,36 +62,44 @@ title: リンク
|
||||
リンクを表示するためのスタイライズされたリンクコンポーネントです。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { RawLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { RawLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RawLink className href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RawLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<Router>
|
||||
<RawLink className href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RawLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------------- | ---------------------------- |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| onClick | 機能 | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
</Tab>
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------------- | ---------------------------- |
|
||||
| className | string | 追加のスタイリングのためのオプション名 |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| onClick | 機能 | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 丸リンク
|
||||
@@ -89,34 +107,41 @@ title: リンク
|
||||
リンクのためのチップコンポーネントを備えた丸いスタイルのリンクです。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { RoundedLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { RoundedLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RoundedLink href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RoundedLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | ----------------- | ---------------------------- |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
| onClick | 機能 | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
</Tab>
|
||||
return (
|
||||
<Router>
|
||||
<RoundedLink href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RoundedLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | ----------------- | ---------------------------- |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
| onClick | 機能 | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## ソーシャルリンク
|
||||
@@ -124,30 +149,37 @@ title: リンク
|
||||
URL、LinkedIn、X(またはTwitter)など、さまざまなソーシャルリンクタイプに対応したスタイライズされたソーシャルリンクです。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { SocialLink } from "twenty-ui/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<SocialLink
|
||||
type="twitter"
|
||||
href="https://twitter.com/twentycrm"
|
||||
></SocialLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { SocialLink } from "twenty-ui/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | ----------------- | ------------------------------------------------------- |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
| タイプ | string | ソーシャルリンクの種類です。 オプションには、`url`、`LinkedIn`、`Twitter`があります。 |
|
||||
| onClick | function | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<SocialLink
|
||||
type="twitter"
|
||||
href="https://twitter.com/twentycrm"
|
||||
></SocialLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------- | ----------------- | ------------------------------------------------------- |
|
||||
| href | string | リンクのターゲットURLまたはパス |
|
||||
| children | `React.ReactNode` | リンク内に表示されるコンテンツ |
|
||||
| タイプ | string | ソーシャルリンクの種類です。 オプションには、`url`、`LinkedIn`、`Twitter`があります。 |
|
||||
| onClick | function | リンクがクリックされた際にトリガーされるコールバック関数 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,54 +1,62 @@
|
||||
---
|
||||
title: メニュー項目
|
||||
icon: bars
|
||||
---
|
||||
|
||||
|
||||
メニューまたはナビゲーションリストで使用するために設計された汎用性の高いメニュー項目です。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItem } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItem } from "twenty-ui/display";
|
||||
|
||||
const handleButtonClick = (event) => {
|
||||
console.log("Icon button clicked!", event);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
text="Menu item text"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={true}
|
||||
testId="menu-item-1"
|
||||
onClick={handleMenuItemClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
const handleButtonClick = (event) => {
|
||||
console.log("Icon button clicked!", event);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------- | ----------- | -------------------------------------------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| アクセント | string | メニュー項目のアクセントカラーを指定します。 オプションは `default`、`danger`、`placeholder` を含みます |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| アイコンボタン | 配列 | メニュー項目に関連する追加のアイコンボタンを表すオブジェクトの配列 |
|
||||
| ツールチップオープンフラグ | ブール型 | メニュー項目に関連するツールチップの表示状態を制御する |
|
||||
| テストID | string | テスト目的のためのdata-testid属性 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItem
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
text="Menu item text"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={true}
|
||||
testId="menu-item-1"
|
||||
onClick={handleMenuItemClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------- | ----------- | -------------------------------------------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| アクセント | string | メニュー項目のアクセントカラーを指定します。 オプションは `default`、`danger`、`placeholder` を含みます |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| アイコンボタン | 配列 | メニュー項目に関連する追加のアイコンボタンを表すオブジェクトの配列 |
|
||||
| ツールチップオープンフラグ | ブール型 | メニュー項目に関連するツールチップの表示状態を制御する |
|
||||
| テストID | string | テスト目的のためのdata-testid属性 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## バリエーション
|
||||
@@ -60,42 +68,49 @@ title: メニュー項目
|
||||
キーボードショートカットを示すためのメニュー内のコマンド形式のメニュー項目。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemCommand } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleCommandClick = () => {
|
||||
console.log("Command clicked!");
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemCommand } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
firstHotKey="⌘"
|
||||
secondHotKey="1"
|
||||
isSelected={true}
|
||||
onClick={handleCommandClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleCommandClick = () => {
|
||||
console.log("Command clicked!");
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| firstHotKey | string | コマンドに関連する最初のキーボードショートカット |
|
||||
| secondHotKey | string | コマンドに関連する2番目のキーボードショートカット |
|
||||
| isSelected | ブール型 | メニュー項目が選択またはハイライトされているかどうかを示します |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemCommand
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
firstHotKey="⌘"
|
||||
secondHotKey="1"
|
||||
isSelected={true}
|
||||
onClick={handleCommandClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| ------------ | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| firstHotKey | string | コマンドに関連する最初のキーボードショートカット |
|
||||
| secondHotKey | string | コマンドに関連する2番目のキーボードショートカット |
|
||||
| isSelected | ブール型 | メニュー項目が選択またはハイライトされているかどうかを示します |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### ドラッグ可能
|
||||
@@ -103,45 +118,52 @@ title: メニュー項目
|
||||
メニューまたはリスト内で項目をドラッグし、アイコンボタンを通じて追加のアクションを実行するために設計されたドラッグ可能なメニュー項目コンポーネント。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItemDraggable } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItemDraggable } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemDraggable
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={false}
|
||||
onClick={handleMenuItemClick}
|
||||
text="Menu item draggable"
|
||||
isDragDisabled={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | ------------------------------------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| アクセント | string | メニュー項目のアクセントカラー。 `default`、`placeholder`、および `danger` のいずれかです |
|
||||
| アイコンボタン | 配列 | メニュー項目に関連する追加のアイコンボタンを表すオブジェクトの配列 |
|
||||
| ツールチップオープンフラグ | ブール型 | メニュー項目に関連するツールチップの表示状態を制御します |
|
||||
| onClick | 関数 | リンクがクリックされるとトリガーされるコールバック関数 |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| isDragDisabled | ブール型 | ドラッグが無効かどうかを示します |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemDraggable
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={false}
|
||||
onClick={handleMenuItemClick}
|
||||
text="Menu item draggable"
|
||||
isDragDisabled={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | ------------------------------------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| アクセント | string | メニュー項目のアクセントカラー。 `default`、`placeholder`、および `danger` のいずれかです |
|
||||
| アイコンボタン | 配列 | メニュー項目に関連する追加のアイコンボタンを表すオブジェクトの配列 |
|
||||
| ツールチップオープンフラグ | ブール型 | メニュー項目に関連するツールチップの表示状態を制御します |
|
||||
| onClick | 関数 | リンクがクリックされるとトリガーされるコールバック関数 |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| isDragDisabled | ブール型 | ドラッグが無効かどうかを示します |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### マルチ選択
|
||||
@@ -149,34 +171,41 @@ title: メニュー項目
|
||||
関連するチェックボックスを使用してマルチ選択機能を実装する方法を提供します。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemMultiSelect } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemMultiSelect } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemMultiSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | ----------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| onSelectChange | 関数 | チェックボックスの状態が変更されたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemMultiSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | ----------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| onSelectChange | 関数 | チェックボックスの状態が変更されたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### マルチ選択アバター
|
||||
@@ -184,35 +213,42 @@ title: メニュー項目
|
||||
選択用のチェックボックスとテキストコンテンツを備えたアバター付きのマルチ選択メニュー項目。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { MenuItemMultiSelectAvatar } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
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=";
|
||||
```jsx
|
||||
import { MenuItemMultiSelectAvatar } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
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=";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | ----------------------------------- |
|
||||
| avatar | `ReactNode` | メニュー項目の左側に表示されるアバターまたはアイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| onSelectChange | 関数 | チェックボックスの状態が変更されたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | ----------------------------------- |
|
||||
| avatar | `ReactNode` | メニュー項目の左側に表示されるアバターまたはアイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| onSelectChange | 関数 | チェックボックスの状態が変更されたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### ナビゲート
|
||||
@@ -220,36 +256,43 @@ title: メニュー項目
|
||||
オプションの左アイコン、テキストコンテンツ、および右のチューロマークを特徴とするメニュー項目。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemNavigate } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleNavigation = () => {
|
||||
console.log("Navigate to another page");
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemNavigate } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemNavigate
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
onClick={handleNavigation}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleNavigation = () => {
|
||||
console.log("Navigate to another page");
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemNavigate
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
onClick={handleNavigation}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 選択
|
||||
@@ -257,42 +300,49 @@ title: メニュー項目
|
||||
オプションの左コンテンツ(アイコンとテキスト)と選択状態のインジケーター(チェックアイコン)を備えた選択可能なメニュー項目。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemSelect } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemSelect } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| 無効 | ブール型 | メニュー項目が無効かどうかを示します |
|
||||
| hovered | ブール型 | メニュー項目が現在ホバーされているかどうかを示します |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| 無効 | ブール型 | メニュー項目が無効かどうかを示します |
|
||||
| hovered | ブール型 | メニュー項目が現在ホバーされているかどうかを示します |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 選択アバター
|
||||
@@ -300,47 +350,54 @@ title: メニュー項目
|
||||
アバター付きの選択可能なメニュー項目、オプションの左コンテンツ(アバターとテキスト)および選択状態のためのインジケーター(チェックアイコン)。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { MenuItemSelectAvatar } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
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=";
|
||||
```jsx
|
||||
import { MenuItemSelectAvatar } from "twenty-ui/display";
|
||||
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
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=";
|
||||
|
||||
return (
|
||||
<MenuItemSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
testId="menu-item-test"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
testId="menu-item-test"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------- | -------------------------------- |
|
||||
| avatar | `ReactNode` | メニュー項目の左側に表示されるアバターまたはアイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| 無効 | ブール型 | メニュー項目が無効かどうかを示します |
|
||||
| hovered | ブール型 | メニュー項目が現在ホバーされているかどうかを示します |
|
||||
| テストID | string | テスト目的のためのdata-testid属性 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ----------- | -------------------------------- |
|
||||
| avatar | `ReactNode` | メニュー項目の左側に表示されるアバターまたはアイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| selected | ブール型 | メニュー項目が選択されているかどうかを示します |
|
||||
| 無効 | ブール型 | メニュー項目が無効かどうかを示します |
|
||||
| hovered | ブール型 | メニュー項目が現在ホバーされているかどうかを示します |
|
||||
| テストID | string | テスト目的のためのdata-testid属性 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 色を選択
|
||||
@@ -348,41 +405,48 @@ title: メニュー項目
|
||||
ユーザーがメニューから色を選択できるシナリオに使用するための、カラーパレットサンプルを備えた選択可能なメニュー項目。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { MenuItemSelectColor } from "twenty-ui/display";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
```jsx
|
||||
import { MenuItemSelectColor } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemSelectColor
|
||||
color="green"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={true}
|
||||
variant="default"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| カラー | string | メニュー項目にサンプルとして表示されるテーマカラー。 選択肢には、`green`、`turquoise`、`sky`、`blue`、`purple`、`pink`、`red`、`orange`、`yellow`、`gray`があります。 |
|
||||
| 選択済み | ブール型 | メニュー項目が選択されている(チェックされている)かどうかを示します |
|
||||
| 無効 | ブール型 | メニュー項目が無効かどうかを示します |
|
||||
| ホバー | ブール型 | メニュー項目が現在ホバーされているかどうかを示します |
|
||||
| バリアント | string | 色サンプルのバリアント。 `default` または `pipeline` のいずれかです。 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemSelectColor
|
||||
color="green"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={true}
|
||||
variant="default"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| カラー | string | メニュー項目にサンプルとして表示されるテーマカラー。 選択肢には、`green`、`turquoise`、`sky`、`blue`、`purple`、`pink`、`red`、`orange`、`yellow`、`gray`があります。 |
|
||||
| 選択済み | ブール型 | メニュー項目が選択されている(チェックされている)かどうかを示します |
|
||||
| 無効 | ブール型 | メニュー項目が無効かどうかを示します |
|
||||
| ホバー | ブール型 | メニュー項目が現在ホバーされているかどうかを示します |
|
||||
| バリアント | string | 色サンプルのバリアント。 `default` または `pipeline` のいずれかです。 |
|
||||
| onClick | 関数 | メニュー項目がクリックされたときにトリガーされるコールバック関数 |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 切り替え
|
||||
@@ -390,35 +454,42 @@ title: メニュー項目
|
||||
特定の機能を有効または無効にするためのトグルスイッチが付いたメニュー項目
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconBell } from '@tabler/icons-react';
|
||||
<Tab title="使用方法">
|
||||
|
||||
import { MenuItemToggle } from 'twenty-ui/display';
|
||||
```jsx
|
||||
import { IconBell } from '@tabler/icons-react';
|
||||
|
||||
export const MyComponent = () => {
|
||||
import { MenuItemToggle } from 'twenty-ui/display';
|
||||
|
||||
return (
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
toggled={true}
|
||||
toggleSize="small"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| 切り替え済み | ブール型 | トグルスイッチが「オン」または「オフ」かを示します |
|
||||
| onToggleChange | 関数 | トグルスイッチの状態が変化した際にトリガーされるコールバック関数 |
|
||||
| toggleSize | string | トグルスイッチのサイズです。 いずれかになります \ |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
toggled={true}
|
||||
toggleSize="small"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ----------- | -------------------------------- |
|
||||
| 左アイコン | アイコンコンポーネント | メニュー項目のテキストの前に表示されるオプションの左アイコン |
|
||||
| テキスト | string | メニュー項目のテキスト内容 |
|
||||
| 切り替え済み | ブール型 | トグルスイッチが「オン」または「オフ」かを示します |
|
||||
| onToggleChange | 関数 | トグルスイッチの状態が変化した際にトリガーされるコールバック関数 |
|
||||
| toggleSize | string | トグルスイッチのサイズです。 いずれかになります \ |
|
||||
| className | string | 追加スタイル用のオプション名 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,45 +1,52 @@
|
||||
---
|
||||
title: ナビゲーションバー
|
||||
icon: bars
|
||||
---
|
||||
|
||||
|
||||
複数の`NavigationBarItem`コンポーネントを含むナビゲーションバーをレンダリングします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { IconHome, IconUser, IconSettings } from '@tabler/icons-react';
|
||||
import { NavigationBar } from "@/ui/navigation/navigation-bar/components/NavigationBar";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import { IconHome, IconUser, IconSettings } from '@tabler/icons-react';
|
||||
import { NavigationBar } from "@/ui/navigation/navigation-bar/components/NavigationBar";
|
||||
|
||||
const navigationItems = [
|
||||
{
|
||||
name: "Home",
|
||||
Icon: IconHome,
|
||||
onClick: () => console.log("Home clicked"),
|
||||
},
|
||||
{
|
||||
name: "Profile",
|
||||
Icon: IconUser,
|
||||
onClick: () => console.log("Profile clicked"),
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
Icon: IconSettings,
|
||||
onClick: () => console.log("Settings clicked"),
|
||||
},
|
||||
];
|
||||
export const MyComponent = () => {
|
||||
|
||||
return <NavigationBar activeItemName="Home" items={navigationItems}/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
const navigationItems = [
|
||||
{
|
||||
name: "Home",
|
||||
Icon: IconHome,
|
||||
onClick: () => console.log("Home clicked"),
|
||||
},
|
||||
{
|
||||
name: "Profile",
|
||||
Icon: IconUser,
|
||||
onClick: () => console.log("Profile clicked"),
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
Icon: IconSettings,
|
||||
onClick: () => console.log("Settings clicked"),
|
||||
},
|
||||
];
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| activeItemName | string | 現在アクティブなナビゲーション項目の名前 |
|
||||
| アイテム | 配列 | 各ナビゲーションアイテムを表すオブジェクトの配列。 各オブジェクトには、アイテムの `name`、表示する `Icon` コンポーネント、およびアイテムがクリックされたときに呼び出される `onClick` 関数が含まれています。 |
|
||||
</Tab>
|
||||
return <NavigationBar activeItemName="Home" items={navigationItems}/>;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| activeItemName | string | 現在アクティブなナビゲーション項目の名前 |
|
||||
| アイテム | 配列 | 各ナビゲーションアイテムを表すオブジェクトの配列。 各オブジェクトには、アイテムの `name`、表示する `Icon` コンポーネント、およびアイテムがクリックされたときに呼び出される `onClick` 関数が含まれています。 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,25 +9,33 @@ title: ステップバー
|
||||
アクティブなステップをハイライトして、一連の番号付きステップの進行状況を表示します。 各 `Step` コンポーネントによって表されるステップを含むコンテナをレンダリングします。 各 `Step` コンポーネントによって表されるステップを含むコンテナをレンダリングします。 アクティブなステップをハイライトして、一連の番号付きステップの進行状況を表示します。 各 `Step` コンポーネントによって表されるステップを含むコンテナをレンダリングします。 各 `Step` コンポーネントによって表されるステップを含むコンテナをレンダリングします。 各 `Step` コンポーネントによって表されるステップを含むコンテナをレンダリングします。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { StepBar } from "@/ui/navigation/step-bar/components/StepBar";
|
||||
<Tab title="使用方法">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<StepBar activeStep={2}>
|
||||
<StepBar.Step>Step 1</StepBar.Step>
|
||||
<StepBar.Step>Step 2</StepBar.Step>
|
||||
<StepBar.Step>Step 3</StepBar.Step>
|
||||
</StepBar>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { StepBar } from "@/ui/navigation/step-bar/components/StepBar";
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --- | ----------------------------------------------------- |
|
||||
| アクティブステップ | 数 | 現在アクティブなステップのインデックス。 これにより、どのステップを視覚的にハイライトするかが決まります。 |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<StepBar activeStep={2}>
|
||||
<StepBar.Step>Step 1</StepBar.Step>
|
||||
<StepBar.Step>Step 2</StepBar.Step>
|
||||
<StepBar.Step>Step 3</StepBar.Step>
|
||||
</StepBar>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 |
|
||||
| --------- | --- | ----------------------------------------------------- |
|
||||
| アクティブステップ | 数 | 現在アクティブなステップのインデックス。 これにより、どのステップを視覚的にハイライトするかが決まります。 |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -2,39 +2,46 @@
|
||||
title: フィードバック
|
||||
---
|
||||
|
||||
|
||||
進捗やカウントダウンを示し、右から左に動きます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { ProgressBar } from "twenty-ui/feedback";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ProgressBar
|
||||
duration={6000}
|
||||
delay={0}
|
||||
easing="easeInOut"
|
||||
barHeight={10}
|
||||
barColor="#4bb543"
|
||||
autoStart={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | ------ | ------------------------------------------------------ | --------- |
|
||||
| 継続時間 | 数 | プログレスバーアニメーションの総デュレーション(ミリ秒) | 3 |
|
||||
| 遅延 | 数 | プログレスバーアニメーションの開始遅延時間(ミリ秒) | 0 |
|
||||
| イージング | string | プログレスバーアニメーションのイージング関数 | easeInOut |
|
||||
| バーの高さ | 数 | バーの高さ(ピクセル) | 24 |
|
||||
| バーの色 | string | バーの色 | グレー80 |
|
||||
| 自動開始 | ブール型 | `true`の場合、コンポーネントがマウントされたときにプログレスバーアニメーションが自動的に開始されます。 | `真` |
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ProgressBar } from "twenty-ui/feedback";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ProgressBar
|
||||
duration={6000}
|
||||
delay={0}
|
||||
easing="easeInOut"
|
||||
barHeight={10}
|
||||
barColor="#4bb543"
|
||||
autoStart={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | ------ | ------------------------------------------------------ | --------- |
|
||||
| 継続時間 | 数 | プログレスバーアニメーションの総デュレーション(ミリ秒) | 3 |
|
||||
| 遅延 | 数 | プログレスバーアニメーションの開始遅延時間(ミリ秒) | 0 |
|
||||
| イージング | string | プログレスバーアニメーションのイージング関数 | easeInOut |
|
||||
| バーの高さ | 数 | バーの高さ(ピクセル) | 24 |
|
||||
| バーの色 | string | バーの色 | グレー80 |
|
||||
| 自動開始 | ブール型 | `true`の場合、コンポーネントがマウントされたときにプログレスバーアニメーションが自動的に開始されます。 | `真` |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 円形プログレスバー
|
||||
@@ -42,21 +49,30 @@ title: フィードバック
|
||||
タスクの進捗を示し、ユーザーに進行中のプロセスを伝達したい場合に、ロード画面などでよく使用されます。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="使用方法">
|
||||
```jsx
|
||||
import { CircularProgressBar } from "@/ui/feedback/progress-bar/components/CircularProgressBar";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <CircularProgressBar size={80} barWidth={6} barColor="green" />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="使用方法">
|
||||
|
||||
```jsx
|
||||
import { CircularProgressBar } from "@/ui/feedback/progress-bar/components/CircularProgressBar";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <CircularProgressBar size={80} barWidth={6} barColor="green" />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
|
||||
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | ------ | ------------- | ----- |
|
||||
| サイズ | 数 | 円形プログレスバーのサイズ | 50 |
|
||||
| バーの幅 | 数 | プログレスバーのラインの幅 | 5 |
|
||||
| バーの色 | string | プログレスバーの色 | 現在の色 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="プロパティ">
|
||||
| プロパティ | タイプ | 説明 | デフォルト |
|
||||
| ----- | ------ | ------------- | ----- |
|
||||
| サイズ | 数 | 円形プログレスバーのサイズ | 50 |
|
||||
| バーの幅 | 数 | プログレスバーのラインの幅 | 5 |
|
||||
| バーの色 | string | プログレスバーの色 | 現在の色 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -4,7 +4,7 @@ description: AI機能を自動化ワークフローに直接統合します。
|
||||
---
|
||||
|
||||
<Note>
|
||||
この機能は開発中で、まもなくベータ版として利用可能になります。
|
||||
この機能は開発中で、まもなくベータ版として利用可能になります。
|
||||
</Note>
|
||||
|
||||
## 概要
|
||||
|
||||
@@ -4,7 +4,7 @@ description: 自然言語でCRMデータとやり取りできるインテリジ
|
||||
---
|
||||
|
||||
<Note>
|
||||
この機能は開発中で、まもなくベータ版として利用可能になります。
|
||||
この機能は開発中で、まもなくベータ版として利用可能になります。
|
||||
</Note>
|
||||
|
||||
## 概要
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
title: MCP サーバー
|
||||
description: Model Context Protocol を使用して、AI アシスタントを Twenty ワークスペースに接続します。
|
||||
---
|
||||
|
||||
<Warning>
|
||||
MCP は現在**アルファ**版で、一部のワークスペースでのみ利用可能です。 お使いのワークスペースでは、まだ有効化されていない可能性があります。
|
||||
</Warning>
|
||||
|
||||
Twenty は [MCP](https://modelcontextprotocol.io/) サーバーを公開し、Claude Desktop、Claude Code、Cursor、ChatGPT などの AI アシスタントが自然言語で CRM データを読み書きできるようにします。
|
||||
|
||||
MCP エンドポイントとして、あなたの**ワークスペース URL**(Twenty にアクセスするために使用している URL)を使用します。 Twenty Cloud では、ワークスペース URL は `https://{mycompany}.twenty.com` またはカスタムドメインの場合があります。 サーバーは次の場所で利用できます:
|
||||
|
||||
| 環境 | MCP エンドポイント |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **クラウド** | `https://{your-workspace-url}/mcp` (例: `https://mycompany.twenty.com/mcp`) |
|
||||
| **セルフホスト** | `https://{your-domain}/mcp` |
|
||||
|
||||
## 認証方法
|
||||
|
||||
MCP クライアントの認証方法は 2 つあります: **OAuth**(推奨)または **API Key**。
|
||||
|
||||
### オプション A — OAuth(推奨)
|
||||
|
||||
OAuth を使用すると、MCP クライアントがログイン用のブラウザーウィンドウを開きます。 シークレットは設定ファイルに保存されず、トークンは自動的に更新されます。
|
||||
|
||||
<Note>
|
||||
OAuth を利用するには、[MCP Authorization 仕様](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization)をサポートする MCP クライアントが必要です。 Claude Desktop、Claude Code、Cursor、ChatGPT が対応しています。
|
||||
</Note>
|
||||
|
||||
MCP クライアントの設定に次を追加し、`{your-workspace-url}` をワークスペースのホスト(例: `mycompany.twenty.com`)に置き換えてください:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"twenty": {
|
||||
"type": "streamable-http",
|
||||
"url": "https://{your-workspace-url}/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
以上です — API キーは不要です。 クライアントが初回接続する際には、次を実行します:
|
||||
|
||||
1. `/.well-known/oauth-protected-resource` および `/.well-known/oauth-authorization-server` を通じて Twenty の OAuth メタデータを検出する
|
||||
2. 動的クライアント登録(RFC 7591)を通じて自らを OAuth クライアントとして登録する
|
||||
3. アクセスを許可するためにブラウザーを開く
|
||||
4. トークンを受け取り、MCP サーバーに接続する
|
||||
|
||||
以後の接続では保存済みトークンを再利用し、自動で更新します。
|
||||
|
||||
### オプション B — API キー
|
||||
|
||||
MCP クライアントが OAuth をサポートしていない場合、または固定認証情報を好む場合は、`Authorization` ヘッダーに API キーを渡してください:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"twenty": {
|
||||
"type": "streamable-http",
|
||||
"url": "https://{your-workspace-url}/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer YOUR_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
API キーはワークスペース データへのアクセスを許可します。 バージョン管理や共有のドットファイルには含めないでください。
|
||||
</Warning>
|
||||
|
||||
API キーを作成するには、**Settings > APIs & Webhooks > + Create key** に移動します。 詳細は[API](/l/ja/developers/extend/api#create-an-api-key)を参照してください。
|
||||
|
||||
## クイックスタート
|
||||
|
||||
### 1. 構成をコピー
|
||||
|
||||
Twenty で **Settings > AI > More > MCP Server** に移動します。 認証方法(OAuth または API Key)を選択し、JSON スニペットをコピーして(ワークスペース URL は既に反映されています)、MCP クライアントの設定ファイルに貼り付けます。
|
||||
|
||||
| クライアント | 設定ファイルの場所 |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Claude Desktop** | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) |
|
||||
| **Claude Code** | `~/.claude.json` (ユーザー) または `.mcp.json` (プロジェクト) |
|
||||
| **Cursor** | プロジェクト内では `.cursor/mcp.json`、グローバルでは `~/.cursor/mcp.json` |
|
||||
| **ChatGPT** | **Settings > Apps & Connectors > Advanced settings** で Developer Mode を有効にし、その後 **Settings > Apps & Connectors** の **Create** を使用して MCP サーバーを追加します。 |
|
||||
|
||||
### 2. 接続
|
||||
|
||||
MCP クライアントを再起動します(または設定を再読み込みします)。 OAuth を使用している場合、アクセス許可のために Twenty にリダイレクトされます。 API キーを使用する場合は、すぐに接続されます。
|
||||
|
||||
### 3. 使い始める
|
||||
|
||||
AI アシスタントに CRM とやり取りするよう依頼してください:
|
||||
|
||||
* *"最近作成された企業を5件表示して"*
|
||||
* *"Acme Corp に Jane Doe という名前の新しい連絡先を作成して"*
|
||||
* *"$10k を超える金額のオープンな商談をすべて見つけて"*
|
||||
|
||||
## 利用可能なツール
|
||||
|
||||
接続されると、MCP サーバーは Twenty API を反映したツールを公開します。 推奨されるワークフローは次のとおりです:
|
||||
|
||||
1. **`learn_tools`** — 特定のツールの入力スキーマを取得する
|
||||
2. **`execute_tool`** — ツールを実行する
|
||||
|
||||
ツール名を覚えておく必要はありません。 AI アシスタントにできることを尋ねると、自動的に `learn_tools` を呼び出します。
|
||||
|
||||
## 権限
|
||||
|
||||
MCP 接続は、認証済みユーザー(OAuth)の権限、または API キーに割り当てられたロールを継承します。 MCP サーバーが実行できる操作を制限するには:
|
||||
|
||||
* **OAuth**: ユーザーのワークスペース ロールが適用されます。
|
||||
* **API Key**: **Settings > Roles** で API キーにロールを割り当てます。 詳しくは[権限](/l/ja/user-guide/permissions-access/capabilities/permissions)を参照してください。
|
||||
|
||||
## セルフホスト構成
|
||||
|
||||
セルフホストのインスタンスでは、`{your-workspace-url}` をサーバーの URL に置き換えてください。 環境の `SERVER_URL` が Twenty インスタンスの公開 URL と一致していることを確認してください — これは OAuth のディスカバリ メタデータの生成に使用されます。
|
||||
|
||||
```bash
|
||||
SERVER_URL=https://twenty.yourcompany.com
|
||||
```
|
||||
|
||||
MCP エンドポイント、OAuth エンドポイント、およびディスカバリ メタデータはすべてこの値から導出されます。
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
**"Unauthorized" または 401 エラー**
|
||||
|
||||
* OAuth: MCP クライアントで保存済みトークンをクリアして再接続し、再認可します。
|
||||
* API Key: キーが有効で、有効期限切れでないことを確認します。 必要に応じて再生成してください。
|
||||
|
||||
**OAuth フローでブラウザーが開かない**
|
||||
|
||||
* MCP クライアントが MCP Authorization をサポートしていることを確認してください。 サポートしていない場合は API Key の方法に切り替えてください。
|
||||
|
||||
**接続タイムアウト**
|
||||
|
||||
* MCP エンドポイントの URL にあなたのマシンから到達できることを確認してください。 セルフホストのインスタンスでは、サーバーが稼働していることと `SERVER_URL` が正しく設定されていることを確認してください。
|
||||
@@ -9,7 +9,7 @@ AI エージェントは既存の権限構造に従います。 これは、ワ
|
||||
|
||||
## AI エージェントに役割を割り当てる
|
||||
|
||||
1. **設定 → 役割** に移動
|
||||
1. **設定 → メンバー → 役割** に移動します
|
||||
2. 割り当てたい役割をクリック
|
||||
3. **割り当て** タブを開く
|
||||
4. 「**AI Agents**」で、**+ Assign to AI agent** をクリックします
|
||||
@@ -26,7 +26,7 @@ AI エージェントは既存の権限構造に従います。 これは、ワ
|
||||
| **監査可能性** | どのエージェントがどの操作を実行したかを追跡する |
|
||||
|
||||
<Note>
|
||||
ワークフロー内で実行される AI エージェントの場合、役割の割り当てにより、ワークフローにより広い権限がある場合でも、エージェントが意図された範囲外のデータにアクセスしたり変更したりできないようにします。
|
||||
ワークフロー内で実行される AI エージェントの場合、役割の割り当てにより、ワークフローにより広い権限がある場合でも、エージェントが意図された範囲外のデータにアクセスしたり変更したりできないようにします。
|
||||
</Note>
|
||||
|
||||
## 関連
|
||||
|
||||
@@ -4,6 +4,7 @@ description: Frequently asked questions about AI features in Twenty.
|
||||
---
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="When will AI features be available?">
|
||||
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
|
||||
</Accordion>
|
||||
@@ -16,7 +17,7 @@ description: Frequently asked questions about AI features in Twenty.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Will AI agents have access to all my data?">
|
||||
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
|
||||
AI agents will operate under the permission system. **設定 → メンバー → ロール** で AIエージェントに特定のロールを割り当て、アクセスできるデータと実行できるアクションを完全に制御できます。
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How will AI credits work?">
|
||||
@@ -26,4 +27,5 @@ description: Frequently asked questions about AI features in Twenty.
|
||||
<Accordion title="Can I use my own AI models?">
|
||||
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -48,7 +48,7 @@ AI アクションと自律型エージェントでワークフローを拡張
|
||||
|
||||
AI エージェントは既存の権限システムを通じて管理されます:
|
||||
|
||||
1. **設定 → 役割** に移動
|
||||
1. **設定 → メンバー → 役割** に移動します
|
||||
2. 各 AI エージェントがアクセスできるデータを設定する
|
||||
3. オブジェクトごとに読み取り/書き込み権限を設定する
|
||||
|
||||
@@ -59,4 +59,4 @@ AI エージェントは既存の権限システムを通じて管理されま
|
||||
AI 機能が利用可能になり次第、このセクションを更新します。 それまでの間:
|
||||
|
||||
* 開発の最新情報は私たちの[GitHub](https://github.com/twentyhq/twenty)をフォローしてください
|
||||
* フィードバックや機能リクエストを共有するには、私たちの[Discord](https://discord.gg/twenty)に参加してください
|
||||
* フィードバックや機能リクエストを共有するには、私たちの[Discord](https://discord.gg/UfGNZJfAG6)に参加してください
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user