diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/building.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/building.mdx deleted file mode 100644 index a348388328..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: الهيكلية -description: كيف تعمل تطبيقات Twenty — العزل، دورة الحياة، واللبنات الأساسية. -icon: sitemap ---- - -تطبيقات Twenty هي حزم TypeScript توسّع مساحة عملك بكائنات مخصّصة، ومنطق، ومكوّنات واجهة مستخدم (UI)، وقدرات ذكاء اصطناعي. تعمل على منصة Twenty مع عزل كامل وضوابط الأذونات. - -## كيف تعمل التطبيقات - -التطبيق عبارة عن مجموعة من **الكيانات** يتم إعلانها باستخدام دوال `defineEntity()` من حزمة `twenty-sdk`. يكتشف SDK هذه التصريحات عبر تحليل AST وقت البناء وينتج **ملف بيان** — وصفًا كاملًا لما يضيفه تطبيقك إلى مساحة العمل. - -``` -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 -``` - - - **تنظيم الملفات متروك لك.** يعتمد اكتشاف الكيانات على AST — يعثر SDK على استدعاءات `export default defineEntity(...)` بغض النظر عن مكان وجود الملف. بنية المجلدات أعلاه هي اصطلاح وليست متطلبًا. - - -## أنواع الكيانات - -| كيان | الغرض | وثائق | -| ---------------------- | ------------------------------------------------------ | -------------------------------------------------------------- | -| **تطبيق** | هوية التطبيق، الأذونات، المتغيرات | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) | -| **دور** | مجموعات الأذونات للكائنات والحقول | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) | -| **الكائن** | جداول بيانات مخصّصة مع حقول | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) | -| **الحقل** | توسيع الكائنات الموجودة، تعريف العلاقات | [نموذج البيانات](/l/ar/developers/extend/apps/data-model) | -| **دالة منطقية** | TypeScript على جانب الخادم مع مشغّلات | [الوظائف المنطقية](/l/ar/developers/extend/apps/logic-functions) | -| **مكوّن أمامي** | واجهة مستخدم React معزولة داخل صفحة Twenty. | [المكوّنات الأمامية](/l/ar/developers/extend/apps/front-components) | -| **مهارة** | تعليمات قابلة لإعادة الاستخدام لوكلاء الذكاء الاصطناعي | [المهارات والوكلاء](/l/ar/developers/extend/apps/skills-and-agents) | -| **وكيل** | مساعدو الذكاء الاصطناعي بموجهات مخصّصة | [المهارات والوكلاء](/l/ar/developers/extend/apps/skills-and-agents) | -| **عرض** | عروض قوائم السجلات المكوّنة مسبقًا | [التخطيط](/l/ar/developers/extend/apps/layout) | -| **عنصر قائمة التنقّل** | عناصر الشريط الجانبي المخصّصة | [التخطيط](/l/ar/developers/extend/apps/layout) | -| **تخطيط الصفحة** | علامات تبويب وعناصر واجهة مخصّصة لصفحة السجل | [التخطيط](/l/ar/developers/extend/apps/layout) | - -## العزل - -* **الدوال المنطقية** تعمل في عمليات Node.js معزولة على الخادم. لا تصل إلى البيانات إلا عبر عميل API مضبوط الأنواع، ومقيَّد بأذونات دور التطبيق. -* **المكوّنات الأمامية** تعمل ضمن Web Workers باستخدام Remote DOM — معزولة عن الصفحة الرئيسية لكنها تعرض عناصر DOM الأصلية (وليس iframes). تتواصل مع Twenty عبر واجهة API للمضيف تعتمد تمرير الرسائل. -* **الأذونات** تُطبَّق على مستوى واجهة API. يُشتق رمز وقت التشغيل (`TWENTY_APP_ACCESS_TOKEN`) من الدور المعرَّف في `defineApplication()`. - -## دورة حياة التطبيق - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ 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 build`** — يجمّع TypeScript، ويضمّن الدوال المنطقية والمكوّنات الأمامية باستخدام esbuild، وينتج ملف بيان. -* **خطّافات ما قبل/ما بعد التثبيت** — دوال منطقية اختيارية تعمل أثناء التثبيت. راجع [الدوال المنطقية](/l/ar/developers/extend/apps/logic-functions) للحصول على التفاصيل. - -## الخطوات التالية - - - - عرّف الكائنات والحقول والأدوار والعلاقات. - - - دوال على جانب الخادم مع HTTP وcron ومشغّلات الأحداث. - - - مكوّنات React معزولة داخل واجهة مستخدم Twenty. - - - العروض، وعناصر التنقّل، وتخطيطات صفحات السجل. - - - مهارات ووكلاء ذكاء اصطناعي بموجهات مخصّصة. - - - أوامر CLI، والاختبار، والأصول، والوحدات البعيدة، وCI. - - - انشر إلى خادم أو انشر في السوق. - - diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index 53b4726eac..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI والاختبار -description: أوامر CLI، إعداد الاختبار، الأصول العامة، حزم npm، المستودعات البعيدة، وتهيئة CI. -icon: terminal ---- - -## الأصول العامة (مجلد `public/`) - -يحتوي مجلد `public/` في جذر تطبيقك على ملفات ثابتة — صور وأيقونات وخطوط وأي أصول أخرى يحتاجها تطبيقك وقت التشغيل. تُدرج هذه الملفات تلقائيًا في عمليات البناء، وتُزامَن أثناء وضع التطوير، وتُرفَع إلى الخادم. - -الملفات الموضوعة في `public/` هي: - -* **متاحة للعامة** — بمجرد مزامنتها إلى الخادم، تُقدَّم الأصول عبر عنوان URL عام. لا حاجة إلى مصادقة للوصول إليها. -* **متاحة في المكوّنات الأمامية** — استخدم عناوين الأصول لعرض الصور أو الأيقونات أو أي وسائط داخل مكوّنات React لديك. -* **متاحة في الدوال المنطقية** — أشِر إلى عناوين الأصول في رسائل البريد الإلكتروني أو استجابات واجهات البرمجة أو أي منطق على جهة الخادم. -* **مستخدمة لبيانات تعريف السوق** — يشير حقلا `logoUrl` و`screenshots` في `defineApplication()` إلى ملفات من هذا المجلد (مثل `public/logo.png`). تُعرَض هذه عند نشر تطبيقك في السوق. -* **تُزامَن تلقائيًا في وضع التطوير** — عند إضافة ملف في `public/` أو تحديثه أو حذفه، تتم مزامنته إلى الخادم تلقائيًا. لا حاجة لإعادة التشغيل. -* **مضمَّنة في عمليات البناء** — يقوم `yarn twenty build` بتجميع جميع الأصول العامة ضمن مخرجات التوزيع. - -### الوصول إلى الأصول العامة باستخدام `getPublicAssetUrl` - -استخدم المساعد `getPublicAssetUrl` من `twenty-sdk` للحصول على العنوان الكامل لملف في دليل `public/` لديك. يعمل ذلك في كلٍ من الدوال المنطقية والمكوّنات الأمامية. - -**في دالة منطقية:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -وسيطة `path` نسبية إلى مجلد `public/` الخاص بتطبيقك. كلٌّ من `getPublicAssetUrl('logo.png')` و`getPublicAssetUrl('public/logo.png')` يُحلاّن إلى العنوان نفسه — تتم إزالة بادئة `public/` تلقائيًا إن وُجدت. - -## استخدام حِزَم 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 => { - 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

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### كيف يعمل التجميع - -تستخدم خطوة البناء أداة esbuild لإنتاج ملف واحد مستقل لكل دالة منطقية ولكل مكوّن أمامي. تُضمَّن جميع الحزم المستوردة داخل الحزمة. - -**الدوال المنطقية** تعمل في بيئة Node.js. الوحدات المدمجة في Node (`fs` و`path` و`crypto` و`http` وغيرها) متاحة ولا تحتاج إلى تثبيت. - -**المكوّنات الأمامية** تعمل ضمن Web Worker. وحدات Node المدمجة غير متاحة — المتاح فقط واجهات برمجة المتصفّح وحِزَم npm التي تعمل في بيئة المتصفّح. - -كلتا البيئتين تحتويان على `twenty-client-sdk/core` و`twenty-client-sdk/metadata` كوحدات متاحة مُسبقًا — لا تُضمَّن هذه ضمن الحزم بل تُحلّ وقت التشغيل بواسطة الخادم. - -## اختبار تطبيقك - -يوفّر SDK واجهات برمجة قابلة للتنفيذ برمجيًا تمكّنك من بناء تطبيقك ونشره وتثبيته وإلغاء تثبيته من شيفرة الاختبار. بالاقتران مع [Vitest](https://vitest.dev/) وعملاء واجهة البرمجة مضبوطي الأنواع، يمكنك كتابة اختبارات تكامل تتحقّق من أن تطبيقك يعمل من البداية إلى النهاية مقابل خادم Twenty حقيقي. - -### إعداد - -يتضمّن التطبيق المُولَّد بالقالب بالفعل 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 البرمجية - -يُصدِّر المسار الفرعي `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 typecheck -``` - -يشغِّل هذا الأمر `tsc --noEmit` ويبلغ عن أي أخطاء في الأنواع. - -## مرجع CLI - -بالإضافة إلى `dev` و`build` و`add` و`typecheck`، يوفّر CLI أوامر لتنفيذ الدوال وعرض السجلات وإدارة تثبيتات التطبيقات. - -### تنفيذ الدوال (`yarn twenty exec`) - -تشغيل دالة منطقية يدويًا دون تشغيلها عبر HTTP أو cron أو حدث قاعدة بيانات: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### عرض سجلات الدوال (`yarn twenty logs`) - -بثّ سجلات التنفيذ لدوال تطبيقك المنطقية: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -يختلف هذا عن `yarn twenty server logs`، الذي يعرض سجلات حاوية Docker. يعرض `yarn twenty logs` سجلات تنفيذ دوال تطبيقك من خادم Twenty. - - -### إلغاء تثبيت تطبيق (`yarn twenty uninstall`) - -أزل تطبيقك من مساحة العمل النشطة: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## إدارة الريموتات - -**الريموت** هو خادم 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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -تُخزَّن بيانات اعتمادك في `~/.twenty/config.json`. - -## التكامل المستمر (CI) باستخدام GitHub Actions - -تولّد أداة إنشاء الهيكل سير عمل GitHub Actions جاهزًا للاستخدام في `.github/workflows/ci.yml`. يشغّل اختبارات التكامل لديك تلقائيًا عند كل دفع إلى `main` وعلى طلبات السحب. - -سير العمل: - -1. يجلب الشيفرة الخاصة بك -2. يشغّل خادم Twenty مؤقتًا باستخدام الإجراء `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. يثبّت التبعيات باستخدام `yarn install --immutable` -4. يشغّل `yarn test` مع حقن `TWENTY_API_URL` و`TWENTY_API_KEY` من مخرجات الإجراء - -```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. - -لتثبيت إصدار محدّد من Twenty بدلًا من `latest`، غيّر متغير البيئة `TWENTY_VERSION` في أعلى سير العمل. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/data-model.mdx deleted file mode 100644 index 94d7fce6c7..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,494 +0,0 @@ ---- -title: نموذج البيانات -description: عرّف الكائنات والحقول والأدوار وبيانات تعريف التطبيق باستخدام SDK الخاصة بـ Twenty. -icon: database ---- - -توفر حزمة `twenty-sdk` دوالّ `defineEntity` لتعريف نموذج بيانات تطبيقك. يجب عليك استخدام `export default defineEntity({...})` لكي يكتشف SDK الكيانات الخاصة بك. تتحقق هذه الدوال من تكوينك وقت البناء وتوفّر إكمالًا تلقائيًا في بيئة التطوير وأمان الأنواع. - - - **تنظيم الملفات يعود إليك.** - يعتمد اكتشاف الكيانات على AST — حيث يعثر SDK على استدعاءات `export default defineEntity(...)` بغض النظر عن مكان وجود الملف. تجميع الملفات حسب النوع (مثلًا، `logic-functions/` و`roles/`) هو مجرّد عرف، وليس متطلبًا. - - - - - -تُغلّف الأدوار الصلاحيات على كائنات وإجراءات مساحة العمل لديك. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -يجب أن يحتوي كل تطبيق على استدعاء واحد فقط لـ `defineApplication` يصف: - -* **الهوية**: المعرّفات، اسم العرض، والوصف. -* **الأذونات**: أيُّ دورٍ تستخدمه وظائفه ومكوّناته الأمامية. -* **(اختياري) المتغيرات**: أزواج مفتاح-قيمة تُعرض لوظائفك كمتغيرات بيئة. -* **(اختياري) دوال ما قبل التثبيت/ما بعد التثبيت**: دوال منطقية تعمل قبل التثبيت أو بعده. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -export default defineApplication({ - universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -الملاحظات: -* حقول `universalIdentifier` هي معرّفات حتمية تملكها أنت. أنشِئها مرة واحدة واحتفظ بها ثابتة عبر عمليات المزامنة. -* `applicationVariables` تصبح متغيرات بيئة لوظائفك ومكوّناتك الأمامية (على سبيل المثال، `DEFAULT_RECIPIENT_NAME` متاح كـ `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` يجب أن يُشير إلى دور مُعرَّف باستخدام `defineRole()` (انظر أعلاه). -* يتم اكتشاف دوال ما قبل التثبيت وما بعده تلقائيًا أثناء بناء البيان — لا حاجة للإشارة إليها في `defineApplication()`. - -#### بيانات التعريف لسوق التطبيقات - -إذا كنت تخطط لـ [نشر تطبيقك](/l/ar/developers/extend/apps/publishing)، فإن هذه الحقول الاختيارية تتحكّم في كيفية ظهوره في السوق: - -| الحقل | الوصف | -| ------------------ | ------------------------------------------------------------------------------------------------------------ | -| `author` | اسم المؤلف أو الشركة | -| `category` | فئة التطبيق لتصفية سوق التطبيقات | -| `logoUrl` | مسار شعار تطبيقك (مثلًا، `public/logo.png`) | -| `screenshots` | مصفوفة لمسارات لقطات الشاشة (مثلًا، `public/screenshot-1.png`) | -| `aboutDescription` | وصف ماركداون أطول لعلامة التبويب "حول". إذا لم يتم تضمينه، يستخدم السوق ملف `README.md` الخاص بالحزمة من npm | -| `websiteUrl` | رابط إلى موقعك الإلكتروني | -| `termsUrl` | رابط إلى شروط الخدمة | -| `emailSupport` | عنوان البريد الإلكتروني للدعم | -| `issueReportUrl` | رابط إلى متتبّع المشاكل | - -#### الأدوار والصلاحيات - -يُحدّد الحقل `defaultRoleUniversalIdentifier` في `application-config.ts` الدور الافتراضي الذي تستخدمه وظائف المنطق والمكوّنات الأمامية في تطبيقك. راجع `defineRole` أعلاه للحصول على التفاصيل. - -* رمز وقت التشغيل المحقون باسم `TWENTY_APP_ACCESS_TOKEN` مستمد من هذا الدور. -* العميل مضبوط الأنواع مقيَّد بالأذونات الممنوحة لذلك الدور. -* اتبع مبدأ أقل الامتياز: أنشئ دورًا مخصصًا يضم فقط الأذونات التي تحتاجها وظائفك. - -##### الدور الافتراضي للوظيفة - -عند توليد تطبيق جديد بالقالب، ينشئ CLI ملفّ دور افتراضي: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -يُشار إلى `universalIdentifier` لهذا الدور في `application-config.ts` باسم `defaultRoleUniversalIdentifier`: - -* **\*.role.ts** يحدد ما يمكن أن يفعله الدور. -* **application-config.ts** يشير إلى ذلك الدور بحيث ترث وظائفك أذوناته. - -الملاحظات: -* ابدأ من الدور المُنشأ بالقالب، ثم قيّده تدريجيًا باتباع مبدأ أقل الامتياز. -* استبدل `objectPermissions` و`fieldPermissions` بالكائنات والحقول التي تحتاجها وظائفك فعليًا. -* `permissionFlags` تتحكم في الوصول إلى القدرات على مستوى المنصة. اجعلها في حدّها الأدنى. -* اطّلع على مثال عملي: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -تصف الكائنات المخصصة كلًا من المخطط والسلوك للسجلات في مساحة عملك. استخدم `defineObject()` لتعريف كائنات مع تحقق مدمج: - -```ts postCard.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, - }, - ], -}); -``` - -النقاط الرئيسية: - -* استخدم `defineObject()` للحصول على تحقق مدمج ودعم أفضل من IDE. -* `universalIdentifier` يجب أن يكون فريدًا وثابتًا عبر عمليات النشر. -* يتطلب كل حقل `name` و`type` و`label` ومعرّف `universalIdentifier` ثابتًا خاصًا به. -* المصفوفة `fields` اختيارية — يمكنك تعريف كائنات بدون حقول مخصصة. -* يمكنك إنشاء كائنات جديدة باستخدام `yarn twenty add`، والذي يرشدك خلال التسمية والحقول والعلاقات. - - -**يتم إنشاء الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، يضيف Twenty تلقائيًا حقولًا قياسية -مثل `id` و`name` و`createdAt` و`updatedAt` و`createdBy` و`updatedBy` و`deletedAt`. -لا تحتاج إلى تعريف هذه في مصفوفة `fields` — أضف فقط حقولك المخصصة. -يمكنك تجاوز الحقول الافتراضية من خلال تعريف حقل بالاسم نفسه في مصفوفة `fields` الخاصة بك، -لكن هذا غير مستحسن. - - - - - -استخدم `defineField()` لإضافة حقول إلى كائنات لا تملكها — مثل كائنات Twenty القياسية (Person, Company, etc.) أو كائنات من تطبيقات أخرى. على خلاف الحقول المضمّنة في `defineObject()`، تتطلّب الحقول المستقلة `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` يحدّد الكائن الهدف. بالنسبة للكائنات القياسية، استخدم `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` المُصدَّر من `twenty-sdk`. -* عند تعريف الحقول بشكل مضمّن في `defineObject()`، **لا** تحتاج إلى `objectUniversalIdentifier` — إذ يُورَّث من الكائن الأب. -* `defineField()` هي الطريقة الوحيدة لإضافة حقول إلى كائنات لم تُنشئها باستخدام `defineObject()`. - - - - -تربط العلاقات الكائنات معًا. في Twenty، تكون العلاقات دائمًا **ثنائية الاتجاه** — حيث تعرّف الجانبين، ويشير كل جانب إلى الآخر. - -هناك نوعان من العلاقات: - -| نوع العلاقة | الوصف | هل لديه مفتاح خارجي؟ | -| ------------- | ------------------------------------------------------ | ---------------------- | -| `MANY_TO_ONE` | تشير العديد من سجلات هذا الكائن إلى سجل واحد من الهدف | نعم (`joinColumnName`) | -| `ONE_TO_MANY` | يحتوي سجل واحد من هذا الكائن على العديد من سجلات الهدف | لا (الجانب العكسي) | - -#### كيف تعمل العلاقات - -تتطلّب كل علاقة **حقلين** يشيران إلى بعضهما البعض: - -1. جانب **MANY_TO_ONE** — يوجد على الكائن الذي يحمل المفتاح الخارجي -2. جانب **ONE_TO_MANY** — يوجد على الكائن الذي يملك المجموعة - -يستخدم كلا الحقلين `FieldType.RELATION` ويُحيل كلٌ منهما إلى الآخر عبر `relationTargetFieldMetadataUniversalIdentifier`. - -#### مثال: البطاقة البريدية لديها العديد من المستلمين - -افترض أن `PostCard` يمكن إرسالها إلى العديد من سجلات `PostCardRecipient`. ينتمي كل مستلم إلى بطاقة بريدية واحدة بالضبط. - -**الخطوة 1: عرّف جانب ONE_TO_MANY على PostCard** (جانب "الواحد"): - -```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: عرّف جانب MANY_TO_ONE على PostCardRecipient** (جانب "العديد" — يحمل المفتاح الخارجي): - -```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', - }, -}); -``` - - -**الاستيرادات الدائرية:** كلا حقلي العلاقة يُحيل كلٌ منهما إلى `universalIdentifier` الخاص بالآخر. لتجنّب مشكلات الاستيراد الدائري، صدّر معرّفات الحقول كثوابت مسمّاة من كل ملف، واستوردها في الملف الآخر. يقوم نظام البناء بحلّها في وقت التجميع. - - -#### الربط مع الكائنات القياسية - -لإنشاء علاقة مع كائن Twenty مضمّن (Person, Company, etc.)، استخدم `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 - -يمكنك أيضًا تعريف حقول العلاقات مباشرةً داخل `defineObject()`. في هذه الحالة، احذف `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 - ], -}); -``` - - - -## توليد قوالب الكيانات باستخدام `yarn twenty add` - -بدلًا من إنشاء ملفات الكيانات يدويًا، يمكنك استخدام أداة القوالب التفاعلية: - -```bash filename="Terminal" -yarn twenty add -``` - -ستطالبك باختيار نوع الكيان وتُرشدك خلال الحقول المطلوبة. تُولّد ملفًا جاهزًا للاستخدام مع `universalIdentifier` ثابت واستدعاء `defineEntity()` الصحيح. - -يمكنك أيضًا تمرير نوع الكيان مباشرة لتخطي المطالبة الأولى: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### أنواع الكيانات المتاحة - -| نوع الكيان | أمر | الملف المُولَّد | -| ------------------ | ------------------------------------ | ------------------------------------------------------- | -| كائن | `yarn twenty add object` | `src/objects/\.ts` | -| الحقل | `yarn twenty add field` | `src/fields/\.ts` | -| دالة منطقية | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| مكوّن أمامي | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| دور | `yarn twenty add role` | `src/roles/\.ts` | -| مهارة | `yarn twenty add skill` | `src/skills/\.ts` | -| وكيل | `yarn twenty add agent` | `src/agents/\.ts` | -| عرض | `yarn twenty add view` | `src/views/\.ts` | -| عنصر قائمة التنقّل | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| تخطيط الصفحة | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### ما الذي تُنشئه أداة القوالب - -لكل نوع كيان قالب خاص به. على سبيل المثال، يسأل `yarn twenty add object` عن: - -1. **الاسم (مفرد)** — مثل `invoice` -2. **الاسم (جمع)** — مثل `invoices` -3. **التسمية (مفرد)** — تُستمد تلقائيًا من الاسم (مثل `Invoice`) -4. **التسمية (جمع)** — تُملأ تلقائيًا (مثل `Invoices`) -5. **إنشاء عرض وعنصر تنقّل؟** — إذا أجبت بنعم، فستُنشئ أداة القوالب أيضًا عرضًا مطابقًا ورابط شريط جانبي للكائن الجديد. - -أنواع الكيانات الأخرى لها مطالبات أبسط — فمعظمها يطلب اسمًا فقط. - -نوع الكيان `field` أكثر تفصيلاً: يطلب اسم الحقل وتسمية الحقل ونوعه (من قائمة بكل أنواع الحقول المتاحة مثل `TEXT` و`NUMBER` و`SELECT` و`RELATION` وغيرها)، ومعرّف `universalIdentifier` للكائن الهدف. - -### مسار خرج مخصّص - -استخدم العلم `--path` لوضع الملف المُولَّد في موقع مخصّص: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/front-components.mdx deleted file mode 100644 index e9fdb645db..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: المكوّنات الأمامية -description: أنشئ مكونات React تُعرَض داخل واجهة مستخدم Twenty ضمن بيئة معزولة (sandbox). -icon: window-maximize ---- - -المكوّنات الأمامية هي مكوّنات React تُعرَض مباشرة داخل واجهة مستخدم Twenty. تعمل ضمن **Web Worker** معزول باستخدام Remote DOM — تكون شيفرتك في صندوق عزل لكنها تُعرَض أصيلًا داخل الصفحة، وليس ضمن iframe. - -## أين يمكن استخدام مكوّنات الواجهة الأمامية - -يمكن عرض مكوّنات الواجهة الأمامية في موقعين داخل Twenty: - -* **اللوحة الجانبية** — المكوّنات غير عديمة الرأس تفتح في اللوحة الجانبية اليمنى. هذا هو السلوك الافتراضي عندما يتم تشغيل مكوّن واجهة أمامية من قائمة الأوامر. -* **الويدجت (لوحات المعلومات وصفحات السجلات)** — يمكن تضمين مكوّنات الواجهة الأمامية كويدجت داخل تخطيطات الصفحات. عند تكوين لوحة معلومات أو تخطيط صفحة سجل، يمكن للمستخدمين إضافة ويدجت لمكوّن واجهة أمامية. - -## مثال أساسي - -أسرع طريقة لرؤية مكوّن أمامي قيد العمل هي تسجيله كأمر. إضافة حقل `command` مع `isPinned: true` يجعلُه يظهر كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة — دون الحاجة إلى تخطيط صفحة: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', - name: 'hello-world', - description: 'A simple front component', - component: HelloWorld, - command: { - universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345', - shortLabel: 'Hello', - label: 'Hello World', - icon: 'IconBolt', - isPinned: true, - availabilityType: 'GLOBAL', - }, -}); -``` - -بعد المزامنة باستخدام `yarn twenty dev` (أو تشغيل الأمر لمرة واحدة `yarn twenty dev --once`)، يظهر الإجراء السريع في الزاوية العلوية اليمنى من الصفحة: - -
- زر إجراء سريع في الزاوية العلوية اليمنى -
- -انقره لعرض المكوّن مضمنًا داخل الصفحة. - -## حقول التكوين - -| الحقل | مطلوب | الوصف | -| --------------------- | ----- | ----------------------------------------------------------------- | -| `universalIdentifier` | نعم | معرّف فريد ثابت لهذا المكوّن | -| `component` | نعم | دالة مكوّن React | -| `name` | لا | اسم العرض | -| `description` | لا | وصف لما يفعله المكوّن | -| `isHeadless` | لا | عيِّنه إلى `true` إذا كان المكوّن بلا واجهة مرئية (انظر أدناه) | -| `command` | لا | سجّل المكوّن كأمر (انظر [خيارات الأوامر](#command-options) أدناه) | - -## وضع مكوّن أمامي على صفحة - -إضافةً إلى الأوامر، يمكنك تضمين مكوّن أمامي مباشرةً في صفحة سجل عبر إضافته كودجت في **تخطيط صفحة**. راجع قسم [definePageLayout](/l/ar/developers/extend/apps/skills-and-agents#definepagelayout) للتفاصيل. - -## عديم الرأس مقابل غير عديم الرأس - -تأتي مكوّنات الواجهة الأمامية بوضعَي عرض يتحكّم بهما الخيار `isHeadless`: - -**غير عديم الرأس (افتراضي)** — يعرض المكوّن واجهة مستخدم مرئية. عند تشغيله من قائمة الأوامر يفتح في اللوحة الجانبية. هذا هو السلوك الافتراضي عندما تكون `isHeadless` تساوي `false` أو يتم تجاهلها. - -**عديم الرأس (`isHeadless: true`)** — يتم تركيب المكوّن بشكل غير مرئي في الخلفية. لا يفتح اللوحة الجانبية. تم تصميم المكوّنات عديمة الرأس لإجراءات تنفّذ منطقًا ثم تُزيل تركيبها ذاتيًا — على سبيل المثال، تشغيل مهمة غير متزامنة، أو الانتقال إلى صفحة، أو إظهار نافذة تأكيد منبثقة. تتوافق بشكل طبيعي مع مكوّنات Command في SDK الموصوفة أدناه. - -```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 يتخطّى عرض حاوية له — ولن تظهر مساحة فارغة في التخطيط. لا يزال لدى المكوّن إمكانية الوصول إلى جميع الخطافات وواجهة برمجة الاتصال مع المضيف. - -## مكوّنات Command في SDK - -توفر حزمة `twenty-sdk` أربعة مكوّنات مساعدة من نوع Command مصممة للمكوّنات عديمة الرأس في الواجهة الأمامية. كل مكوّن ينفّذ إجراءً عند التركيب، ويتعامل مع الأخطاء بعرض إشعار Snackbar، ويزيل تركيب مكوّن الواجهة الأمامية تلقائيًا عند الانتهاء. - -استوردها من `twenty-sdk/command`: - -* **`Command`** — يشغّل رد نداء غير متزامن عبر الخاصية `execute`. -* **`CommandLink`** — ينتقل إلى مسار في التطبيق. الخصائص: `to`، `params`، `queryParams`، `options`. -* **`CommandModal`** — يفتح نافذة تأكيد منبثقة. إذا أكّد المستخدم، ينفّذ رد النداء `execute`. الخصائص: `title`، `subtitle`، `execute`، `confirmButtonText`، `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — يفتح صفحة محدّدة في اللوحة الجانبية. الخصائص: `page`، `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 ; -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', - name: 'run-action', - description: 'Creates a task from the command menu', - component: RunAction, - isHeadless: true, - command: { - universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', - label: 'Run my action', - icon: 'IconPlayerPlay', - }, -}); -``` - -ومثال يستخدم `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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, - command: { - universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567', - label: 'Delete draft', - icon: 'IconTrash', - }, -}); -``` - -## الوصول إلى سياق وقت التشغيل - -داخل مكوّنك، استخدم خطافات 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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -الخطافات المتاحة: - -| الخطّاف | القيم المعادة | الوصف | -| --------------------------------------------- | ------------------ | ---------------------------------------------- | -| `useUserId()` | `string` أو `null` | معرّف المستخدم الحالي | -| `useRecordId()` | `string` أو `null` | معرّف السجل الحالي (عند وضعه على صفحة سجل) | -| `useFrontComponentId()` | `string` | معرّف مثيل هذا المكوّن | -| `useFrontComponentExecutionContext(selector)` | يختلف | الوصول إلى سياق التنفيذ الكامل عبر دالة محدِّد | - -## واجهة الاتصال مع المضيف - -يمكن للمكوّنات الأمامية تشغيل التنقّل والنوافذ المنبثقة والإشعارات باستخدام دوال من `twenty-sdk`: - -| دالة | الوصف | -| ----------------------------------------------- | ------------------------------ | -| `navigate(to, params?, queryParams?, options?)` | الانتقال إلى صفحة داخل التطبيق | -| `openSidePanelPage(params)` | فتح لوحة جانبية | -| `closeSidePanel()` | إغلاق اللوحة الجانبية | -| `openCommandConfirmationModal(params)` | عرض مربع حوار تأكيد | -| `enqueueSnackbar(params)` | عرض إشعار توست | -| `unmountFrontComponent()` | إلغاء تركيب المكوّن | -| `updateProgress(progress)` | تحديث مؤشّر التقدّم | - -فيما يلي مثال يستخدم واجهة برمجة تطبيقات المضيف لعرض Snackbar وإغلاق اللوحة الجانبية بعد اكتمال الإجراء: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -## خيارات الأوامر - -إضافة حقل `command` إلى `defineFrontComponent` تُسجِّل المكوّن في قائمة الأوامر (Cmd+K). إذا كانت قيمة `isPinned` هي `true`، فسيظهر أيضًا كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة. - -| الحقل | مطلوب | الوصف | -| --------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `universalIdentifier` | نعم | معرّف فريد ثابت للأمر | -| `label` | نعم | التسمية الكاملة المعروضة في قائمة الأوامر (Cmd+K) | -| `shortLabel` | لا | تسمية أقصر تُعرَض على زر الإجراء السريع المثبّت | -| `icon` | لا | اسم الأيقونة المعروض بجانب التسمية (مثل `'IconBolt'` و`'IconSend'`) | -| `isPinned` | لا | عند كونها `true`، يعرض الأمر كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة | -| `availabilityType` | لا | تتحكّم في مكان ظهور الأمر: `'GLOBAL'` (متاح دائمًا)، و`'RECORD_SELECTION'` (فقط عند تحديد سجلات)، أو `'FALLBACK'` (يُعرَض عند عدم تطابق أي أوامر أخرى) | -| `availabilityObjectUniversalIdentifier` | لا | تقييد الأمر بصفحات نوع كائن معيّن (مثل سجلات Company فقط) | -| `conditionalAvailabilityExpression` | لا | تعبير منطقي للتحكم ديناميكيًا في ما إذا كان الأمر مرئيًا (انظر أدناه) | - -## تعابير الإتاحة الشرطية - -يتيح لك الحقل `conditionalAvailabilityExpression` التحكّم في وقت ظهور الأمر بناءً على سياق الصفحة الحالي. استورد متغيّرات ومشغّلات مضبوطة الأنواع من `twenty-sdk` لبناء التعابير: - -```tsx -import { - defineFrontComponent, - pageType, - numberOfSelectedRecords, - objectPermissions, - everyEquals, - isDefined, -} from 'twenty-sdk/define'; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'bulk-action', - component: BulkAction, - command: { - universalIdentifier: '...', - label: 'Bulk Update', - availabilityType: 'RECORD_SELECTION', - conditionalAvailabilityExpression: everyEquals( - objectPermissions, - 'canUpdateObjectRecords', - true, - ), - }, -}); -``` - -**متغيّرات السياق** — تُمثّل الحالة الحالية للصفحة: - -| المتغيّر | النوع | الوصف | -| ------------------------------ | ------------- | --------------------------------------------------------------- | -| `pageType` | `string` | نوع الصفحة الحالي (مثل `'RecordIndexPage'` و`'RecordShowPage'`) | -| `isInSidePanel` | `boolean` | ما إذا كان المكوّن معروضًا في لوحة جانبية | -| `numberOfSelectedRecords` | `number` | عدد السجلات المحدّدة حاليًا | -| `isSelectAll` | `boolean` | ما إذا كان "تحديد الكل" مفعّلًا | -| `selectedRecords` | `array` | كائنات السجلات المحدّدة | -| `favoriteRecordIds` | `array` | معرّفات السجلات المفضّلة | -| `objectPermissions` | `object` | الأذونات الخاصة بنوع الكائن الحالي | -| `targetObjectReadPermissions` | `object` | أذونات القراءة للكائن الهدف | -| `targetObjectWritePermissions` | `object` | أذونات الكتابة للكائن الهدف | -| `featureFlags` | `object` | أعلام الميزات المفعَّلة | -| `objectMetadataItem` | `object` | بيانات التعريف لنوع الكائن الحالي | -| `hasAnySoftDeleteFilterOnView` | `قيمة منطقية` | ما إذا كان العرض الحالي يحتوي على مرشّح حذف منطقي | - -**المُشغِّلات** — جمّع المتغيّرات في تعابير منطقية: - -| المُشغِّل | الوصف | -| ----------------------------------- | -------------------------------------------------------------- | -| `isDefined(value)` | `true` إذا لم تكن القيمة null/undefined | -| `isNonEmptyString(value)` | `true` إذا كانت القيمة سلسلة غير فارغة | -| `includes(array, value)` | `true` إذا كانت المصفوفة تحتوي على القيمة | -| `includesEvery(array, prop, value)` | `true` إذا كانت خاصية كل عنصر تتضمن القيمة | -| `every(array, prop)` | `true` إذا كانت الخاصية تُقيَّم بصحّة في كل عنصر | -| `everyDefined(array, prop)` | `true` إذا كانت الخاصية معرّفة في كل عنصر | -| `everyEquals(array, prop, value)` | `true` إذا كانت الخاصية تساوي القيمة في كل عنصر | -| `some(array, prop)` | `true` إذا كانت الخاصية تُقيَّم بصحّة في عنصر واحد على الأقل | -| `someDefined(array, prop)` | `true` إذا كانت الخاصية معرّفة في عنصر واحد على الأقل | -| `someEquals(array, prop, value)` | `true` إذا كانت الخاصية تساوي القيمة في عنصر واحد على الأقل | -| `someNonEmptyString(array, prop)` | `true` إذا كانت الخاصية سلسلة غير فارغة في عنصر واحد على الأقل | -| `none(array, prop)` | `true` إذا كانت الخاصية تُقيَّم بخطأ في كل عنصر | -| `noneDefined(array, prop)` | `true` إذا كانت الخاصية غير معرّفة في كل عنصر | -| `noneEquals(array, prop, value)` | `true` إذا لم تكن الخاصية تساوي القيمة في أي عنصر | - -## الأصول العامة - -يمكن للمكوّنات الأمامية الوصول إلى ملفات من دليل `public/` للتطبيق باستخدام `getPublicAssetUrl`: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -راجع [قسم الأصول العامة](/l/ar/developers/extend/apps/cli-and-testing#public-assets-public-folder) للتفاصيل. - -## التنسيق - -تدعم المكوّنات الأمامية عدة أساليب للتنسيق. يمكنك استخدام: - -* **أنماط مضمنة** — `style={{ color: 'red' }}` -* **مكوّنات Twenty لواجهة المستخدم** — استورد من `twenty-sdk/ui` (Button وTag وStatus وChip وAvatar وغيرها) -* **Emotion** — CSS-in-JS مع `@emotion/react` -* **Styled-components** — أنماط `styled.div` -* **Tailwind CSS** — أصناف مساعدة -* **أي مكتبة CSS-in-JS** متوافقة مع React - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started.mdx deleted file mode 100644 index dbd5c15296..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,282 +0,0 @@ ---- -title: البدء -icon: rocket -description: أنشئ أول تطبيق Twenty خلال دقائق. ---- - -## ما هي التطبيقات؟ - -تتيح لك التطبيقات توسيع Twenty باستخدام كائنات وحقول مخصّصة ووظائف منطقية ومكوّنات الواجهة الأمامية ومهارات الذكاء الاصطناعي وغير ذلك — جميعها تُدار ككود. بدلًا من تكوين كل شيء عبر واجهة المستخدم، تعرّف نموذج بياناتك ومنطقك في TypeScript وتقوم بنشره إلى مساحة عمل واحدة أو أكثر. - -## المتطلبات الأساسية - -قبل أن تبدأ، تأكّد من تثبيت ما يلي على جهازك: - -* **Node.js 24+** — [نزّل من هنا](https://nodejs.org/) -* **Yarn 4** — يأتي مع Node.js عبر Corepack. قم بتمكينه عبر تشغيل `corepack enable` -* **Docker** — [نزّل من هنا](https://www.docker.com/products/docker-desktop/). مطلوب لتشغيل مثيل محلي من Twenty. غير مطلوب إذا كان لديك خادم Twenty قيد التشغيل بالفعل. - -## قم بإنشاء تطبيقك الأول - -### أنشئ هيكل تطبيقك - -افتح الطرفية وشغّل: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -سيُطلب منك إدخال اسم ووصف لتطبيقك. اضغط **Enter** لقبول الإعدادات الافتراضية. - -سيؤدي ذلك إلى إنشاء مجلد جديد باسم `my-twenty-app` يحتوي على كل ما تحتاجه. - -### إعداد مثيل محلي من Twenty - -ستسأل أداة إنشاء الهيكل: - -> **هل ترغب في إعداد مثيل محلي من Twenty؟** - -* **اكتب `yes`** (موصى به) — سيؤدي ذلك إلى سحب صورة Docker `twenty-app-dev` وبدء تشغيل خادم Twenty محلي على المنفذ `2020`. تأكّد من أن Docker قيد التشغيل قبل المتابعة. -* **اكتب `no`** — اختر هذا إذا كان لديك خادم Twenty يعمل محليًا بالفعل. - -
- هل يجب بدء المثيل المحلي؟ -
- -### سجّل الدخول إلى مساحة العمل الخاصة بك - -بعد ذلك، ستُفتح نافذة متصفح تعرض صفحة تسجيل الدخول الخاصة بـ Twenty. سجّل الدخول باستخدام حساب العرض التوضيحي المُجهَّز مسبقًا: - -* **البريد الإلكتروني:** `tim@apple.dev` -* **كلمة المرور:** `tim@apple.dev` - -
- شاشة تسجيل الدخول إلى Twenty -
- -### قم بتفويض التطبيق - -بعد تسجيل الدخول، ستظهر لك شاشة تفويض. يتيح هذا لتطبيقك التفاعل مع مساحة العمل الخاصة بك. - -انقر **Authorize** للمتابعة. - -
- شاشة تفويض واجهة الأوامر (CLI) الخاصة بـ Twenty -
- -بمجرد منح التفويض، ستؤكّد الطرفية أن كل شيء قد تم إعداده. - -
- تم إنشاء هيكل التطبيق بنجاح -
- -### ابدأ التطوير - -انتقل إلى مجلد تطبيقك الجديد وابدأ خادم التطوير: - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -يقوم هذا بمراقبة ملفات المصدر لديك، وإعادة البناء عند كل تغيير، ومزامنة تطبيقك تلقائيًا مع خادم Twenty المحلي. يفترض أن ترى لوحة حالة مباشرة في الطرفية. - -للحصول على مخرجات أكثر تفصيلاً (سجلات البناء، طلبات المزامنة، تتبعات الأخطاء)، استخدم العلم `--verbose`: - -```bash filename="Terminal" -yarn twenty dev --verbose -``` - - -وضع التطوير متاح فقط على مثيلات Twenty التي تعمل في وضع التطوير (`NODE_ENV=development`). المثيلات الإنتاجية ترفض طلبات مزامنة وضع التطوير. استخدم `yarn twenty deploy` للنشر إلى خوادم الإنتاج — اطّلع على [نشر التطبيقات](/l/ar/developers/extend/apps/publishing) للتفاصيل. - - -
- مخرجات الطرفية في وضع التطوير -
- -#### مزامنة لمرة واحدة باستخدام `yarn twenty dev --once` - -إذا كنت لا تريد تشغيل مراقب في الخلفية (مثلًا في خط أنابيب CI، أو خطاف Git، أو سير عمل مُؤتمت عبر سكربت)، فمرِّر الخيار `--once`. يُشغِّل خط الأنابيب نفسه مثل `yarn twenty dev` — إنشاء بيان البناء، تجميع الملفات، الرفع، المزامنة، إعادة توليد عميل API مضبوط الأنواع — ولكنه **ينهي التنفيذ فور اكتمال المزامنة**: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| أمر | السلوك | متى يُستخدم | -| ------------------------ | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| `yarn twenty dev` | يراقب ملفات المصدر ويعيد المزامنة عند كل تغيير. يستمر بالتشغيل حتى توقفه. | تطوير محلي تفاعلي — تريد لوحة حالة مباشرة وحلقة تغذية راجعة فورية. | -| `yarn twenty dev --once` | يجري عملية بناء واحدة + مزامنة واحدة، ثم ينهي التنفيذ برمز `0` عند النجاح أو `1` عند الفشل. | البرامج النصية، وCI، وخطافات ما قبل الالتزام، ووكلاء الذكاء الاصطناعي، وأي سير عمل غير تفاعلي. | - -كلا الوضعين يتطلبان خادم Twenty يعمل في وضع التطوير وجهة بعيدة موثَّقة — تنطبق المتطلبات المسبقة نفسها. - -### اعرض تطبيقك في Twenty - -افتح [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer) في متصفحك. انتقل إلى **Settings > Apps** واختر علامة التبويب **Developer**. يُفترض أن ترى تطبيقك مُدرجًا تحت **Your Apps**: - -
- قائمة "Your Apps" تعرض "My twenty app" -
- -انقر على **My twenty app** لفتح **تسجيل التطبيق** الخاص به. التسجيل عبارة عن سجل على مستوى الخادم يصف تطبيقك — اسمه، والمعرّف الفريد، وبيانات اعتماد OAuth، والمصدر (محلي، npm، أو tarball). يُخزَّن على الخادم، وليس داخل أي مساحة عمل محددة. عند تثبيت تطبيق في مساحة عمل، ينشئ Twenty **تطبيقًا** بنطاق مساحة العمل يُشير مرة أخرى إلى هذا التسجيل. يمكن تثبيت تسجيل واحد عبر عدة مساحات عمل على الخادم نفسه. - -
- تفاصيل تسجيل التطبيق -
- -انقر **View installed app** لعرض التطبيق المثبّت. تعرض علامة التبويب **About** الإصدار الحالي وخيارات الإدارة: - -
- التطبيق المثبّت — علامة تبويب About -
- -انتقل إلى علامة التبويب **Content** لمشاهدة كل ما يقدمه تطبيقك — الكائنات، والحقول، ودوال المنطق، والوكلاء: - -
- التطبيق المثبّت — علامة تبويب Content -
- -أنت جاهز تمامًا! حرّر أي ملف في `src/` وسيتم التقاط التغييرات تلقائيًا. - ---- - -## ما الذي يمكنك بناؤه - -تتكون التطبيقات من **كيانات** — يُعرَّف كل منها كملف TypeScript يحتوي على `export default` واحد: - -| كيان | ماذا يفعل | -| ---------------------- | ------------------------------------------------------------------------------------------------- | -| **الكائنات والحقول** | عرّف نماذج بيانات مخصّصة (مثل Post Card، Invoice) مع حقول محددة النوع | -| **الوظائف المنطقية** | دوال TypeScript على جانب الخادم يتم تشغيلها عبر مسارات HTTP، وجداول cron، أو أحداث قاعدة البيانات | -| **المكوّنات الأمامية** | مكوّنات React تُعرَض داخل واجهة مستخدم Twenty (اللوحة الجانبية، الودجات، قائمة الأوامر) | -| **المهارات والوكلاء** | قدرات الذكاء الاصطناعي — تعليمات قابلة لإعادة الاستخدام ومساعدون مستقلون ذاتيًا | -| **طرق العرض والتنقّل** | طرق عرض القوائم مُعدّة مسبقًا وعناصر قائمة الشريط الجانبي لكائناتك | -| **تخطيطات الصفحات** | صفحات تفاصيل سجلات مخصصة تتضمن علامات تبويب وعناصر واجهة | - -انتقل إلى [بناء التطبيقات](/l/ar/developers/extend/apps/building) للاطلاع على دليل مفصّل لكل نوع من الكيانات. - ---- - -## هيكل المشروع - -تولّد أداة إنشاء الهيكل بنية الملفات التالية: - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .oxlintrc.json - tsconfig.json - tsconfig.spec.json # TypeScript config for tests - vitest.config.ts # Vitest test runner configuration - LLMS.md - README.md - .github/ - └── workflows/ - └── ci.yml # GitHub Actions CI workflow - public/ # Public assets (images, fonts, etc.) - src/ - ├── application-config.ts # Required — main application configuration - ├── default-role.ts # Default role for logic functions - ├── constants/ - │ └── universal-identifiers.ts # Auto-generated UUIDs and app metadata - └── __tests__/ - ├── setup-test.ts # Test setup (server health check, config) - └── app-install.integration-test.ts # Integration test -``` - -### البدء من مثال - -للبدء من مثال أكثر اكتمالًا يضم كائنات وحقولًا مخصّصة، ودوال المنطق، ومكوّنات الواجهة الأمامية، وغير ذلك، استخدم الخيار `--example`: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -تُستمد الأمثلة من الدليل [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) على GitHub. يمكنك أيضًا إنشاء هيكل لكيانات فردية داخل مشروع قائم باستخدام `yarn twenty add` (انظر [بناء التطبيقات](/l/ar/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add)). - -### الملفات الرئيسية - -| ملف / مجلد | الغرض | -| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `package.json` | يصرّح باسم تطبيقك وإصداره واعتماداته. يتضمن نصًا برمجيًا باسم `twenty` بحيث يمكنك تشغيل `yarn twenty help` للاطلاع على جميع الأوامر. | -| `src/application-config.ts` | **مطلوب.** ملف الإعداد الرئيسي لتطبيقك. | -| `src/default-role.ts` | الدور الافتراضي الذي يتحكم بما يمكن لدوال المنطق الوصول إليه. | -| `src/constants/universal-identifiers.ts` | معرّفات UUID وبيانات التعريف للتطبيق، والمولَّدة تلقائيًا (اسم العرض، الوصف). | -| `src/__tests__/` | اختبارات تكامل (إعداد + اختبار مثال). | -| `public/` | أصول ثابتة (صور، خطوط) تُقدَّم مع تطبيقك. | - -## خادم التطوير المحلي - -لقد قامت أداة إنشاء الهيكل بالفعل بتشغيل خادم Twenty محليًا لك. لإدارته لاحقًا، استخدم `yarn twenty server`: - -| أمر | الوصف | -| -------------------------------------- | --------------------------------------------- | -| `yarn twenty server start` | بدء الخادم المحلي (يسحب الصورة إذا لزم الأمر) | -| `yarn twenty server start --port 3030` | ابدأ على منفذ مخصّص | -| `yarn twenty server start --test` | ابدأ مثيل اختبار منفصل على المنفذ 2021 | -| `yarn twenty server stop` | إيقاف الخادم (مع الحفاظ على البيانات) | -| `yarn twenty server status` | عرض حالة الخادم، وعنوان URL، وبيانات الاعتماد | -| `yarn twenty server logs` | بث سجلات الخادم | -| `yarn twenty server logs --lines 100` | عرض آخر 100 سطر من السجلات | -| `yarn twenty server reset` | حذف جميع البيانات والبدء من جديد | - -يتم الاحتفاظ بالبيانات عبر عمليات إعادة التشغيل في وحدتي تخزين Docker (`twenty-app-dev-data` لـ PostgreSQL، و`twenty-app-dev-storage` للملفات). استخدم `reset` لمسح كل شيء والبدء من جديد. - -### تشغيل مثيل الاختبار - -مرر `--test` إلى أي أمر `server` لإدارة مثيل ثانٍ معزول تمامًا — مفيد لتشغيل اختبارات التكامل أو للتجربة من دون لمس بيانات التطوير الرئيسية لديك. - -| أمر | الوصف | -| ---------------------------------- | ---------------------------------------------------- | -| `yarn twenty server start --test` | بدء مثيل الاختبار (المنفذ الافتراضي 2021) | -| `yarn twenty server stop --test` | إيقاف مثيل الاختبار | -| `yarn twenty server status --test` | عرض حالة مثيل الاختبار، وعنوان URL، وبيانات الاعتماد | -| `yarn twenty server logs --test` | بث سجلات مثيل الاختبار | -| `yarn twenty server reset --test` | محو بيانات الاختبار والبدء من جديد | - -يعمل مثيل الاختبار في حاوية Docker خاصة به (`twenty-app-dev-test`) مع وحدات تخزين مخصصة (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) وتهيئة مستقلة، بحيث يمكنه العمل بالتوازي مع مثيلك الرئيسي من دون تعارضات. اجمع `--test` مع `--port` لتجاوز القيمة الافتراضية 2021. - - - يتطلّب الخادم أن يكون **Docker** قيد التشغيل. إذا ظهرت لك رسالة خطأ "Docker not running"، فتأكّد من تشغيل Docker Desktop (أو خادوم Docker). - - -## إعداد يدوي (بدون المهيئ) - -إذا كنت تفضّل إعداد الأمور بنفسك بدلًا من استخدام `create-twenty-app`، فيمكنك ذلك بخطوتين. - -**1. أضِف `twenty-sdk` و`twenty-client-sdk` كاعتمادات:** - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -**2. أضِف نصًا برمجيًا باسم `twenty` إلى `package.json` لديك:** - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -يمكنك الآن تشغيل `yarn twenty dev`، و`yarn twenty help`، وجميع الأوامر الأخرى. - - -لا تثبّت `twenty-sdk` عالميًا. استخدمه دائمًا كاعتماد محلي للمشروع بحيث يتمكن كل مشروع من تثبيت إصداره الخاص. - - -## استكشاف الأخطاء وإصلاحها - -إذا واجهت مشاكل: - -* تأكّد من أن **Docker قيد التشغيل** قبل تشغيل أداة إنشاء الهيكل مع مثيل محلي. -* تأكّد من أنك تستخدم **Node.js 24+** (`node -v` للتحقق). -* تأكّد من **تمكين Corepack** (`corepack enable`) حتى يتوفر Yarn 4. -* جرّب حذف `node_modules` وتشغيل `yarn install` مرة أخرى إذا بدت الاعتمادات معطّلة. - -ما زلت عالقًا؟ اطلب المساعدة على [خادم Twenty على Discord](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout.mdx deleted file mode 100644 index 1673f531bb..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: التخطيط -description: عرّف طرق العرض، وعناصر قائمة التنقّل، وتخطيطات الصفحات لتشكيل كيفية ظهور تطبيقك في Twenty. -icon: table-columns ---- - -تتحكّم كيانات التخطيط في كيفية ظهور تطبيقك داخل واجهة مستخدم Twenty — ما الذي يوجد في الشريط الجانبي، وأي العروض المحفوظة تأتي مع التطبيق، وكيف يتم ترتيب صفحة تفاصيل السجل. - -## مفاهيم التخطيط - -| المفهوم | ما الذي يتحكّم فيه | كيان | -| ---------------------------- | ------------------------------------------------------------------------------- | -------------------------- | -| **عرض** | تكوين قائمة محفوظة لكائن — الحقول المرئية، والترتيب، وعوامل التصفية، والمجموعات | `defineView` | -| **عنصر قائمة التنقّل** | عنصر في الشريط الجانبي الأيسر يرتبط بعرض أو بعنوان URL خارجي | `defineNavigationMenuItem` | -| **تخطيط الصفحة** | علامات التبويب وعناصر الواجهة التي تشكّل صفحة تفاصيل السجل | `definePageLayout` | -| **علامة تبويب تخطيط الصفحة** | علامة تبويب مستقلة مرفقة بتخطيط صفحة موجودة (قياسي أو خاص بتطبيقك) | `definePageLayoutTab` | - -تشير العروض، وعناصر التنقّل، وتخطيطات الصفحات إلى بعضها البعض عبر `universalIdentifier`: - -* يشير **عنصر قائمة التنقّل** من النوع `VIEW` إلى معرّف `defineView`، بحيث يفتح رابط الشريط الجانبي ذلك العرض المحفوظ. -* يستهدف **تخطيط الصفحة** من النوع `RECORD_PAGE` كائنًا ويمكنه تضمين [مكوّنات الواجهة الأمامية](/l/ar/developers/extend/apps/front-components) داخل علامات التبويب الخاصة به بوصفها عناصر واجهة. - - - - -العروض هي تكوينات محفوظة لكيفية عرض سجلات كائن ما — بما في ذلك الحقول المرئية وترتيبها وأي مرشّحات أو مجموعات مُطبَّقة. استخدم `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` يحدّد الكائن الذي ينطبق عليه هذا العرض. -* `key` يحدّد نوع العرض (مثل `ViewKey.INDEX` لعرض القائمة الرئيسي). -* `fields` يتحكّم في الأعمدة الظاهرة وترتيبها. يشير كل حقل إلى `fieldMetadataUniversalIdentifier`. -* يمكنك أيضًا تعريف `filters` و`filterGroups` و`groups` و`fieldGroups` لمزيد من التكوينات المتقدمة. -* `position` يتحكّم في الترتيب عند وجود عدة عروض لنفس الكائن. - - - - -تضيف عناصر قائمة التنقل إدخالات مخصّصة إلى الشريط الجانبي لمساحة العمل. استخدم `defineNavigationMenuItem()` للارتباط بالعروض أو عناوين 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` يحدّد إلى ماذا يرتبط عنصر القائمة: `NavigationMenuItemType.VIEW` لعرض محفوظ، أو `NavigationMenuItemType.LINK` لعنوان URL خارجي. -* لروابط العروض، عيِّن `viewUniversalIdentifier`. لروابط خارجية، عيِّن `link`. -* `position` يتحكّم في الترتيب ضمن الشريط الجانبي. -* `icon` و`color` (اختياريان) يخصّصان المظهر. - - - - -تتيح لك تخطيطات الصفحات تخصيص مظهر صفحة تفاصيل السجل — ما الألسنة التي تظهر، وما الويدجتات داخل كل لسان، وكيف يتم ترتيبها. استخدم `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` داخل لسان أن يعرض مكوّنًا أماميًا أو قائمة علاقات أو أنواع ويدجت مدمجة أخرى. -* `position` على الألسنة يتحكّم في ترتيبها. استخدم قيمًا أعلى (مثل 50) لوضع الألسنة المخصّصة بعد الألسنة المدمجة. - - - - -`definePageLayoutTab` يتيح لتطبيقك إرفاق علامة تبويب واحدة — مع عناصر واجهة اختيارية — إلى تخطيط صفحة **موجود**. أشيع حالات الاستخدام هي إضافة علامة تبويب مخصصة (على سبيل المثال، علامة تبويب للتحليلات أو ملخص الذكاء الاصطناعي) إلى إحدى صفحات السجل المضمنة في Twenty، أو إلى تخطيط صفحة يوفره تطبيقك بالفعل. - -يجب أن يكون تخطيط الصفحة المستهدف إما تخطيط صفحة Twenty **قياسيًا** أو تخطيطًا مُعرَّفًا بواسطة **تطبيقك أنت**؛ المراجع عبر التطبيقات إلى تخطيطات صفحات مملوكة لتطبيق آخر مُثبّت غير مدعومة حاليًا. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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` **مطلوب** عند استخدام `definePageLayoutTab` ويجب أن يشير إلى تخطيط صفحة موجود بالفعل وقت التثبيت (سواء قياسيًا أو تابعًا لتطبيقك). عند فقدان تخطيط الصفحة الأب، يفشل التثبيت برسالة خطأ تحقق واضحة. -* `widgets` نطاقها مقتصر على علامة التبويب هذه فقط — فهي تُشير إلى مكونات الواجهة الأمامية، والعروض، وما إلى ذلك تمامًا مثل عناصر الواجهة المعرّفة مضمّنة داخل `definePageLayout`. -* `position` يتحكّم في الترتيب مقارنةً بعلامات التبويب الموجودة على التخطيط المستهدف. اختر قيمة تضع علامة التبويب الخاصة بك في الموضع الذي تريده بالنسبة إلى علامات التبويب المضمنة. -* استخدم هذا بدلًا من `definePageLayout` عندما تريد فقط **الإضافة** إلى تخطيط موجود. استخدم `definePageLayout` عندما تملك التخطيط بأكمله (عادةً ما يكون `RECORD_PAGE` لكائن توفّره في تطبيقك، أو `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index b6a57bf749..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,560 +0,0 @@ ---- -title: الوظائف المنطقية -description: عرّف دوال TypeScript على جانب الخادم مع HTTP وcron ومشغّلات أحداث قاعدة البيانات. -icon: bolt ---- - -دوال المنطق هي دوال TypeScript على جانب الخادم تعمل على منصة Twenty. يمكن تشغيلها بواسطة طلبات HTTP أو جداول cron أو أحداث قاعدة البيانات — كما يمكن إتاحتها كأدوات لوكلاء الذكاء الاصطناعي. - - - - -كل ملف وظيفة يستخدم `defineLogicFunction()` لتصدير تكوين مع معالج ومشغّلات اختيارية. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -أنواع المشغّلات المتاحة: -* **httpRoute**: يعرِض وظيفتك على مسار وطريقة HTTP **تحت نقطة النهاية `/s/`**: -> مثال: `path: '/post-card/create'` يمكن استدعاؤه عبر `https://your-twenty-server.com/s/post-card/create` -* **cron**: يشغّل وظيفتك على جدول باستخدام تعبير CRON. -* **databaseEvent**: يعمل على أحداث دورة حياة كائنات مساحة العمل. عندما تكون عملية الحدث هي `updated`، يمكن تحديد الحقول المحددة المراد الاستماع إليها في مصفوفة `updatedFields`. إذا تُركت غير معرّفة أو فارغة، فسيؤدي أي تحديث إلى تشغيل الدالة. -> مثال: `person.updated`، `*.created`، `company.*` - - -يمكنك أيضًا تنفيذ دالة يدويًا باستخدام CLI: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -يمكنك متابعة السجلات باستخدام: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### حمولة مشغل المسار - -عندما يستدعي مُشغِّل المسار وظيفتك المنطقية، فإنها تتلقّى كائن `RoutePayload` الذي يتبع [صيغة AWS HTTP API v2](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). -استورد نوع `RoutePayload` من `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -يحتوي نوع `RoutePayload` على البنية التالية: - - | الخاصية | النوع | الوصف | مثال | - | ---------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | رؤوس HTTP (فقط تلك المدرجة في `forwardedRequestHeaders`) | انظر القسم أدناه | - | `queryStringParameters` | `Record\` | معلمات سلسلة الاستعلام (تُضمّ القيم المتعددة باستخدام فواصل) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | معلمات المسار المستخرجة من نمط المسار | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | جسم الطلب المُحلَّل (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Original UTF-8 request body, before JSON parsing. Useful for verifying HMAC-style webhook signatures (e.g. GitHub's `X-Hub-Signature-256`, Stripe). `undefined` when the runtime did not preserve it. | | - | `isBase64Encoded` | `boolean` | ما إذا كان جسم الطلب مُرمَّزًا بترميز 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 }; -}; -``` - - -تُحوَّل أسماء الرؤوس إلى أحرف صغيرة. يمكنك الوصول إليها باستخدام مفاتيح بأحرف صغيرة (على سبيل المثال، `event.headers['content-type']`). - - -#### إتاحة دالة كأداة - -يمكن إتاحة الدوال المنطقية بوصفها **أدوات** لوكلاء الذكاء الاصطناعي وسير العمل. عند تمييز دالة كأداة، تصبح قابلة للاكتشاف بواسطة ميزات الذكاء الاصطناعي في Twenty ويمكن استخدامها في أتمتة سير العمل. - -لتمييز دالة منطقية كأداة، عيِّن `isTool: true`: - -```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, - isTool: true, -}); -``` - -النقاط الرئيسية: - -* يمكنك دمج `isTool` مع المشغِّلات — إذ يمكن للدالة أن تكون أداة (قابلة للاستدعاء من قِبل وكلاء الذكاء الاصطناعي) وأن تُشغَّل بواسطة الأحداث في الوقت نفسه. -* **`toolInputSchema`** (اختياري): كائن JSON Schema يصف المعلمات التي تقبلها دالتك. يُحسَب المخطط تلقائيًا من خلال تحليل ساكن للشيفرة المصدرية، ولكن يمكنك تعيينه صراحةً: - -```ts -export default defineLogicFunction({ - ..., - toolInputSchema: { - 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'], - }, -}); -``` - - -**اكتب `description` جيدًا.** يعتمد وكلاء الذكاء الاصطناعي على حقل `description` الخاص بالدالة لتحديد وقت استخدام الأداة. كن محددًا بشأن ما تفعله الأداة ومتى ينبغي استدعاؤها. - - - - - -دالة ما بعد التثبيت هي دالة منطقية تعمل تلقائيًا بعد تثبيت تطبيقك على مساحة عمل. ينفّذه الخادم **بعد** مزامنة البيانات الوصفية للتطبيق وإنشاء عميل SDK، بحيث تكون مساحة العمل جاهزة تمامًا للاستخدام ويكون المخطط الجديد مطبَّقًا. تشمل حالات الاستخدام النموذجية تهيئة البيانات الافتراضية، وإنشاء السجلات الأولية، وتكوين إعدادات مساحة العمل، أو توفير الموارد على خدمات جهات خارجية. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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 exec --postInstall -``` - -النقاط الرئيسية: -* تستخدم دوال ما بعد التثبيت `definePostInstallLogicFunction()` — وهو إصدار متخصص يستبعد إعدادات المُشغِّل (`cronTriggerSettings` و`databaseEventTriggerSettings` و`httpRouteTriggerSettings` و`isTool`). -* يتلقى المعالج `InstallPayload` يحتوي على `{ previousVersion?: string; newVersion: string }` — حيث إن `newVersion` هو الإصدار الجاري تثبيته، و`previousVersion` هو الإصدار الذي كان مُثبّتًا سابقًا (أو `undefined` عند التثبيت الأولي). استخدم هذه القيم للتمييز بين عمليات التثبيت الجديدة والترقيات ولتشغيل منطق الترحيل الخاص بالإصدار. -* **موعد تشغيل الخطاف**: في عمليات التثبيت الجديدة فقط، افتراضيًا. مرّر `shouldRunOnVersionUpgrade: true` إذا كنت تريد تشغيله أيضًا عند ترقية التطبيق من إصدار سابق. عند إغفاله، تكون القيمة الافتراضية للعلم `false`، وتتجاوز الترقيات هذا الخطاف. -* **نموذج التنفيذ — غير متزامن افتراضيًا، والتزامني اختياري**: يتحكّم العلم `shouldRunSynchronously` في كيفية تنفيذ ما بعد التثبيت. - * `shouldRunSynchronously: false` *(الإعداد الافتراضي)* — يتم **إدراج الخطاف في قائمة الرسائل** مع `retryLimit: 3` ويعمل بشكل غير متزامن داخل عامل عمل. يعود ردّ التثبيت بمجرد وضع المهمة في الطابور، لذا فإن معالجًا بطيئًا أو متعطلًا لا يحجب المستدعي. سيُجرِّب العامل إعادة المحاولة حتى ثلاث مرات. **استخدم هذا للمهام طويلة التشغيل** — بَذر مجموعات بيانات كبيرة، استدعاء واجهات برمجة تطبيقات خارجية بطيئة، تهيئة موارد خارجية، أو أي شيء قد يتجاوز نافذة استجابة HTTP المعقولة. - * `shouldRunSynchronously: true` — يُنفّذ الخطاف **ضمن تدفّق التثبيت مباشرةً** (نفس المنفِّذ كما قبل التثبيت). يَحجُب طلب التثبيت حتى ينتهي المعالج، وإذا رمى استثناءً، سيتلقى مستدعي التثبيت `POST_INSTALL_ERROR`. لا توجد محاولات إعادة تلقائية. **استخدم هذا للمهام السريعة التي يجب إكمالها قبل الاستجابة** — مثل إظهار خطأ تحقق للمستخدم، أو إعداد سريع سيعتمد عليه العميل مباشرةً بعد عودة نداء التثبيت. ضع في اعتبارك أن ترحيل البيانات الوصفية يكون قد طُبِّق بالفعل عند تشغيل ما بعد التثبيت، لذلك فإن فشل الوضع المتزامن **لا** يعيد التغييرات على المخطط إلى الوراء — بل يكتفي بإبراز الخطأ. -* تأكّد من أن معالجك قابل للتنفيذ المتكرر دون آثار جانبية. في الوضع غير المتزامن قد تُعيد قائمة الانتظار المحاولة حتى ثلاث مرات؛ وفي أي من الوضعين قد يعمل الخطاف مجددًا أثناء الترقيات عند ضبط `shouldRunOnVersionUpgrade: true`. -* متغيرات البيئة `APPLICATION_ID` و`APP_ACCESS_TOKEN` و`API_URL` متاحة داخل المعالج (كما في أي دالة منطق أخرى)، لذا يمكنك استدعاء واجهة Twenty API باستخدام رمز وصول للتطبيق مقيّد بنطاق تطبيقك. -* يُسمح بدالة ما بعد التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة. -* تُرفَق خصائص الدالة `universalIdentifier` و`shouldRunOnVersionUpgrade` و`shouldRunSynchronously` تلقائيًا ببيان التطبيق ضمن الحقل `postInstallLogicFunction` أثناء عملية البناء — ولا تحتاج إلى الإشارة إليها في `defineApplication()`. -* تم تعيين مهلة افتراضية إلى 300 ثانية (5 دقائق) للسماح بمهام الإعداد الأطول مثل تهيئة البيانات. -* **لا يُنفَّذ في وضع التطوير**: عند تسجيل تطبيق محليًا (عبر `yarn twenty dev`)، يتجاوز الخادم تدفّق التثبيت بالكامل ويُزامن الملفات مباشرةً عبر مراقِب CLI — لذا لن يعمل ما بعد التثبيت في وضع التطوير مطلقًا، بغضّ النظر عن `shouldRunSynchronously`. استخدم `yarn twenty exec --postInstall` لتشغيله يدويًا على مساحة عمل قيد التشغيل. - - - - -دالة ما قبل التثبيت هي دالة منطقية تعمل تلقائيًا أثناء التثبيت، **قبل تطبيق ترحيل البيانات الوصفية لمساحة العمل**. تتشارك نفس بنية الحمولة مع ما بعد التثبيت (`InstallPayload`)، لكنها موضوعة أبكر في تدفّق التثبيت كي تجهّز حالة يعتمد عليها الترحيل القادم — ومن الاستخدامات الشائعة: نسخ البيانات احتياطيًا، التحقق من التوافق مع المخطط الجديد، أو أرشفة السجلات التي ستُعاد هيكلتها أو ستُحذف. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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 exec --preInstall -``` - -النقاط الرئيسية: -* تستخدم دوال ما قبل التثبيت `definePreInstallLogicFunction()` — نفس الإعدادات المتخصصة كما في ما بعد التثبيت، لكنها مرتبطة بموضع مختلف ضمن دورة الحياة. -* يتلقّى كلٌّ من معالجي ما قبل التثبيت وما بعد التثبيت النوع نفسه `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. استورده مرة واحدة وأعد استخدامه لكلا الخطافين. -* **موعد تشغيل الخطاف**: موضوع مباشرةً قبل ترحيل البيانات الوصفية لمساحة العمل (`synchronizeFromManifest`). قبل التنفيذ، يُشغِّل الخادم مزامنة "pared-down sync" ذات طابع إضافي فقط تقوم بتسجيل دالة ما قبل التثبيت للإصدار **الجديد** في البيانات الوصفية لمساحة العمل — دون لمس أي شيء آخر — ثم يُنفّذها. لأن هذه المزامنة «إضافية فقط»، تبقى كائنات وحقول وبيانات الإصدار السابق سليمة عند تشغيل معالجك: يمكنك قراءة حالة ما قبل الترحيل ونسخها احتياطيًا بأمان. -* **نموذج التنفيذ**: يُنفَّذ ما قبل التثبيت **بشكل متزامن** و**يحجب عملية التثبيت**. إذا رمى المعالج استثناءً، تُلغى عملية التثبيت قبل تطبيق أي تغييرات على المخطط — وتبقى مساحة العمل على الإصدار السابق بحالة متّسقة. هذا مقصود: ما قبل التثبيت هو فرصتك الأخيرة لرفض ترقية تنطوي على مخاطر. -* كما هو الحال مع ما بعد التثبيت، يُسمح بدالة ما قبل التثبيت واحدة فقط لكل تطبيق. تُربَط تلقائيًا ببيان التطبيق تحت `preInstallLogicFunction` أثناء عملية البناء. -* **لا يُنفَّذ في وضع التطوير**: كما في ما بعد التثبيت — يتم تجاوز تدفّق التثبيت بالكامل للتطبيقات المسجّلة محليًا، لذا لن يعمل ما قبل التثبيت مطلقًا عند `yarn twenty dev`. استخدم `yarn twenty exec --preInstall` لتشغيله يدويًا. - - - - -كلا الخطافين جزء من تدفّق التثبيت نفسه ويتلقّيان نفس `InstallPayload`. الاختلاف يكمن في **موعد** تشغيلهما نسبةً إلى ترحيل البيانات الوصفية لمساحة العمل، وهذا يغيّر البيانات التي يمكنهما التعامل معها بأمان. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -ما قبل التثبيت دائمًا **متزامن** (يحجب التثبيت ويمكنه إحباطه). ما بعد التثبيت **غير متزامن افتراضيًا** — يُدرج على عامل مع محاولات إعادة تلقائية — لكن يمكن التبديل إلى تنفيذ متزامن عبر `shouldRunSynchronously: true`. راجع الأكورديون `definePostInstallLogicFunction` أعلاه لمعرفة متى تستخدم كل وضع. - -**استخدم `post-install` لأي شيء يتطلّب وجود المخطط الجديد.** وهذا هو السيناريو الشائع: - -* بَذر بيانات افتراضية (إنشاء سجلات أولية وعروض افتراضية ومحتوى تجريبي) للكائنات والحقول المضافة حديثًا. -* تسجيل خطافات الويب مع خدمات أطراف ثالثة بعد أن حصل التطبيق على بيانات الاعتماد الخاصة به. -* استدعاء واجهة برمجة التطبيقات الخاصة بك لإكمال إعداد يعتمد على البيانات الوصفية المتزامنة. -* منطق idempotent لتحقيق "تأكّد من وجود هذا" والذي ينبغي مواءمة الحالة في كل ترقية — بالاقتران مع `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 => { - 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` وتحتاج أولًا إلى حذف الصفوف ذات القيم الفارغة أو إصلاحها. -* **التحقق من التوافق ورفض الترقية إذا تعذّر ترحيل البيانات الحالية بسلاسة** — ارمِ من داخل المعالج وسيُلغى التثبيت دون تطبيق أي تغييرات. هذا أكثر أمانًا من اكتشاف عدم التوافق في منتصف الترحيل. -* **إعادة تسمية البيانات أو إعادة تعيين مفاتيحها** قبل تغيير في المخطط قد يؤدي إلى فقدان الارتباط. - -مثال — أرشف السجلات قبل ترحيل هدّام: - -```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 => { - // 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` (الإعداد الافتراضي) | - - -إذا ساورك الشك، فاجعل الافتراضي هو **post-install**. الجأ إلى ما قبل التثبيت فقط عندما يكون الترحيل نفسه هدّامًا وتحتاج إلى التقاط الحالة السابقة قبل أن تزول. - - - - - -## عملاء واجهة برمجة تطبيقات مضبوطة الأنواع (`twenty-client-sdk`) - -توفر حزمة `twenty-client-sdk` عميلين لـ GraphQL ذوي أنواع ثابتة للتفاعل مع واجهة Twenty البرمجية من وظائفك المنطقية ومكوّنات الواجهة الأمامية. - -| العميل | استيراد | نقطة النهاية | مُولَّد؟ | -| ------------------- | ---------------------------- | --------------------------------------------------- | -------------------------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — بيانات مساحة العمل (السجلات، الكائنات) | نعم، في وقت التطوير/البناء | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — تكوين مساحة العمل، رفع الملفات | لا، يأتي مُجهزًا مسبقًا | - - - - -`CoreApiClient` هو العميل الرئيسي للاستعلام وتعديل بيانات مساحة العمل. يُولَّد من مخطط مساحة العمل لديك أثناء `yarn twenty dev` أو `yarn twenty 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, - }, -}); -``` - -يستخدم العميل صياغة مجموعة اختيار: مرِّر `true` لتضمين حقل، واستخدم `__args` للوسيطات، وعشّش الكائنات للعلاقات. ستحصل على إكمال تلقائي كامل وفحص للأنواع يعتمد على مخطط مساحة العمل لديك. - - -**يتم توليد CoreApiClient في وقت التطوير/البناء.** إذا استخدمته دون تشغيل `yarn twenty dev` أو `yarn twenty build` أولًا، فسيؤدي ذلك إلى خطأ. تحدث عملية التوليد تلقائيًا — إذ يستطلع CLI مخطط GraphQL لمساحة عملك وينشئ عميلًا مضبوط الأنواع باستخدام `@genql/cli`. - - -#### استخدام CoreSchema للتعليقات التوضيحية للأنواع - -`CoreSchema` يوفّر أنواع TypeScript المطابقة لكائنات مساحة العمل لديك — مفيد لتعيين أنواع حالة المكوّن أو معاملات الدوال: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -يأتي `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` الخاص بالحقل (وليس معرّفه الخاص بمساحة العمل)، بحيث يعمل كود الرفع لديك عبر أي مساحة عمل مُثبَّت فيها تطبيقك. -* العنوان `url` المُعاد هو عنوان URL موقّع يمكنك استخدامه للوصول إلى الملف المرفوع. - - - - - - عند تشغيل كودك على Twenty (وظائف منطقية أو مكوّنات أمامية)، يقوم النظام الأساسي بحقن بيانات الاعتماد كمتغيرات بيئية: - - * `TWENTY_API_URL` — عنوان URL الأساسي لواجهة Twenty البرمجية - * `TWENTY_APP_ACCESS_TOKEN` — مفتاح قصير العمر ذو نطاق يقتصر على الدور الافتراضي لوظيفة تطبيقك - - لست **بحاجة** إلى تمرير هذه القيم إلى العملاء — فهي تُقرأ تلقائيًا من `process.env`. تُحدَّد أذونات مفتاح واجهة برمجة التطبيقات بواسطة الدور المشار إليه في `defaultRoleUniversalIdentifier` ضمن `application-config.ts`. - diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/publishing.mdx deleted file mode 100644 index 8b384c0a93..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,254 +0,0 @@ ---- -title: النشر -icon: رفع -description: وزّع تطبيق Twenty الخاص بك على سوق Twenty أو انشره داخليًا. ---- - -## نظرة عامة - -بمجرد أن يكون تطبيقك [مبنيًا ومختبرًا محليًا](/l/ar/developers/extend/apps/building)، لديك مساران لتوزيعه: - -* **نشر أرشيف tar** — ارفع تطبيقك مباشرةً إلى خادم Twenty محدد للاستخدام الداخلي أو الخاص. -* **النشر على npm** — أدرج تطبيقك في سوق Twenty ليتسنى لأي مساحة عمل اكتشافه وتثبيته. - -كلا المسارين يبدآن من نفس خطوة **build**. - -## بناء تطبيقك - -شغّل أمر build لتجميع تطبيقك وإنشاء ملف `manifest.json` جاهز للتوزيع: - -```bash filename="Terminal" -yarn twenty build -``` - -يقوم هذا بتجميع مصادر TypeScript، وتحويل دوال المنطق ومكوّنات الواجهة الأمامية، وكتابة كل شيء إلى `.twenty/output/`. أضِف `--tarball` لإنتاج حزمة `.tgz` أيضًا للتوزيع اليدوي أو لأمر deploy. - -## النشر إلى خادم (tarball) - -بالنسبة للتطبيقات التي لا تريد إتاحتها للعامة — مثل الأدوات المملوكة، أو عمليات التكامل الخاصة بالمؤسسات فقط، أو الإصدارات التجريبية — يمكنك نشر tarball مباشرةً إلى خادم Twenty. - -### المتطلبات الأساسية - -قبل النشر، تحتاج إلى remote مُعدّ يشير إلى خادم الهدف. تُخزّن remotes عنوان URL للخادم وبيانات اعتماد المصادقة محليًا في `~/.twenty/config.json`. - -أضِف remote: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### النشر - -بناء تطبيقك ورفعه إلى الخادم في خطوة واحدة: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### مشاركة تطبيق منشور - - -تُعد مشاركة التطبيقات الخاصة (tarball) عبر مساحات العمل ميزة ضمن **Enterprise**. ستعرض علامة التبويب **Distribution** مطالبة بالترقية بدلًا من عناصر التحكم في المشاركة حتى تحتوي مساحة العمل لديك على مفتاح Enterprise صالح. اطلع على [الإعدادات > لوحة الإدارة > Enterprise](/settings/admin-panel#enterprise) لتنشيطه. - - -تطبيقات tarball لا تُدرَج في السوق العامة، لذا لن تكتشفها مساحات العمل الأخرى على الخادم نفسه عبر الاستعراض. بمجرد أن تصبح مساحة العمل لديك ضمن خطة Enterprise، يمكنك مشاركة تطبيق تم نشره كما يلي: - -1. اذهب إلى **الإعدادات > التطبيقات > التسجيلات** وافتح تطبيقك -2. في علامة التبويب **التوزيع**، انقر **نسخ رابط المشاركة** -3. شارك هذا الرابط مع المستخدمين في مساحات عمل أخرى — سيأخذهم مباشرةً إلى صفحة تثبيت التطبيق - -يستخدم رابط المشاركة عنوان URL الأساسي للخادم (من دون أي نطاق فرعي لمساحة عمل)، لذا يعمل مع أي مساحة عمل على الخادم. - -### إدارة الإصدارات - -عند تحديث تطبيق tarball منشور مسبقًا، يشترط الخادم أن تكون قيمة `version` في `package.json` **أعلى قطعًا** (وفق ترتيب [الإصدار الدلالي](https://semver.org)) من الإصدار المنشور حاليًا. إعادة نشر الإصدار نفسه، أو دفع إصدار أدنى، يُرفَض قبل تخزين ملف tarball — سترى خطأ `VERSION_ALREADY_EXISTS` من CLI. - -لطرح تحديث: - -1. قم بزيادة الحقل `version` في ملف `package.json` (مثلًا: `1.2.3` → `1.2.4`، `1.3.0`، أو `2.0.0`) -2. شغّل `yarn twenty deploy` (أو `yarn twenty deploy --remote production`) -3. سترى مساحات العمل التي ثبّتت التطبيق الترقية متاحة في إعداداتها - - -علامات ما قبل الإصدار تعمل كما هو متوقع: زيادة `1.0.0-rc.1` → `1.0.0-rc.2` مسموح بها، ويُعترَف بالإصدار النهائي مثل `1.0.0` على أنه أعلى من `1.0.0-rc.5`. يجب أن يكون الإصدار في `package.json` بنفسه سلسلة semver صالحة. - - -{/* TODO: add screenshot of the Upgrade button */} - -## CI/CD المؤتمتة (مهام سير عمل مُولَّدة بالقوالب) - -التطبيقات المُولَّدة باستخدام `create-twenty-app` تأتي افتراضيًا مع مهمَّتي سير عمل من GitHub Actions ضمن `.github/workflows/`. هي جاهزة للتشغيل بمجرد دفع المستودع إلى GitHub — لا حاجة لأي إعداد إضافي لـ CI، وCD يتطلّب سرًّا واحدًا فقط. - -### CI — `ci.yml` - -يشغّل اختبارات التكامل عند كل دفع إلى `main` وعند كل طلب سحب. - -**ماذا يفعل:** - -1. يجلب مصدر تطبيقك. -2. ينشئ مثيلاً اختبارياً معزولاً من Twenty باستخدام الإجراء المركّب `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (المكافئ في CI للأمر `yarn twenty server start --test`). -3. يُفعِّل Corepack، ويُعدّ Node.js من ملف `.nvmrc` لديك، ويثبّت التبعيات بواسطة `yarn install --immutable`. -4. يشغّل `yarn test`، ويمرّر `TWENTY_API_URL` و`TWENTY_API_KEY` من المثيل الذي تم إنشاؤه بحيث تتمكّن اختباراتك من التواصل مع خادم حقيقي. - -**خيارات التكوين:** - -* `TWENTY_VERSION` (متغيّر بيئة، القيمة الافتراضية `latest`) — ثبّت نسخة خادم Twenty المستخدمة في CI عبر تعديل هذا في `ci.yml`. -* يتم تجميع التشغيل المتزامن حسب `github.ref` ويلغي التشغيلات قيد التقدّم عند أي دفع جديد. - -لا تتطلّب أي أسرار — مثيل الاختبار مؤقّت ويستمر فقط طوال مدّة المهمّة. - -### CD — `cd.yml` - -ينشر تطبيقك إلى خادم Twenty مُهيّأ عند كل دفع إلى `main`، وبشكل اختياري من طلب سحب عند تطبيق الوسم `deploy`. - -**ماذا يفعل:** - -1. يجلب رأس طلب السحب (للطلبات الموسومة) أو الالتزام المدفوع. -2. يشغّل `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — وهو المكافئ في CI للأمر `yarn twenty deploy`. -3. يشغّل `twentyhq/twenty/.github/actions/install-twenty-app@main` بحيث تُثبَّت النسخة المُنشَرة حديثًا في مساحة العمل المستهدفة. - -**التكوين المطلوب:** - -| الإعداد | حيث | الغرض | -| ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `env` في `cd.yml` (القيمة الافتراضية `http://localhost:3000`) | خادم Twenty الذي سيتم النشر إليه. غيّر هذا إلى عنوان URL لخادمك الحقيقي قبل أول استخدام. | -| `TWENTY_DEPLOY_API_KEY` | في مستودع GitHub **Settings → Secrets and variables → Actions** | مفتاح API يمتلك إذن النشر على الخادم المستهدف. | - - -القيمة الافتراضية لـ `TWENTY_DEPLOY_URL` وهي `http://localhost:3000` مجرد عنصر نائب — لن تصل إلى أي شيء من مُشغِّل مستضاف لدى GitHub. حدّثها إلى عنوان URL العام لخادمك (أو استخدم مُشغِّلًا مستضافًا ذاتيًا مع وصول شبكي) قبل تمكين CD. - - -**تشغيل نشر معاينة من طلب سحب:** - -أضِف الوسم `deploy` إلى طلب سحب. الشرط `if:` في `cd.yml` سيشغّل المهمّة لذلك الطلب مستخدمًا التزام رأس الطلب، مما يتيح لك التحقّق من التغيير على الخادم المستهدف قبل الدمج. - -### تثبيت الإجراءات القابلة لإعادة الاستخدام - -يشير كلا سيرَي العمل إلى إجراءات قابلة لإعادة الاستخدام عند `@main`، لذا تُلتقط تحديثات الإجراءات في مستودع `twentyhq/twenty` تلقائيًا. إذا كنت تريد بناءات حتمية، فاستبدِل `@main` بقيمة SHA لالتزام أو بوسم إصدار في كل سطر `uses:`. - -## النشر على npm - -يُتيح النشر على npm إمكانية العثور على تطبيقك في سوق Twenty. يمكن لأي مساحة عمل في Twenty استعراض تطبيقات السوق وتثبيتها وترقيتها مباشرةً من واجهة المستخدم. - -### المتطلبات - -* حساب على [npm](https://www.npmjs.com) -* الكلمة المفتاحية `twenty-app` في مصفوفة `keywords` في `package.json` (أضفها يدويًا — فهي غير مضمنة افتراضيًا في قالب `create-twenty-app`) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### بيانات التعريف لسوق التطبيقات - -يدعم إعداد `defineApplication()` حقولًا اختيارية تتحكم في كيفية ظهور تطبيقك في السوق. استخدم `logoUrl` و`screenshots` للإشارة إلى الصور من مجلد `public/`: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -اطّلع على [أكورديون defineApplication](/l/ar/developers/extend/apps/building#defineentity-functions) في صفحة بناء التطبيقات للاطلاع على القائمة الكاملة لحقول السوق (`author` و`category` و`aboutDescription` و`websiteUrl` و`termsUrl` وغيرها). - -### النشر - -```bash filename="Terminal" -yarn twenty publish -``` - -للنشر تحت dist-tag معيّن (مثلًا: `beta` أو `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### كيف تعمل آلية الاكتشاف في السوق - -يقوم خادم Twenty بمزامنة كتالوج السوق من سجل npm **كل ساعة**. - -يمكنك تشغيل المزامنة فورًا بدلًا من الانتظار: - -```bash filename="Terminal" -yarn twenty catalog-sync -# To target a specific remote: -# yarn twenty catalog-sync --remote production -``` - -تأتي بيانات التعريف المعروضة في السوق من إعداد `defineApplication()` — حقول مثل `displayName` و`description` و`author` و`category` و`logoUrl` و`screenshots` و`aboutDescription` و`websiteUrl` و`termsUrl`. - - -إذا لم يحدد تطبيقك `aboutDescription` في `defineApplication()`، فسيستخدم السوق تلقائيًا ملف `README.md` الخاص بحزمتك من npm كمحتوى لصفحة حول. هذا يعني أنه يمكنك الاحتفاظ بملف README واحد لكل من npm وسوق Twenty. إذا كنت تريد وصفًا مختلفًا في السوق، فقم بتعيين `aboutDescription` بشكل صريح. - - -### النشر عبر 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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -بالنسبة لأنظمة CI الأخرى (GitLab CI، وCircleCI، إلخ)، تنطبق الأوامر الثلاثة نفسها: `yarn install`، ثم `yarn twenty build`، ثم `npm publish` من `.twenty/output`. - - -**npm provenance** اختياري ولكنه موصى به. يضيف النشر باستخدام `--provenance` شارة ثقة إلى إدراجك على npm، مما يتيح للمستخدمين التحقق من أن الحزمة تم بناؤها من التزام محدد ضمن خط أنابيب CI عام. راجع [وثائق npm provenance](https://docs.npmjs.com/generating-provenance-statements) للحصول على تعليمات الإعداد. - - -## تثبيت التطبيقات - -بعد نشر التطبيق (npm) أو نشره (tarball)، يمكن لمساحات العمل تثبيته عبر واجهة المستخدم. - -اذهب إلى صفحة **الإعدادات > التطبيقات** في Twenty، حيث يمكن استعراض تطبيقات السوق والتطبيقات المنشورة عبر tarball وتثبيتها. - -{/* TODO: add screenshot of the UI when the app is registered */} - -يمكنك أيضًا تثبيت التطبيقات من سطر الأوامر: - -```bash filename="Terminal" -yarn twenty install -``` - - -يفرض الخادم اعتماد إصدارات semver عند التثبيت، بما يعكس القواعد المطبّقة عند النشر: - -* تثبيت الإصدار نفسه المثبّت بالفعل في مساحة عملك يُرفَض بخطأ `APP_ALREADY_INSTALLED`. -* تثبيت إصدار أدنى من الإصدار المثبّت حاليًا يُرفَض بخطأ `CANNOT_DOWNGRADE_APPLICATION`. - -لتثبيت إصدار أحدث، انشره (deploy) أو انشره إلى السجل (publish) أولًا، ثم أعد تشغيل `yarn twenty install`. - diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index 6afeea04e7..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: المهارات والوكلاء -description: عرّف مهارات ووكلاء الذكاء الاصطناعي لتطبيقك. -icon: robot ---- - - - المهارات والوكلاء حاليًا في مرحلة الألفا. الميزة تعمل لكنها لا تزال قيد التطور. - - -يمكن للتطبيقات تعريف قدرات ذكاء اصطناعي تعمل داخل مساحة العمل — تعليمات مهارات قابلة لإعادة الاستخدام ووكلاء بموجهات نظام مخصّصة. - - - - -تُحدِّد المهارات تعليمات وإمكانات قابلة لإعادة الاستخدام يمكن لوكلاء الذكاء الاصطناعي استخدامها داخل مساحة العمل لديك. استخدم `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` هو اسم العرض المقروء للبشر الظاهر في واجهة المستخدم. -* `content` يحتوي على تعليمات المهارة — وهو النص الذي يستخدمه وكيل الذكاء الاصطناعي. -* `icon` (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم. -* `description` (اختياري) يوفّر سياقًا إضافيًا حول غرض المهارة. - - - - -الوكلاء هم مساعدون ذكاء اصطناعي يعيشون داخل مساحة العمل لديك. استخدم `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` هو اسم العرض الظاهر في واجهة المستخدم. -* `prompt` هو موجه النظام الذي يحدّد سلوك الوكيل. -* `description` (اختياري) يوفّر سياقًا حول ما يفعله الوكيل. -* `icon` (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم. -* `modelId` (اختياري) يتجاوز نموذج الذكاء الاصطناعي الافتراضي الذي يستخدمه الوكيل. - - - diff --git a/packages/twenty-docs/l/ar/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/ar/developers/extend/capabilities/apps.mdx deleted file mode 100644 index fe75199df0..0000000000 --- a/packages/twenty-docs/l/ar/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: تطبيقات Twenty -description: أنشئ وأدِر تخصيصات Twenty على هيئة كود. ---- - - -التطبيقات حاليًا في مرحلة الألفا. الميزة تعمل لكنها لا تزال قيد التطور. - - -## ما هي التطبيقات؟ - -تتيح لك التطبيقات توسيع Twenty باستخدام كائنات وحقول مخصّصة ووظائف منطقية ومكوّنات الواجهة الأمامية ومهارات الذكاء الاصطناعي وغير ذلك — جميعها تُدار ككود. بدلًا من تكوين كل شيء عبر واجهة المستخدم، تعرّف نموذج بياناتك ومنطقك في TypeScript وتقوم بنشره إلى مساحة عمل واحدة أو أكثر. - -**ما الذي يمكنك بناؤه:** - -* **الكائنات والحقول المخصّصة** — وسّع نموذج بياناتك بكيانات جديدة أو أضف حقولًا إلى الكائنات الموجودة مثل Company أو Person -* **الوظائف المنطقية** — وظائف على جانب الخادم يتم تشغيلها بواسطة أحداث قاعدة البيانات، أو جداول cron، أو مسارات HTTP -* **مكوّنات الواجهة الأمامية** — مكوّنات React تُعرَض داخل واجهة مستخدم Twenty (صفحات السجل، قائمة الأوامر، اللوحات الجانبية) -* **مهارات ووكلاء الذكاء الاصطناعي** — وسّع ذكاء Twenty الاصطناعي بقدرات مخصّصة -* **العروض والتنقّل** — عروض محفوظة مُعدّة مسبقًا وروابط الشريط الجانبي - -## البدء السريع - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -يُنشئ هذا هيكل تطبيق جديدًا، ويبدأ اختياريًا خادم Twenty محليًا، ويبدأ في مراقبة ملفاتك لاكتشاف التغييرات. اطّلع على دليل [البدء](/l/ar/developers/extend/apps/getting-started) للحصول على شرح كامل. - -## أدلة تفصيلية - -| دليل | الوصف | -| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| [البدء](/l/ar/developers/extend/apps/getting-started) | إنشاء هيكل تطبيق، إعداد خادم محلي، بنية المشروع، التكامل المستمر | -| [بناء التطبيقات](/l/ar/developers/extend/apps/building) | تعريفات الكيانات (`defineObject`, `defineLogicFunction`, `defineFrontComponent`، إلخ)، عملاء API، حزم npm، الأصول العامة، الاختبار | -| [النشر](/l/ar/developers/extend/apps/publishing) | النشر إلى خادم، النشر إلى npm، السوق | - -## المفاهيم الأساسية - -### اكتشاف الكيانات - -يكتشف SDK الكيانات عبر فحص ملفات TypeScript لديك بحثًا عن استدعاءات `export default define({...})`. تسمية الملفات وبنية المجلدات مرنة — يعتمد الاكتشاف على AST وليس على المسارات. - -### أنواع الكيانات المتاحة - -| دالة | الغرض | -| ---------------------------------- | ------------------------------------------------ | -| `defineApplication()` | بيانات التعريف للتطبيق (مطلوبة، واحدة لكل تطبيق) | -| `defineObject()` | كائنات مخصّصة مع حقول | -| `defineField()` | حقول على الكائنات الموجودة | -| `defineLogicFunction()` | منطق على جانب الخادم مع مشغّلات | -| `defineFrontComponent()` | مكوّنات React ضمن واجهة مستخدم Twenty | -| `defineRole()` | أدوار الصلاحيات | -| `defineView()` | تكوينات العروض المحفوظة | -| `defineNavigationMenuItem()` | روابط التنقّل في الشريط الجانبي | -| `defineSkill()` | مهارات وكيل الذكاء الاصطناعي | -| `defineAgent()` | وكلاء ذكاء اصطناعي مع موجّهات | -| `definePageLayout()` | تخطيطات صفحات السجل المخصّصة | -| `definePreInstallLogicFunction()` | يعمل قبل تثبيت التطبيق | -| `definePostInstallLogicFunction()` | يعمل بعد تثبيت التطبيق | - -### سير عمل التطوير - -1. **`yarn twenty dev`** — يراقب ملفات المصدر، ويعيد البناء عند التغيير، ويُزامن مع الخادم، ويولّد عملاء API بأنواع محددة -2. **`yarn twenty build`** — ينتج إصدارًا قابلًا للتوزيع -3. **`yarn twenty deploy`** — ينشر إلى خادم Twenty بعيد -4. **`yarn twenty add`** — ينشئ هيكلًا لكيان جديد تفاعليًا - -### مرجع CLI - -```bash filename="Terminal" -yarn twenty help # عرض جميع الأوامر -yarn twenty server start # بدء خادم التطوير المحلي -yarn twenty remote add # الاتصال بخادم Twenty -yarn twenty exec -n fn # تنفيذ دالة المنطق -yarn twenty logs -n fn # بث سجلات الدالة -``` - -اطّلع على دليل [البدء](/l/ar/developers/extend/apps/getting-started) للاطلاع على مرجع CLI الكامل. diff --git a/packages/twenty-docs/l/ar/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/ar/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4e4e8c86a3..0000000000 --- a/packages/twenty-docs/l/ar/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: إعدادات الإصدارات -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## ميزات المختبر - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. اذهب إلى **الإعدادات → الإصدارات** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/building.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/building.mdx deleted file mode 100644 index ebe4863060..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Architektura -description: Jak fungují aplikace Twenty — sandboxing, životní cyklus a stavební bloky. -icon: sitemap ---- - -Aplikace Twenty jsou balíčky TypeScriptu, které rozšiřují váš pracovní prostor o vlastní objekty, logiku, komponenty UI a funkce AI. Běží na platformě Twenty s plnou izolací (sandboxingem) a řízením oprávnění. - -## Jak aplikace fungují - -Aplikace je kolekce **entit** deklarovaných pomocí funkcí `defineEntity()` z balíčku `twenty-sdk`. SDK tyto deklarace detekuje pomocí analýzy AST při sestavení a vytváří **manifest** — úplný popis toho, co vaše aplikace přidává do pracovního prostoru. - -``` -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 -``` - - - **Uspořádání souborů je na vás.** Detekce entit je založená na AST — SDK najde volání `export default defineEntity(...)` bez ohledu na to, kde se soubor nachází. Výše uvedená struktura složek je konvence, nikoli požadavek. - - -## Typy entit - -| Entita | Účel | Dokumentace | -| ----------------------------- | ----------------------------------------------- | ------------------------------------------------------------------ | -| **Aplikace** | Identita aplikace, oprávnění, proměnné | [Datový model](/l/cs/developers/extend/apps/data-model) | -| **Role** | Sady oprávnění pro objekty a pole | [Datový model](/l/cs/developers/extend/apps/data-model) | -| **Objekt** | Vlastní datové tabulky s poli | [Datový model](/l/cs/developers/extend/apps/data-model) | -| **Pole** | Rozšíření existujících objektů, definice relací | [Datový model](/l/cs/developers/extend/apps/data-model) | -| **Logická funkce** | TypeScript na straně serveru se spouštěči | [Logické funkce](/l/cs/developers/extend/apps/logic-functions) | -| **Frontendová komponenta** | Izolované React UI na stránce Twenty | [Frontendové komponenty](/l/cs/developers/extend/apps/front-components) | -| **Dovednost** | Znovupoužitelné pokyny pro AI agenty | [Dovednosti a agenti](/l/cs/developers/extend/apps/skills-and-agents) | -| **Agent** | AI asistenti s vlastními prompty | [Dovednosti a agenti](/l/cs/developers/extend/apps/skills-and-agents) | -| **Pohled** | Předkonfigurovaná zobrazení seznamu záznamů | [Rozvržení](/l/cs/developers/extend/apps/layout) | -| **Položka navigační nabídky** | Vlastní položky postranního panelu | [Rozvržení](/l/cs/developers/extend/apps/layout) | -| **Rozvržení stránky** | Vlastní karty a widgety na stránce záznamu | [Rozvržení](/l/cs/developers/extend/apps/layout) | - -## Izolace (sandboxing) - -* **Logické funkce** běží v izolovaných procesech Node.js na serveru. K datům přistupují pouze prostřednictvím typovaného klienta API, a to v rozsahu oprávnění role aplikace. -* **Frontendové komponenty** běží ve Web Workerech s využitím Remote DOM — jsou oddělené od hlavní stránky, ale vykreslují nativní prvky DOM (nikoli iframy). Komunikují s Twenty prostřednictvím hostitelského API pro předávání zpráv. -* **Oprávnění** jsou vynucována na úrovni API. Běhový token (`TWENTY_APP_ACCESS_TOKEN`) je odvozen z role definované v `defineApplication()`. - -## Životní cyklus aplikace - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ Install flow │ -│ upload → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -├─────────────────────────────────────────────────────────┤ -│ Publish │ -│ npm publish → appears in Twenty marketplace │ -└─────────────────────────────────────────────────────────┘ -``` - -* **`yarn twenty dev`** — sleduje vaše zdrojové soubory a průběžně synchronizuje změny s připojeným serverem Twenty. Typovaný klient API se při změně schématu automaticky znovu vygeneruje. -* **`yarn twenty build`** — zkompiluje TypeScript, zabalí logické funkce a frontendové komponenty pomocí esbuild a vytvoří manifest. -* **Pre/post-install hooks** — volitelné logické funkce, které běží během instalace. Podrobnosti najdete v [Logických funkcích](/l/cs/developers/extend/apps/logic-functions). - -## Další kroky - - - - Definujte objekty, pole, role a relace. - - - Funkce na straně serveru s HTTP, cron a událostními spouštěči. - - - Izolované komponenty Reactu v uživatelském rozhraní Twenty. - - - Pohledy, položky navigace a rozvržení stránek záznamů. - - - AI dovednosti a agenti s vlastními prompty. - - - Příkazy CLI, testování, prostředky, vzdálené zdroje a CI. - - - Nasaďte na server nebo publikujte na tržišti. - - diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index 59c8aba17e..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI a testování -description: Příkazy CLI, nastavení testování, veřejné statické soubory, balíčky npm, vzdálené repozitáře a konfigurace CI. -icon: terminal ---- - -## Veřejné prostředky (složka `public/`) - -Složka `public/` v kořenu vaší aplikace obsahuje statické soubory — obrázky, ikony, písma a další prostředky, které vaše aplikace potřebuje za běhu. Tyto soubory jsou automaticky zahrnuty do buildů, synchronizovány během vývojového režimu a nahrávány na server. - -Soubory umístěné v `public/` jsou: - -* **Veřejně přístupné** — po synchronizaci na server jsou prostředky dostupné na veřejné URL. K přístupu k nim není potřeba žádná autentizace. -* **Dostupné ve frontendových komponentách** — použijte URL prostředků k zobrazení obrázků, ikon či jiných médií uvnitř komponent Reactu. -* **Dostupné v logických funkcích** — odkazujte na URL prostředků v e-mailech, odpovědích API či jiné serverové logice. -* **Používány pro metadata Marketplace** — pole `logoUrl` a `screenshots` v `defineApplication()` odkazují na soubory z této složky (např. `public/logo.png`). Tyto se zobrazují v Marketplace, když je vaše aplikace zveřejněna. -* **Automaticky synchronizované ve vývojovém režimu** — když v `public/` přidáte, aktualizujete nebo smažete soubor, je automaticky synchronizován na server. Není potřeba restartovat. -* **Zahrnuté do buildů** — `yarn twenty build` zabalí všechny veřejné prostředky do distribučního výstupu. - -### Přístup k veřejným prostředkům pomocí `getPublicAssetUrl` - -K získání plné URL souboru ve vaší složce `public/` použijte pomocnou funkci `getPublicAssetUrl` z `twenty-sdk`. Funguje jak v logických funkcích, tak ve frontendových komponentách. - -**V logické funkci:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, -}); -``` - -**Ve frontendové komponentě:** - -```tsx src/front-components/company-card.tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -Argument `path` je relativní ke složce `public/` vaší aplikace. Jak `getPublicAssetUrl('logo.png')`, tak `getPublicAssetUrl('public/logo.png')` se vyhodnotí na stejnou URL — předpona `public/` je, je-li přítomna, automaticky odstraněna. - -## Používání balíčků npm - -Ve své aplikaci můžete nainstalovat a používat libovolný balíček npm. Logické funkce i frontendové komponenty se bundlují pomocí [esbuild](https://esbuild.github.io/), který vloží všechny závislosti přímo do výstupu — za běhu nejsou potřeba žádné `node_modules`. - -### Instalace balíčku - -```bash filename="Terminal" -yarn add axios -``` - -Poté jej importujte ve svém kódu: - -```ts src/logic-functions/fetch-data.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import axios from 'axios'; - -const handler = async (): Promise => { - 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, -}); -``` - -Stejně to funguje i pro frontendové komponenty: - -```tsx src/front-components/chart.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { format } from 'date-fns'; - -const DateWidget = () => { - return

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### Jak funguje bundlování - -Krok sestavení používá esbuild k vytvoření jediného samostatného souboru pro každou logickou funkci a každou frontendovou komponentu. Všechny importované balíčky jsou vloženy přímo do bundlu. - -**Logické funkce** běží v prostředí Node.js. Vestavěné moduly Node (`fs`, `path`, `crypto`, `http` atd.) jsou k dispozici a není je třeba instalovat. - -**Frontendové komponenty** běží ve Web Workeru. Vestavěné moduly Node nejsou k dispozici — pouze prohlížečová API a balíčky npm, které fungují v prohlížečovém prostředí. - -V obou prostředích jsou jako předpřipravené moduly k dispozici `twenty-client-sdk/core` a `twenty-client-sdk/metadata` — nejsou součástí bundlu, ale server je za běhu načítá. - -## Testování vaší aplikace - -SDK poskytuje programová rozhraní, která vám umožní z testovacího kódu aplikaci sestavit, nasadit, nainstalovat a odinstalovat. V kombinaci s [Vitest](https://vitest.dev/) a typovanými klienty API můžete psát integrační testy, které ověří, že vaše aplikace funguje end-to-end proti reálnému serveru Twenty. - -### Nastavení - -Vygenerovaná aplikace již obsahuje Vitest. Pokud to nastavujete ručně, nainstalujte závislosti: - -```bash filename="Terminal" -yarn add -D vitest vite-tsconfig-paths -``` - -Vytvořte `vitest.config.ts` v kořeni vaší aplikace: - -```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', - }, - }, -}); -``` - -Vytvořte soubor nastavení, který před spuštěním testů ověří dostupnost serveru: - -```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), - ); -}); -``` - -### Programová rozhraní SDK - -Subcesta `twenty-sdk/cli` exportuje funkce, které můžete volat přímo z testovacího kódu: - -| Funkce | Popis | -| -------------- | ----------------------------------------------------- | -| `appBuild` | Sestaví aplikaci a volitelně zabalí tarball | -| `appDeploy` | Nahraje tarball na server | -| `appInstall` | Nainstaluje aplikaci do aktivního pracovního prostoru | -| `appUninstall` | Odinstaluje aplikaci z aktivního pracovního prostoru | - -Každá funkce vrací objekt výsledku se `success: boolean` a buď `data`, nebo `error`. - -### Psání integračního testu - -Zde je kompletní příklad, který aplikaci sestaví, nasadí a nainstaluje a poté ověří, že se objeví v pracovním prostoru: - -```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(); - }); -}); -``` - -### Spuštění testů - -Ujistěte se, že běží váš lokální server Twenty, a poté: - -```bash filename="Terminal" -yarn test -``` - -Nebo v režimu watch během vývoje: - -```bash filename="Terminal" -yarn test:watch -``` - -### Kontrola typů - -Kontrolu typů můžete spustit i na vaší aplikaci bez spuštění testů: - -```bash filename="Terminal" -yarn twenty typecheck -``` - -Spustí se `tsc --noEmit` a nahlásí se případné chyby typů. - -## Referenční dokumentace CLI - -Kromě `dev`, `build`, `add` a `typecheck` poskytuje CLI příkazy pro spouštění funkcí, zobrazení logů a správu instalací aplikací. - -### Spouštění funkcí (`yarn twenty exec`) - -Spusťte logickou funkci ručně bez vyvolání přes HTTP, cron nebo databázovou událost: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Zobrazení logů funkcí (`yarn twenty logs`) - -Streamujte výstupní logy běhu logických funkcí vaší aplikace: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -To je jiné než `yarn twenty server logs`, které zobrazují logy kontejneru Docker. `yarn twenty logs` zobrazuje logy spuštění funkcí vaší aplikace ze serveru Twenty. - - -### Odinstalace aplikace (`yarn twenty uninstall`) - -Odeberte svou aplikaci z aktivního pracovního prostoru: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## Správa vzdálených serverů - -**Remote** je server Twenty, ke kterému se vaše aplikace připojuje. Během nastavení jej generátor kostry automaticky vytvoří. Můžete kdykoli přidat další vzdálené servery nebo mezi nimi přepínat. - -```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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Vaše přihlašovací údaje jsou uloženy v `~/.twenty/config.json`. - -## CI s GitHub Actions - -Generátor kostry vytvoří připravený k použití workflow GitHub Actions v `.github/workflows/ci.yml`. Automaticky spouští integrační testy při každém pushi do `main` a u pull requestů. - -Workflow: - -1. Načte váš kód (checkout). -2. Spustí dočasný server Twenty pomocí akce `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Nainstaluje závislosti pomocí `yarn install --immutable` -4. Spustí `yarn test` s proměnnými `TWENTY_API_URL` a `TWENTY_API_KEY` vloženými z výstupů akce - -```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 }} -``` - -Není potřeba konfigurovat žádné secrets — akce `spawn-twenty-docker-image` spustí dočasný server Twenty přímo v runneru a vypíše podrobnosti připojení. Secret `GITHUB_TOKEN` je poskytován GitHubem automaticky. - -Chcete-li připnout konkrétní verzi Twenty místo `latest`, změňte proměnnou prostředí `TWENTY_VERSION` na začátku workflow. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/data-model.mdx deleted file mode 100644 index 65e77ffafd..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,494 +0,0 @@ ---- -title: Datový model -description: Definujte objekty, pole, role a metadata aplikace pomocí Twenty SDK. -icon: database ---- - -Balíček `twenty-sdk` poskytuje funkce `defineEntity` pro deklaraci datového modelu vaší aplikace. Abyste umožnili SDK detekovat vaše entity, musíte použít `export default defineEntity({...})`. Tyto funkce validují vaši konfiguraci v době sestavení a poskytují automatické doplňování v IDE a typovou bezpečnost. - - - **Uspořádání souborů je na vás.** - Detekce entit je založená na AST — SDK najde volání `export default defineEntity(...)` bez ohledu na to, kde se soubor nachází. Seskupování souborů podle typu (např. `logic-functions/`, `roles/`) je pouze konvence, nikoli požadavek. - - - - - -Role zapouzdřují oprávnění k objektům a akcím ve vašem pracovním prostoru. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -Každá aplikace musí mít právě jedno volání `defineApplication`, které popisuje: - -* **Identita**: identifikátory, zobrazovaný název a popis. -* **Oprávnění**: jakou roli používají její funkce a frontendové komponenty. -* **(Volitelné) proměnné**: dvojice klíč–hodnota zpřístupněné vašim funkcím jako proměnné prostředí. -* **(Volitelné) předinstalační / postinstalační funkce**: logické funkce, které se spouštějí před nebo po instalaci. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -export default defineApplication({ - universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Poznámky: -* Pole `universalIdentifier` jsou deterministické identifikátory, které vlastníte. Vygenerujte je jednou a zachovejte je stabilní napříč synchronizacemi. -* `applicationVariables` se stanou proměnnými prostředí pro vaše funkce a frontendové komponenty (například `DEFAULT_RECIPIENT_NAME` je dostupné jako `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` musí odkazovat na roli definovanou pomocí `defineRole()` (viz výše). -* Předinstalační a postinstalační funkce jsou při sestavení manifestu detekovány automaticky — není třeba na ně odkazovat v `defineApplication()`. - -#### Metadata tržiště - -Pokud plánujete [zveřejnit svou aplikaci](/l/cs/developers/extend/apps/publishing), tato volitelná pole určují, jak se vaše aplikace zobrazuje v tržišti: - -| Pole | Popis | -| ------------------ | ------------------------------------------------------------------------------------------------------------- | -| `author` | Jméno autora nebo název společnosti | -| `category` | Kategorie aplikace pro filtrování v tržišti | -| `logoUrl` | Cesta k logu vaší aplikace (např. `public/logo.png`) | -| `screenshots` | Pole cest ke snímkům obrazovky (např. `public/screenshot-1.png`) | -| `aboutDescription` | Delší popis v Markdownu pro kartu "O aplikaci". Pokud je vynecháno, tržiště použije `README.md` balíčku z npm | -| `websiteUrl` | Odkaz na váš web | -| `termsUrl` | Odkaz na podmínky služby | -| `emailSupport` | E-mailová adresa podpory | -| `issueReportUrl` | Odkaz na nástroj pro sledování problémů | - -#### Role a oprávnění - -Pole `defaultRoleUniversalIdentifier` v `application-config.ts` určuje výchozí roli používanou logickými funkcemi a frontendovými komponentami vaší aplikace. Podrobnosti viz výše u `defineRole`. - -* Běhový token vložený jako `TWENTY_APP_ACCESS_TOKEN` je odvozen z této role. -* Typovaný klient bude omezen oprávněními udělenými této roli. -* Dodržujte princip nejmenších oprávnění: vytvořte vyhrazenou roli pouze s oprávněními, která vaše funkce potřebují. - -##### Výchozí role funkce - -Když vygenerujete novou aplikaci, CLI vytvoří výchozí soubor role: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -Na `universalIdentifier` této role se v `application-config.ts` odkazuje jako na `defaultRoleUniversalIdentifier`: - -* **\*.role.ts** definuje, co daná role může dělat. -* **application-config.ts** ukazuje na tuto roli, aby vaše funkce zdědily její oprávnění. - -Poznámky: -* Začněte vygenerovanou rolí a postupně ji omezujte podle principu nejmenších oprávnění. -* Nahraďte `objectPermissions` a `fieldPermissions` objekty a poli, které vaše funkce skutečně potřebují. -* `permissionFlags` řídí přístup k schopnostem na úrovni platformy. Udržujte je co nejmenší. -* Podívejte se na funkční příklad: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Vlastní objekty popisují jak schéma, tak chování záznamů ve vašem pracovním prostoru. K definování objektů s vestavěnou validací použijte `defineObject()`: - -```ts postCard.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, - }, - ], -}); -``` - -Hlavní body: - -* Použijte `defineObject()` pro vestavěnou validaci a lepší podporu v IDE. -* Hodnota `universalIdentifier` musí být jedinečná a stabilní napříč nasazeními. -* Každé pole vyžaduje `name`, `type`, `label` a svůj vlastní stabilní `universalIdentifier`. -* Pole `fields` je volitelné — objekty můžete definovat i bez vlastních polí. -* Nové objekty můžete vygenerovat pomocí `yarn twenty add`, který vás provede pojmenováním, poli a vztahy. - - -**Základní pole jsou vytvořena automaticky.** Když definujete vlastní objekt, Twenty automaticky přidá standardní pole -jako `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` a `deletedAt`. -Nemusíte je definovat v poli `fields` — přidejte pouze svá vlastní pole. -Výchozí pole můžete přepsat definováním pole se stejným názvem v poli `fields`, -ale to se nedoporučuje. - - - - - -Pomocí `defineField()` přidejte pole k objektům, které nevlastníte — například ke standardním objektům Twenty (Person, Company atd.). nebo k objektům z jiných aplikací. Na rozdíl od inline polí v `defineObject()` vyžadují samostatná pole `objectUniversalIdentifier` k určení, který objekt rozšiřují: - -```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' }, - ], -}); -``` - -Hlavní body: -* `objectUniversalIdentifier` identifikuje cílový objekt. Pro standardní objekty použijte `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` exportovaný z `twenty-sdk`. -* Při definování polí inline v `defineObject()` `objectUniversalIdentifier` nepotřebujete — dědí se z nadřazeného objektu. -* `defineField()` je jediný způsob, jak přidat pole k objektům, které jste nevytvořili pomocí `defineObject()`. - - - - -Relace propojují objekty. Ve Twenty jsou relace vždy obousměrné — definujete obě strany a každá strana odkazuje na tu druhou. - -Existují dva typy relací: - -| Typ vztahu | Popis | Má cizí klíč? | -| ------------- | --------------------------------------------------------------------- | ---------------------- | -| `MANY_TO_ONE` | Mnoho záznamů tohoto objektu ukazuje na jeden záznam cílového objektu | Ano (`joinColumnName`) | -| `ONE_TO_MANY` | Jeden záznam tohoto objektu má mnoho záznamů cílového objektu | Ne (inverzní strana) | - -#### Jak fungují relace - -Každá relace vyžaduje dvě pole, která na sebe vzájemně odkazují: - -1. Strana MANY_TO_ONE — je na objektu, který drží cizí klíč -2. Strana ONE_TO_MANY — je na objektu, který vlastní kolekci - -Obě pole používají `FieldType.RELATION` a vzájemně se odkazují prostřednictvím `relationTargetFieldMetadataUniversalIdentifier`. - -#### Příklad: Pohlednice má mnoho příjemců - -Předpokládejme, že `PostCard` lze odeslat mnoha záznamům `PostCardRecipient`. Každý příjemce náleží přesně jedné pohlednici. - -**Krok 1: Definujte stranu ONE_TO_MANY na PostCard** (strana "one"): - -```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, - }, -}); -``` - -**Krok 2: Definujte stranu MANY_TO_ONE na PostCardRecipient** (strana "many" — drží cizí klíč): - -```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', - }, -}); -``` - - -**Cyklické importy:** Obě relační pole odkazují na `universalIdentifier` toho druhého. Abyste předešli problémům s cyklickými importy, exportujte ID polí jako pojmenované konstanty z každého souboru a v druhém souboru je importujte. Build systém je vyřeší v době kompilace. - - -#### Vazby na standardní objekty - -Chcete-li vytvořit relaci s vestavěným objektem Twenty (Person, Company atd.), použijte `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', - }, -}); -``` - -#### Vlastnosti relačních polí - -| Vlastnost | Povinné | Popis | -| ------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------- | -| `type` | Ano | Musí být `FieldType.RELATION` | -| `relationTargetObjectMetadataUniversalIdentifier` | Ano | `universalIdentifier` cílového objektu | -| `relationTargetFieldMetadataUniversalIdentifier` | Ano | `universalIdentifier` odpovídajícího pole na cílovém objektu | -| `universalSettings.relationType` | Ano | `RelationType.MANY_TO_ONE` nebo `RelationType.ONE_TO_MANY` | -| `universalSettings.onDelete` | Pouze MANY_TO_ONE | Co se stane, když je smazán odkazovaný záznam: `CASCADE`, `SET_NULL`, `RESTRICT` nebo `NO_ACTION` | -| `universalSettings.joinColumnName` | Pouze MANY_TO_ONE | Název databázového sloupce pro cizí klíč (např. `postCardId`) | - -#### Vložená relační pole v defineObject - -Relační pole můžete také definovat přímo uvnitř `defineObject()`. V takovém případě vynechejte `objectUniversalIdentifier` — dědí se z nadřazeného objektu: - -```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 - ], -}); -``` - - - -## Generování entit pomocí `yarn twenty add` - -Místo ručního vytváření souborů entit můžete použít interaktivní generátor: - -```bash filename="Terminal" -yarn twenty add -``` - -Požádá vás o výběr typu entity a provede vás požadovanými poli. Vygeneruje soubor připravený k použití se stabilním `universalIdentifier` a správným voláním `defineEntity()`. - -Můžete také předat typ entity přímo a přeskočit první dotaz: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Dostupné typy entit - -| Typ entity | Příkaz | Vygenerovaný soubor | -| ------------------------- | ------------------------------------ | ------------------------------------------------------- | -| Objekt | `yarn twenty add object` | `src/objects/\.ts` | -| Pole | `yarn twenty add field` | `src/fields/\.ts` | -| Logická funkce | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Frontendová komponenta | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Role | `yarn twenty add role` | `src/roles/\.ts` | -| Dovednost | `yarn twenty add skill` | `src/skills/\.ts` | -| Agent | `yarn twenty add agent` | `src/agents/\.ts` | -| Pohled | `yarn twenty add view` | `src/views/\.ts` | -| Položka navigační nabídky | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Rozvržení stránky | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### Co generátor vytváří - -Každý typ entity má vlastní šablonu. Například `yarn twenty add object` se zeptá na: - -1. **Název (jednotné číslo)** — např. `invoice` -2. **Název (množné číslo)** — např. `invoices` -3. **Štítek (jednotné číslo)** — automaticky doplněn z názvu (např. `Invoice`) -4. **Štítek (množné číslo)** — automaticky doplněn (např. `Invoices`) -5. **Vytvořit zobrazení a položku navigace?** — pokud odpovíte ano, generátor také vytvoří odpovídající zobrazení a odkaz v postranním panelu pro nový objekt. - -Ostatní typy entit mají jednodušší dotazy — většinou se ptají pouze na název. - -Typ entity `field` je podrobnější: ptá se na název pole, štítek, typ (ze seznamu všech dostupných typů polí jako `TEXT`, `NUMBER`, `SELECT`, `RELATION` atd.) a `universalIdentifier` cílového objektu. - -### Vlastní výstupní cesta - -Pomocí příznaku `--path` umístíte vygenerovaný soubor do vlastního umístění: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/front-components.mdx deleted file mode 100644 index e98773a276..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Frontendové komponenty -description: Vytvářejte komponenty Reactu, které se vykreslují uvnitř uživatelského rozhraní Twenty se sandboxovou izolací. -icon: window-maximize ---- - -Frontendové komponenty jsou React komponenty, které se vykreslují přímo v uživatelském rozhraní Twenty. Běží v **izolovaném Web Workeru** s využitím Remote DOM — váš kód je sandboxovaný, ale vykresluje se nativně na stránce, nikoli v iframu. - -## Kde lze použít frontendové komponenty - -Frontendové komponenty se mohou vykreslovat na dvou místech v rámci Twenty: - -* **Postranní panel** — Frontendové komponenty, které nejsou headless, se otevírají v pravém postranním panelu. Toto je výchozí chování, když je frontendová komponenta vyvolána z příkazového menu. -* **Widgety (nástěnky a stránky záznamů)** — Frontendové komponenty lze vkládat jako widgety do rozložení stránek. Při konfiguraci nástěnky nebo rozložení stránky záznamu mohou uživatelé přidat widget frontendové komponenty. - -## Základní příklad - -Nejrychlejší způsob, jak vidět frontendovou komponentu v akci, je zaregistrovat ji jako **příkaz**. Přidáním pole `command` s `isPinned: true` se zobrazí jako tlačítko rychlé akce v pravém horním rohu stránky — není potřeba žádné rozvržení stránky: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', - name: 'hello-world', - description: 'A simple front component', - component: HelloWorld, - command: { - universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345', - shortLabel: 'Hello', - label: 'Hello World', - icon: 'IconBolt', - isPinned: true, - availabilityType: 'GLOBAL', - }, -}); -``` - -Po synchronizaci pomocí `yarn twenty dev` (nebo po jednorázovém spuštění `yarn twenty dev --once`) se rychlá akce zobrazí v pravém horním rohu stránky: - -
- Tlačítko rychlé akce v pravém horním rohu -
- -Kliknutím na něj vykreslíte komponentu přímo ve stránce. - -## Konfigurační pole - -| Pole | Povinné | Popis | -| --------------------- | ------- | ------------------------------------------------------------------------------------ | -| `universalIdentifier` | Ano | Stabilní jedinečné ID pro tuto komponentu | -| `component` | Ano | Funkce komponenty React | -| `name` | Ne | Zobrazovaný název | -| `description` | Ne | Popis toho, co komponenta dělá | -| `isHeadless` | Ne | Nastavte na `true`, pokud komponenta nemá viditelné UI (viz níže) | -| `command` | Ne | Zaregistrujte komponentu jako příkaz (viz [možnosti příkazu](#command-options) níže) | - -## Umístění frontendové komponenty na stránku - -Mimo příkazy můžete frontendovou komponentu vložit přímo na stránku záznamu přidáním jako widget v **rozvržení stránky**. Podrobnosti viz sekce [definePageLayout](/l/cs/developers/extend/apps/skills-and-agents#definepagelayout). - -## Headless vs. ne-headless - -Front-endové komponenty existují ve dvou režimech vykreslování řízených volbou `isHeadless`: - -**Ne-headless (výchozí)** — Komponenta vykreslí viditelné uživatelské rozhraní. Po vyvolání z menu příkazů se otevře v postranním panelu. Toto je výchozí chování, když je `isHeadless` `false` nebo když tato volba není uvedena. - -**Headless (`isHeadless: true`)** — Komponenta se neviditelně inicializuje na pozadí. Neotevírá postranní panel. Headless komponenty jsou určené pro akce, které provedou logiku a poté se odpojí — například spuštění asynchronního úkolu, navigaci na stránku nebo zobrazení potvrzovacího modálního okna. Přirozeně se hodí ke komponentám SDK Command popsaným níže. - -```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, -}); -``` - -Protože komponenta vrací `null`, Twenty přeskočí vykreslení kontejneru — v rozvržení se neobjeví žádné prázdné místo. Komponenta má však stále přístup ke všem hookům a API komunikace s hostitelem. - -## Komponenty SDK Command - -Balíček `twenty-sdk` poskytuje čtyři pomocné komponenty Command navržené pro headless front-endové komponenty. Každá komponenta při připojení provede akci, chyby zpracuje zobrazením oznámení ve snackbaru a po dokončení automaticky odpojí front-endovou komponentu. - -Importujte je z `twenty-sdk/command`: - -* **`Command`** — Spustí asynchronní callback přes prop `execute`. -* **`CommandLink`** — Naviguje na cestu v aplikaci. Props: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — Otevře potvrzovací modální okno. Pokud uživatel potvrdí, provede callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — Otevře konkrétní stránku postranního panelu. Props: `page`, `pageTitle`, `pageIcon`. - -Zde je kompletní příklad headless front-endové komponenty, která pomocí `Command` spouští akci z menu příkazů: - -```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 ; -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', - name: 'run-action', - description: 'Creates a task from the command menu', - component: RunAction, - isHeadless: true, - command: { - universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', - label: 'Run my action', - icon: 'IconPlayerPlay', - }, -}); -``` - -A příklad s použitím `CommandModal` k vyžádání potvrzení před provedením: - -```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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, - command: { - universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567', - label: 'Delete draft', - icon: 'IconTrash', - }, -}); -``` - -## Přístup k běhovému kontextu - -Uvnitř komponenty použijte hooky SDK pro přístup k aktuálnímu uživateli, záznamu a instanci komponenty: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Dostupné hooky: - -| Hook | Vrací | Popis | -| --------------------------------------------- | -------------------- | ------------------------------------------------------------ | -| `useUserId()` | `string` nebo `null` | ID aktuálního uživatele | -| `useRecordId()` | `string` nebo `null` | ID aktuálního záznamu (pokud je umístěna na stránce záznamu) | -| `useFrontComponentId()` | `string` | ID této instance komponenty | -| `useFrontComponentExecutionContext(selector)` | různé | Přístup k úplnému kontextu běhu pomocí selektorové funkce | - -## API komunikace s hostitelem - -Frontendové komponenty mohou pomocí funkcí z `twenty-sdk` vyvolávat navigaci, modály a oznámení: - -| Funkce | Popis | -| ----------------------------------------------- | ------------------------------ | -| `navigate(to, params?, queryParams?, options?)` | Přejít na stránku v aplikaci | -| `openSidePanelPage(params)` | Otevřít postranní panel | -| `closeSidePanel()` | Zavřít postranní panel | -| `openCommandConfirmationModal(params)` | Zobrazit potvrzovací dialog | -| `enqueueSnackbar(params)` | Zobrazit oznámení typu toast | -| `unmountFrontComponent()` | Odpojit komponentu | -| `updateProgress(progress)` | Aktualizovat indikátor průběhu | - -Zde je příklad, který používá hostitelské API k zobrazení snackbaru a zavření postranního panelu po dokončení akce: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -## Možnosti příkazu - -Přidání pole `command` do `defineFrontComponent` zaregistruje komponentu v příkazovém menu (Cmd+K). Pokud je `isPinned` nastaveno na `true`, zobrazí se také jako tlačítko rychlé akce v pravém horním rohu stránky. - -| Pole | Povinné | Popis | -| --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Ano | Stabilní jedinečné ID pro příkaz | -| `label` | Ano | Plný popisek zobrazený v příkazovém menu (Cmd+K) | -| `shortLabel` | Ne | Kratší popisek zobrazený na připnutém tlačítku rychlé akce | -| `icon` | Ne | Název ikony zobrazený vedle popisku (např. `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Ne | Pokud je `true`, zobrazí příkaz jako tlačítko rychlé akce v pravém horním rohu stránky | -| `availabilityType` | Ne | Určuje, kde se příkaz zobrazuje: `'GLOBAL'` (vždy dostupné), `'RECORD_SELECTION'` (pouze když jsou vybrány záznamy) nebo `'FALLBACK'` (zobrazeno, když neodpovídají žádné jiné příkazy) | -| `availabilityObjectUniversalIdentifier` | Ne | Omezí příkaz na stránky konkrétního typu objektu (např. pouze u záznamů Company) | -| `conditionalAvailabilityExpression` | Ne | Logický výraz pro dynamické řízení, zda je příkaz viditelný (viz níže) | - -## Výrazy podmíněné dostupnosti - -Pole `conditionalAvailabilityExpression` vám umožní řídit viditelnost příkazu na základě aktuálního kontextu stránky. Pro sestavení výrazů importujte typované proměnné a operátory z `twenty-sdk`: - -```tsx -import { - defineFrontComponent, - pageType, - numberOfSelectedRecords, - objectPermissions, - everyEquals, - isDefined, -} from 'twenty-sdk/define'; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'bulk-action', - component: BulkAction, - command: { - universalIdentifier: '...', - label: 'Bulk Update', - availabilityType: 'RECORD_SELECTION', - conditionalAvailabilityExpression: everyEquals( - objectPermissions, - 'canUpdateObjectRecords', - true, - ), - }, -}); -``` - -**Kontextové proměnné** — reprezentují aktuální stav stránky: - -| Proměnná | Typ | Popis | -| ------------------------------ | --------- | -------------------------------------------------------------------- | -| `pageType` | `string` | Aktuální typ stránky (např. `'RecordIndexPage'`, `'RecordShowPage'`) | -| `isInSidePanel` | `boolean` | Zda je komponenta vykreslena v postranním panelu | -| `numberOfSelectedRecords` | `number` | Počet aktuálně vybraných záznamů | -| `isSelectAll` | `boolean` | Zda je aktivní "vybrat vše" | -| `selectedRecords` | `array` | Vybrané objekty záznamů | -| `favoriteRecordIds` | `array` | ID oblíbených záznamů | -| `objectPermissions` | `object` | Oprávnění pro aktuální typ objektu | -| `targetObjectReadPermissions` | `object` | Oprávnění ke čtení pro cílový objekt | -| `targetObjectWritePermissions` | `object` | Oprávnění k zápisu pro cílový objekt | -| `featureFlags` | `object` | Aktivní příznaky funkcí | -| `objectMetadataItem` | `object` | Metadata aktuálního typu objektu | -| `hasAnySoftDeleteFilterOnView` | `boolean` | Zda má aktuální zobrazení filtr soft-delete | - -**Operátory** — kombinují proměnné do logických výrazů: - -| Operátor | Popis | -| ----------------------------------- | ------------------------------------------------------------------------------ | -| `isDefined(value)` | `true`, pokud hodnota není null/undefined | -| `isNonEmptyString(value)` | `true`, pokud je hodnota neprázdný řetězec | -| `includes(array, value)` | `true`, pokud pole obsahuje danou hodnotu | -| `includesEvery(array, prop, value)` | `true`, pokud vlastnost každé položky zahrnuje danou hodnotu | -| `every(array, prop)` | `true`, pokud je vlastnost u každé položky pravdivá (truthy) | -| `everyDefined(array, prop)` | `true`, pokud je vlastnost definována u každé položky | -| `everyEquals(array, prop, value)` | `true`, pokud se vlastnost rovná hodnotě u každé položky | -| `some(array, prop)` | `true`, pokud je vlastnost pravdivá (truthy) alespoň u jedné položky | -| `someDefined(array, prop)` | `true`, pokud je vlastnost definována alespoň u jedné položky | -| `someEquals(array, prop, value)` | `true`, pokud se vlastnost rovná hodnotě alespoň u jedné položky | -| `someNonEmptyString(array, prop)` | `true`, pokud má vlastnost alespoň u jedné položky hodnotu neprázdného řetězce | -| `none(array, prop)` | `true`, pokud je vlastnost u všech položek nepravdivá (falsy) | -| `noneDefined(array, prop)` | `true`, pokud je vlastnost u všech položek nedefinovaná | -| `noneEquals(array, prop, value)` | `true`, pokud se vlastnost nerovná hodnotě u žádné položky | - -## Veřejné soubory - -Frontendové komponenty mohou přistupovat k souborům ze složky aplikace `public/` pomocí `getPublicAssetUrl`: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -Podrobnosti viz [sekci veřejných souborů](/l/cs/developers/extend/apps/cli-and-testing#public-assets-public-folder). - -## Styling - -Frontendové komponenty podporují více přístupů ke stylování. Můžete použít: - -* **Inline styly** — `style={{ color: 'red' }}` -* **Komponenty Twenty UI** — import z `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar a další) -* **Emotion** — CSS-in-JS s `@emotion/react` -* **Styled-components** — vzory `styled.div` -* **Tailwind CSS** — utilitní třídy -* **Jakákoli CSS-in-JS knihovna** kompatibilní s Reactem - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started.mdx deleted file mode 100644 index b513fd1a24..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,282 +0,0 @@ ---- -title: Začínáme -icon: rocket -description: Vytvořte svou první aplikaci Twenty během několika minut. ---- - -## Co jsou aplikace? - -Aplikace vám umožňují rozšířit Twenty o vlastní objekty, pole, logické funkce, frontendové komponenty, AI schopnosti a další — vše je spravováno jako kód. Místo konfigurace všeho přes uživatelské rozhraní definujete v TypeScriptu svůj datový model a logiku a nasadíte je do jednoho nebo více pracovních prostorů. - -## Předpoklady - -Než začnete, ujistěte se, že máte ve svém počítači nainstalováno následující: - -* **Node.js 24+** — [Stáhnout zde](https://nodejs.org/) -* **Yarn 4** — Dodává se s Node.js prostřednictvím Corepacku. Povolte jej spuštěním `corepack enable` -* **Docker** — [Stáhnout zde](https://www.docker.com/products/docker-desktop/). Nutné pro spuštění lokální instance Twenty. Není potřeba, pokud už máte spuštěný server Twenty. - -## Vytvořte svou první aplikaci - -### Vytvořte kostru své aplikace - -Otevřete terminál a spusťte: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Budete vyzváni k zadání názvu a popisu své aplikace. Stisknutím **Enter** přijmete výchozí hodnoty. - -Tím se vytvoří nová složka s názvem `my-twenty-app` se vším potřebným. - -### Nastavte lokální instanci Twenty - -Generátor kostry se zeptá: - -> **Chcete nastavit lokální instanci Twenty?** - -* **Zadejte `yes`** (doporučeno) — Stáhne image Dockeru `twenty-app-dev` a spustí lokální server Twenty na portu `2020`. Než budete pokračovat, ujistěte se, že Docker běží. -* **Zadejte `no`** — Zvolte, pokud už máte lokálně spuštěný server Twenty. - -
- Spustit lokální instanci? -
- -### Přihlaste se do svého pracovního prostoru - -Poté se otevře okno prohlížeče se stránkou přihlášení do Twenty. Přihlaste se předpřipraveným demo účtem: - -* **E-mail:** `tim@apple.dev` -* **Heslo:** `tim@apple.dev` - -
- Přihlašovací obrazovka Twenty -
- -### Autorizujte aplikaci - -Po přihlášení uvidíte autorizační obrazovku. Tím umožníte vaší aplikaci pracovat s vaším pracovním prostorem. - -Pokračujte kliknutím na **Authorize**. - -
- Autorizační obrazovka Twenty CLI -
- -Po autorizaci váš terminál potvrdí, že je vše nastaveno. - -
- Aplikace byla úspěšně vygenerována -
- -### Začněte vyvíjet - -Přejděte do nové složky aplikace a spusťte vývojový server: - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Sleduje zdrojové soubory, při každé změně znovu sestaví a automaticky synchronizuje vaši aplikaci s lokálním serverem Twenty. V terminálu byste měli vidět panel se stavem v reálném čase. - -Pro podrobnější výstup (protokoly sestavení, požadavky na synchronizaci, stopy chyb) použijte přepínač `--verbose`: - -```bash filename="Terminal" -yarn twenty dev --verbose -``` - - -Vývojový režim je k dispozici pouze na instancích Twenty běžících v režimu development (`NODE_ENV=development`). Produkční instance odmítají požadavky na vývojovou synchronizaci. Pro nasazení na produkční servery použijte `yarn twenty deploy` — podrobnosti viz [Publikování aplikací](/l/cs/developers/extend/apps/publishing). - - -
- Výstup terminálu ve vývojovém režimu -
- -#### Jednorázová synchronizace pomocí `yarn twenty dev --once` - -Pokud nechcete, aby na pozadí běžel watcher (například v CI pipeline, git hooku nebo skriptovaném workflow), použijte příznak `--once`. Spouští stejnou pipeline jako `yarn twenty dev` — sestaví manifest, zabalí soubory, nahraje, synchronizuje, znovu vygeneruje typovaného klienta API — ale **ukončí se, jakmile se synchronizace dokončí**: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Příkaz | Chování | Kdy použít | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| `yarn twenty dev` | Sleduje vaše zdrojové soubory a při každé změně znovu spustí synchronizaci. Zůstává spuštěný, dokud jej nezastavíte. | Interaktivní lokální vývoj — chcete živý panel stavu a okamžitou zpětnovazebnou smyčku. | -| `yarn twenty dev --once` | Provede jedno sestavení + synchronizaci, poté ukončí běh s kódem `0` při úspěchu nebo `1` při neúspěchu. | Skripty, CI, pre-commit hooky, AI agenti a jakýkoli neinteraktivní pracovní postup. | - -Oba režimy vyžadují server Twenty běžící v režimu vývoje a autentizovaný remote — platí stejné požadavky. - -### Zobrazte svou aplikaci v Twenty - -Otevřete ve svém prohlížeči [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Přejděte do **Settings > Apps** a vyberte kartu **Developer**. Vaše aplikace by měla být uvedena v části **Your Apps**: - -
- Seznam Your Apps zobrazující My twenty app -
- -Klikněte na **My twenty app** a otevřete její **registraci aplikace**. Registrace je záznam na úrovni serveru, který popisuje vaši aplikaci — její název, jedinečný identifikátor, přihlašovací údaje OAuth a zdroj (lokální, npm nebo tarball). Existuje na serveru, ne uvnitř žádného konkrétního pracovního prostoru. Když nainstalujete aplikaci do pracovního prostoru, Twenty vytvoří **aplikaci** v rozsahu pracovního prostoru, která odkazuje zpět na tuto registraci. Jedna registrace může být nainstalována ve více pracovních prostorech na stejném serveru. - -
- Podrobnosti registrace aplikace -
- -Klikněte na **View installed app**, abyste zobrazili nainstalovanou aplikaci. Karta **About** zobrazuje aktuální verzi a možnosti správy: - -
- Nainstalovaná aplikace — karta About -
- -Přepněte na kartu **Content**, abyste viděli vše, co vaše aplikace poskytuje — objekty, pole, logické funkce a agenty: - -
- Nainstalovaná aplikace — karta Content -
- -Vše je připraveno! Upravte libovolný soubor v `src/` a změny se automaticky projeví. - ---- - -## Co můžete vytvořit - -Aplikace se skládají z **entit** — každá je definována jako soubor TypeScriptu s jediným `export default`: - -| Entita | K čemu slouží | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| **Objekty a pole** | Definujte vlastní datové modely (např. Post Card, Invoice) s typovanými poli | -| **Logické funkce** | Serverové funkce v TypeScriptu spouštěné HTTP trasami, plánovačem cron nebo událostmi databáze | -| **Frontendové komponenty** | Komponenty Reactu, které se vykreslují v uživatelském rozhraní Twenty (postranní panel, widgety, příkazová nabídka) | -| **Dovednosti a agenti** | Schopnosti AI — opakovaně použitelné pokyny a autonomní asistenti | -| **Pohledy a navigace** | Předkonfigurované seznamové pohledy a položky postranní nabídky pro vaše objekty | -| **Rozvržení stránek** | Vlastní stránky detailu záznamu s kartami a widgety | - -Přejděte na [Tvorba aplikací](/l/cs/developers/extend/apps/building) pro podrobný průvodce každým typem entity. - ---- - -## Struktura projektu - -Nástroj pro vytvoření kostry vygeneruje následující strukturu souborů: - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .oxlintrc.json - tsconfig.json - tsconfig.spec.json # TypeScript config for tests - vitest.config.ts # Vitest test runner configuration - LLMS.md - README.md - .github/ - └── workflows/ - └── ci.yml # GitHub Actions CI workflow - public/ # Public assets (images, fonts, etc.) - src/ - ├── application-config.ts # Required — main application configuration - ├── default-role.ts # Default role for logic functions - ├── constants/ - │ └── universal-identifiers.ts # Auto-generated UUIDs and app metadata - └── __tests__/ - ├── setup-test.ts # Test setup (server health check, config) - └── app-install.integration-test.ts # Integration test -``` - -### Začínáme s příkladem - -Chcete-li začít s úplnějším příkladem s vlastními objekty, poli, logickými funkcemi, frontendovými komponentami a dalšími, použijte přepínač `--example`: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Příklady pocházejí z adresáře [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) na GitHubu. Můžete také vytvořit kostru jednotlivých entit v existujícím projektu pomocí `yarn twenty add` (viz [Tvorba aplikací](/l/cs/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add)). - -### Klíčové soubory - -| Soubor / Složka | Účel | -| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `package.json` | Definuje název, verzi a závislosti vaší aplikace. Obsahuje skript `twenty`, takže můžete spustit `yarn twenty help` a zobrazit všechny příkazy. | -| `src/application-config.ts` | **Povinné.** Hlavní konfigurační soubor vaší aplikace. | -| `src/default-role.ts` | Výchozí role, která určuje, k čemu mají vaše logické funkce přístup. | -| `src/constants/universal-identifiers.ts` | Automaticky generovaná UUID a metadata aplikace (zobrazovaný název, popis). | -| `src/__tests__/` | Integrační testy (nastavení + ukázkový test). | -| `public/` | Statická aktiva (obrázky, písma) poskytovaná s vaší aplikací. | - -## Lokální vývojový server - -Nástroj pro vytvoření kostry vám již spustil lokální server Twenty. Pro jeho správu později použijte `yarn twenty server`: - -| Příkaz | Popis | -| -------------------------------------- | ------------------------------------------------------ | -| `yarn twenty server start` | Spustí lokální server (v případě potřeby stáhne image) | -| `yarn twenty server start --port 3030` | Spustí na vlastním portu | -| `yarn twenty server start --test` | Spusťte samostatnou testovací instanci na portu 2021 | -| `yarn twenty server stop` | Zastaví server (zachová data) | -| `yarn twenty server status` | Zobrazí stav serveru, URL a přihlašovací údaje | -| `yarn twenty server logs` | Streamuje protokoly serveru | -| `yarn twenty server logs --lines 100` | Zobrazí posledních 100 řádků logu | -| `yarn twenty server reset` | Smaže všechna data a začne znovu | - -Data přetrvávají při restartech ve dvou svazcích Dockeru (`twenty-app-dev-data` pro PostgreSQL, `twenty-app-dev-storage` pro soubory). Pomocí `reset` vymažte vše a začněte znovu. - -### Spuštění testovací instance - -Předejte volbu `--test` libovolnému příkazu `server` pro správu druhé, plně izolované instance — užitečné pro spouštění integračních testů nebo experimentování, aniž byste se dotkli svých hlavních vývojových dat. - -| Příkaz | Popis | -| ---------------------------------- | --------------------------------------------------------- | -| `yarn twenty server start --test` | Spustí testovací instanci (výchozí port je 2021) | -| `yarn twenty server stop --test` | Zastaví testovací instanci | -| `yarn twenty server status --test` | Zobrazí stav testovací instance, URL a přihlašovací údaje | -| `yarn twenty server logs --test` | Streamuje protokoly testovací instance | -| `yarn twenty server reset --test` | Vymaže testovací data a začne znovu | - -Testovací instance běží ve vlastním kontejneru Docker (`twenty-app-dev-test`) s vyhrazenými svazky (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) a konfigurací, takže může běžet paralelně s vaší hlavní instancí bez konfliktů. Zkombinujte `--test` s `--port` pro změnu výchozího portu 2021. - - - Server vyžaduje, aby **Docker** běžel. Pokud vidíte chybu "Docker not running", ujistěte se, že je spuštěný Docker Desktop (nebo démon Dockeru). - - -## Ruční nastavení (bez scaffolderu) - -Pokud dáváte přednost vlastnímu nastavení místo použití `create-twenty-app`, můžete to udělat ve dvou krocích. - -**1. Přidejte `twenty-sdk` a `twenty-client-sdk` jako závislosti:** - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -**2. Přidejte skript `twenty` do svého `package.json`:** - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -Nyní můžete spouštět `yarn twenty dev`, `yarn twenty help` a všechny ostatní příkazy. - - -Neinstalujte `twenty-sdk` globálně. Vždy jej používejte jako lokální závislost projektu, aby si každý projekt mohl připnout svou vlastní verzi. - - -## Řešení potíží - -Pokud narazíte na potíže: - -* Před spuštěním generátoru kostry s lokální instancí se ujistěte, že **Docker běží**. -* Ujistěte se, že používáte **Node.js 24+** (ověříte příkazem `node -v`). -* Ujistěte se, že je **Corepack povolen** (`corepack enable`), aby byl k dispozici Yarn 4. -* Zkuste smazat `node_modules` a znovu spustit `yarn install`, pokud se zdají závislosti poškozené. - -Pořád se nedaří? Požádejte o pomoc na [Discordu Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout.mdx deleted file mode 100644 index 2420511d03..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Rozvržení -description: Definujte pohledy, položky navigační nabídky a rozvržení stránek, abyste utvářeli, jak se vaše aplikace zobrazuje v Twenty. -icon: table-columns ---- - -Prvky rozvržení řídí, jak se vaše aplikace zobrazuje v uživatelském rozhraní Twenty — co je v postranním panelu, které uložené pohledy jsou součástí aplikace a jak je uspořádána stránka s podrobnostmi záznamu. - -## Pojmy rozvržení - -| Pojem | Co řídí | Entita | -| ----------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------- | -| **Pohled** | Uložené nastavení seznamu pro objekt — viditelná pole, pořadí, filtry, skupiny | `defineView` | -| **Položka navigační nabídky** | Položka v levém postranním panelu, která odkazuje na pohled nebo externí URL | `defineNavigationMenuItem` | -| **Rozvržení stránky** | Karty a widgety, které tvoří stránku s podrobnostmi záznamu | `definePageLayout` | -| **Karta Rozložení stránky** | Samostatná karta připojená k existujícímu rozložení stránky (standardnímu nebo rozložení vaší vlastní aplikace) | `definePageLayoutTab` | - -Pohledy, položky navigační nabídky a rozvržení stránek se na sebe odkazují pomocí `universalIdentifier`: - -* Položka **navigační nabídky** typu `VIEW` odkazuje na identifikátor `defineView`, takže odkaz v postranním panelu otevře daný uložený pohled. -* **Rozvržení stránky** typu `RECORD_PAGE` cílí na objekt a může vkládat [front components](/l/cs/developers/extend/apps/front-components) do svých karet jako widgety. - - - - -Zobrazení jsou uložené konfigurace toho, jak se zobrazují záznamy objektu — včetně toho, která pole jsou viditelná, jejich pořadí a jaké filtry či seskupení jsou použity. Pomocí `defineView()` můžete k aplikaci přidat předkonfigurovaná zobrazení: - -```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, - }, - ], -}); -``` - -Hlavní body: -* `objectUniversalIdentifier` určuje, na který objekt se toto zobrazení vztahuje. -* `key` určuje typ zobrazení (např. `ViewKey.INDEX` pro hlavní seznam). -* `fields` určuje, které sloupce se zobrazí a v jakém pořadí. Každé pole odkazuje na `fieldMetadataUniversalIdentifier`. -* Pro pokročilejší konfigurace můžete definovat také `filters`, `filterGroups`, `groups` a `fieldGroups`. -* `position` určuje pořadí, pokud pro stejný objekt existuje více zobrazení. - - - - -Položky navigační nabídky přidávají vlastní položky do postranního panelu pracovního prostoru. Použijte `defineNavigationMenuItem()` k odkazování na zobrazení, externí URL nebo objekty: - -```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, -}); -``` - -Hlavní body: -* `type` určuje, na co položka menu odkazuje: `NavigationMenuItemType.VIEW` pro uložené zobrazení nebo `NavigationMenuItemType.LINK` pro externí URL. -* Pro odkazy na zobrazení nastavte `viewUniversalIdentifier`. Pro externí odkazy nastavte `link`. -* `position` určuje pořadí v postranním panelu. -* `icon` a `color` (volitelné) upravují vzhled. - - - - -Rozvržení stránek vám umožní přizpůsobit vzhled stránky s detailem záznamu — které karty se zobrazí, jaké widgety jsou uvnitř každé karty a jak jsou uspořádány. Pomocí `definePageLayout()` můžete k aplikaci přidat vlastní rozvržení: - -```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, - }, - }, - ], - }, - ], -}); -``` - -Hlavní body: -* `type` je obvykle `'RECORD_PAGE'` pro úpravu detailního zobrazení konkrétního objektu. -* `objectUniversalIdentifier` určuje, na který objekt se toto rozvržení vztahuje. -* Každá `tab` definuje sekci stránky s `title`, `position` a `layoutMode` (`CANVAS` pro volné rozvržení). -* Každý `widget` uvnitř karty může vykreslit frontendovou komponentu, seznam relací nebo jiné vestavěné typy widgetů. -* `position` na kartách určuje jejich pořadí. Použijte vyšší hodnoty (např. 50) pro umístění vlastních karet za vestavěné. - - - - -`definePageLayoutTab` umožňuje vaší aplikaci připojit jednu kartu — s volitelnými widgety — k **existujícímu** rozvržení stránky. Nejčastějším případem použití je přidání vlastní karty (například karty s analytikou nebo souhrnem AI) na jednu z vestavěných stránek záznamů Twenty nebo do rozvržení stránky, které vaše vlastní aplikace již dodává. - -Cílové rozvržení stránky musí být buď **standardní** rozvržení stránky Twenty, nebo takové, které je definované **vaší vlastní aplikací**; křížové odkazy na rozvržení stránek, která vlastní jiná nainstalovaná aplikace, dnes nejsou podporovány. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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, - }, - }, - ], -}); -``` - -Hlavní body: -* `pageLayoutUniversalIdentifier` je při použití `definePageLayoutTab` **povinný** a musí odkazovat na rozvržení stránky, které již existuje v době instalace (standardní nebo vaší aplikace). Pokud nadřazené rozvržení stránky chybí, instalace selže s jasnou validační chybou. -* `widgets` mají rozsah pouze pro tuto kartu — odkazují na frontendové komponenty, zobrazení apod. úplně stejně jako widgety definované přímo v `definePageLayout`. -* `position` určuje pořadí vzhledem ke stávajícím kartám v cílovém rozvržení. Zvolte hodnotu, která umístí vaši kartu tam, kde ji chcete mít, relativně k vestavěným kartám. -* Použijte to místo `definePageLayout`, když chcete pouze **přidat** do existujícího rozvržení. Použijte `definePageLayout`, když vlastníte celé rozvržení (typicky `RECORD_PAGE` pro objekt, který ve své aplikaci dodáváte, nebo `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index 6d39116f94..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,561 +0,0 @@ ---- -title: Logické funkce -description: Definujte serverové funkce v TypeScriptu se spouštěči pro HTTP, cron a databázové události. -icon: bolt ---- - -Logické funkce jsou serverové funkce v TypeScriptu, které běží na platformě Twenty. Mohou být spouštěny požadavky HTTP, plány cronu nebo databázovými událostmi — a lze je také zpřístupnit jako nástroje pro agenty AI. - - - - -Každý soubor funkce používá `defineLogicFunction()` k exportu konfigurace s obslužnou funkcí (handlerem) a volitelnými spouštěči. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Dostupné typy spouštěčů: -* **httpRoute**: Zpřístupní vaši funkci na HTTP cestě a metodě **pod koncovým bodem `/s/`**: -> např. `path: '/post-card/create'` je volatelné na `https://your-twenty-server.com/s/post-card/create` -* **cron**: Spouští vaši funkci podle plánu pomocí výrazu CRON. -* **databaseEvent**: Spouští se při událostech životního cyklu objektů v pracovním prostoru. Když je operace události `updated`, lze konkrétní sledovaná pole určit v poli `updatedFields`. Pokud zůstane nedefinované nebo prázdné, spustí funkci jakákoli aktualizace. -> např. `person.updated`, `*.created`, `company.*` - - -Funkci můžete také spustit ručně pomocí CLI: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Logy můžete sledovat pomocí: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Payload spouštěče trasy - -Když spouštěč typu route vyvolá vaši logickou funkci, ta obdrží objekt `RoutePayload`, který odpovídá -[AWS HTTP API v2 formátu](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). -Importujte typ `RoutePayload` z `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -Typ `RoutePayload` má následující strukturu: - - | Vlastnost | Typ | Popis | Příklad | - | ---------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | Záhlaví HTTP (pouze ta uvedená v `forwardedRequestHeaders`) | viz sekci níže | - | `queryStringParameters` | `Record\` | Parametry query stringu (více hodnot spojených čárkami) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Parametry cesty extrahované ze vzoru trasy | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Parsované tělo požadavku (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Original UTF-8 request body, before JSON parsing. Useful for verifying HMAC-style webhook signatures (e.g. GitHub's `X-Hub-Signature-256`, Stripe). `undefined` when the runtime did not preserve it. | | - | `isBase64Encoded` | `boolean` | Zda je tělo kódováno base64 | | - | `requestContext.http.method` | `string` | Metoda HTTP (GET, POST, PUT, PATCH, DELETE) | | - | `requestContext.http.path` | `string` | Nezpracovaná cesta požadavku | | - - -#### forwardedRequestHeaders - -Ve výchozím nastavení se záhlaví HTTP z příchozích požadavků z bezpečnostních důvodů do vaší logické funkce **ne** předávají. -Chcete-li zpřístupnit konkrétní záhlaví, výslovně je uveďte v poli `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'], - }, -}); -``` - -Ve vašem handleru k přeposlaným záhlavím přistupujte takto: - -```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 }; -}; -``` - - -Názvy záhlaví jsou normalizovány na malá písmena. Přistupujte k nim pomocí klíčů s malými písmeny (například `event.headers['content-type']`). - - -#### Zpřístupnění funkce jako nástroje - -Logické funkce lze zpřístupnit jako **nástroje** pro agenty AI a pracovní postupy. Když je funkce označena jako nástroj, stane se dohledatelnou funkcemi AI produktu Twenty a lze ji použít v automatizacích pracovních postupů. - -Chcete-li označit logickou funkci jako nástroj, nastavte `isTool: true`: - -```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, - isTool: true, -}); -``` - -Hlavní body: - -* Můžete kombinovat `isTool` se spouštěči — funkce může být zároveň nástrojem (volatelným agenty AI) a současně se spouštět událostmi. -* **`toolInputSchema`** (volitelné): Objekt JSON Schema, který popisuje parametry, jež vaše funkce přijímá. Schéma se určuje automaticky ze statické analýzy zdrojového kódu, ale můžete ho nastavit i explicitně: - -```ts -export default defineLogicFunction({ - ..., - toolInputSchema: { - 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'], - }, -}); -``` - - -**Napište kvalitní `description`.** Agenti AI se spoléhají na pole funkce `description` při rozhodování, kdy nástroj použít. Buďte konkrétní ohledně toho, co nástroj dělá a kdy se má volat. - - - - - -Postinstalační funkce je logická funkce, která se spustí automaticky, jakmile je instalace vaší aplikace v pracovním prostoru dokončena. Server ji provede **poté**, co byla synchronizována metadata aplikace a vygenerován klient SDK, takže je pracovní prostor plně připraven k použití a nové schéma je zavedeno. Mezi typické případy použití patří naplnění výchozími daty, vytvoření počátečních záznamů, konfigurace nastavení pracovního prostoru nebo zřizování prostředků ve službách třetích stran. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Postinstalační funkci můžete také kdykoli spustit ručně pomocí CLI: - -```bash filename="Terminal" -yarn twenty exec --postInstall -``` - -Hlavní body: -* Postinstalační funkce používají `definePostInstallLogicFunction()` — specializovanou variantu, která vynechává nastavení spouštěčů (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`). -* Obslužná funkce obdrží `InstallPayload` s `{ previousVersion?: string; newVersion: string }` — `newVersion` je verze, která se instaluje, a `previousVersion` je verze, která byla nainstalována dříve (nebo `undefined` při čisté instalaci). Tyto hodnoty použijte k rozlišení čistých instalací od aktualizací a ke spuštění migrační logiky specifické pro verzi. -* **Kdy se hook spouští**: ve výchozím nastavení pouze při čistých instalacích. Předejte `shouldRunOnVersionUpgrade: true`, pokud chcete, aby se spouštěl i při aktualizaci aplikace z předchozí verze. Pokud je vynechán, příznak má výchozí hodnotu `false` a při aktualizacích se hook přeskočí. -* **Model provádění — ve výchozím nastavení asynchronní, synchronní volitelně**: příznak `shouldRunSynchronously` určuje *jak* se spouští post-install. - * `shouldRunSynchronously: false` *(výchozí)* — hook je **zařazen do fronty zpráv** s `retryLimit: 3` a běží asynchronně ve workeru. Odezva instalace se vrátí hned po zařazení úlohy do fronty, takže pomalá nebo chybující obslužná funkce neblokuje volajícího. Worker se pokusí o opakování až třikrát. **Použijte pro dlouho běžící úlohy** — plnění velkých datových sad, volání pomalých externích API, zřizování externích prostředků, cokoli, co by mohlo přesáhnout rozumné časové okno HTTP odezvy. - * `shouldRunSynchronously: true` — hook se provádí **inline během instalačního procesu** (stejný vykonavatel jako pre-install). Instalační požadavek blokuje, dokud obslužná funkce nedokončí, a pokud vyvolá výjimku, volající instalace obdrží `POST_INSTALL_ERROR`. Žádné automatické opakování. **Použijte pro rychlé úlohy, které se musí dokončit před odpovědí** — například vrácení validační chyby uživateli nebo rychlé nastavení, na kterém bude klient záviset ihned po návratu volání instalace. Mějte na paměti, že v době, kdy se spustí post-install, už byla migrace metadat aplikována, takže selhání v synchronním režimu změny schématu **ne**vrací zpět — pouze odhalí chybu. -* Ujistěte se, že vaše obslužná funkce je idempotentní. V asynchronním režimu se může fronta pokusit až třikrát; v obou režimech se může hook znovu spustit při aktualizacích, pokud je `shouldRunOnVersionUpgrade: true`. -* Proměnné prostředí `APPLICATION_ID`, `APP_ACCESS_TOKEN` a `API_URL` jsou dostupné uvnitř obslužné funkce (stejně jako u jakékoli jiné logické funkce), takže můžete volat Twenty API s aplikačním přístupovým tokenem omezeným na vaši aplikaci. -* Na jednu aplikaci je povolena pouze jedna postinstalační funkce. Sestavení manifestu skončí chybou, pokud je zjištěna více než jedna. -* Atributy funkce `universalIdentifier`, `shouldRunOnVersionUpgrade` a `shouldRunSynchronously` jsou během buildu automaticky připojeny k manifestu aplikace do pole `postInstallLogicFunction` — není potřeba je uvádět v `defineApplication()`. -* Výchozí časový limit je nastaven na 300 sekund (5 minut), aby umožnil delší úlohy nastavení, jako je naplnění daty. -* **Nespouští se v režimu dev**: když je aplikace registrována lokálně (pomocí `yarn twenty dev`), server zcela přeskočí instalační tok a synchronizuje soubory přímo prostřednictvím sledovače CLI — takže se post-install v režimu dev nikdy nespustí bez ohledu na `shouldRunSynchronously`. Použijte `yarn twenty exec --postInstall` k ručnímu spuštění nad běžícím pracovním prostorem. - - - - -Funkce pre-install je logická funkce, která se během instalace spouští automaticky, **před aplikováním migrace metadat pracovního prostoru**. Má stejný tvar payloadu jako post-install (`InstallPayload`), ale je zařazena dříve v instalačním toku, aby mohla připravit stav, na němž nadcházející migrace závisí — typické použití zahrnuje zálohování dat, ověření kompatibility s novým schématem nebo archivaci záznamů, které se chystají přeuspořádat nebo odstranit. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Předinstalační funkci můžete také kdykoli spustit ručně pomocí CLI: - -```bash filename="Terminal" -yarn twenty exec --preInstall -``` - -Hlavní body: -* Funkce pre-install používají `definePreInstallLogicFunction()` — stejné specializované nastavení jako u post-install, pouze připojené k jiné fázi životního cyklu. -* Obě obslužné funkce pre- i post-install přijímají stejný typ `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importujte jej jednou a znovu použijte pro oba hooky. -* **Kdy se hook spouští**: umístěn těsně před migrací metadat pracovního prostoru (`synchronizeFromManifest`). Před spuštěním server provede čistě aditivní "zjednodušenou synchronizaci", která v metadatech pracovního prostoru zaregistruje pre-install funkci **nové** verze — ničeho dalšího se nedotkne — a poté ji spustí. Protože tato synchronizace je pouze aditivní, objekty, pole a data předchozí verze zůstávají při spuštění vaší obslužné funkce zachována: můžete bezpečně číst a zálohovat stav před migrací. -* **Model provádění**: pre-install se provádí **synchronně** a **blokuje instalaci**. Pokud obslužná funkce vyvolá výjimku, instalace se přeruší ještě před aplikováním jakýchkoli změn schématu — pracovní prostor zůstane na předchozí verzi v konzistentním stavu. Je to záměrné: pre-install je vaše poslední šance odmítnout rizikovou aktualizaci. -* Stejně jako u post-install je na jednu aplikaci povolena pouze jedna funkce pre-install. Během buildu je automaticky připojena k manifestu aplikace pod `preInstallLogicFunction`. -* **Nespouští se v režimu dev**: stejně jako u post-install — u lokálně registrovaných aplikací je instalační tok zcela přeskočen, takže se pre-install pod `yarn twenty dev` nikdy nespustí. Použijte `yarn twenty exec --preInstall` k ručnímu spuštění. - - - - -Oba hooky jsou součástí téhož instalačního toku a přijímají stejný `InstallPayload`. Rozdíl je v tom, **kdy** se spouštějí vzhledem k migraci metadat pracovního prostoru, a to určuje, jakých dat se mohou bezpečně dotýkat. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -Pre-install je vždy **synchronní** (blokuje instalaci a může ji přerušit). Post-install je **ve výchozím nastavení asynchronní** — zařazen do workeru s automatickými pokusy o opakování — ale může přejít na synchronní provádění pomocí `shouldRunSynchronously: true`. Viz accordion `definePostInstallLogicFunction` výše, kdy použít jednotlivé režimy. - -**Použijte `post-install` pro cokoli, co vyžaduje existenci nového schématu.** To je běžný případ: - -* Plnění výchozími daty (vytváření počátečních záznamů, výchozích pohledů, demo obsahu) vůči nově přidaným objektům a polím. -* Registrace webhooků u služeb třetích stran poté, co má aplikace své přihlašovací údaje. -* Volání vlastního API k dokončení nastavení, které závisí na synchronizovaných metadatech. -* Idempotentní logika "zajisti, že to existuje", která má při každé aktualizaci uvést stav do souladu — kombinujte s `shouldRunOnVersionUpgrade: true`. - -Příklad — po instalaci naplňte výchozí záznam `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 => { - 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, -}); -``` - -**Použijte `pre-install`, pokud by migrace jinak zničila nebo poškodila existující data.** Protože pre-install běží proti *předchozímu* schématu a jeho selhání vrací aktualizaci zpět, je to správné místo pro cokoli rizikového: - -* **Zálohování dat, která se chystají odstranit nebo přeuspořádat** — např. odstraňujete pole ve verzi v2 a potřebujete jeho hodnoty zkopírovat do jiného pole nebo je před spuštěním migrace exportovat do úložiště. -* **Archivace záznamů, které by nové omezení zneplatnilo** — např. pole se stává `NOT NULL` a je třeba nejprve smazat nebo opravit řádky s hodnotami null. -* **Ověření kompatibility a odmítnutí aktualizace, pokud nelze aktuální data čistě migrovat** — vyhoďte výjimku z obslužné funkce a instalace se ukončí bez provedených změn. Je to bezpečnější, než zjistit nekompatibilitu uprostřed migrace. -* **Přejmenování nebo změna klíčů dat** před změnou schématu, která by ztratila vazby. - -Příklad — archivujte záznamy před destruktivní migrací: - -```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 => { - // 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, -}); -``` - -**Zlaté pravidlo:** - -| Chcete... | Použít | -| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -| Naplňte výchozí data, nakonfigurujte pracovní prostor, zaregistrujte externí prostředky | `post-install` | -| Spusťte dlouho běžící plnění nebo volání třetích stran, která by neměla blokovat odezvu instalace | `post-install` (výchozí — `shouldRunSynchronously: false`, s opakovanými pokusy workeru) | -| Spusťte rychlé nastavení, na které bude volající spoléhat ihned po návratu volání instalace | `post-install` s `shouldRunSynchronously: true` | -| Čtěte nebo zálohujte data, která by nadcházející migrace ztratila | `pre-install` | -| Odmítněte aktualizaci, která by poškodila existující data | `pre-install` (vyhoďte výjimku z obslužné funkce) | -| Spouštějte srovnání stavu při každé aktualizaci | `post-install` s `shouldRunOnVersionUpgrade: true` | -| Proveďte jednorázové nastavení pouze při první instalaci | `post-install` s `shouldRunOnVersionUpgrade: false` (výchozí) | - - -Pokud si nejste jisti, výchozí volbou je **post-install**. Po pre-install sáhněte pouze tehdy, když je samotná migrace destruktivní a potřebujete zachytit předchozí stav, než zmizí. - - - - - -## Typovaní klienti API (twenty-client-sdk) - -Balíček `twenty-client-sdk` poskytuje dva typované klienty GraphQL pro práci s Twenty API z vašich logických funkcí a frontendových komponent. - -| Klient | Importovat | Koncový bod | Generováno? | -| ------------------- | ---------------------------- | ---------------------------------------------------------------- | ------------------------------ | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — data pracovního prostoru (záznamy, objekty) | Ano, při vývoji/sestavení | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — konfigurace pracovního prostoru, nahrávání souborů | Ne, dodává se předem sestavený | - - - - -`CoreApiClient` je hlavní klient pro dotazování a mutace dat pracovního prostoru. Generuje se z vašeho schématu pracovního prostoru během `yarn twenty dev` nebo `yarn twenty build`, takže je plně typovaný tak, aby odpovídal vašim objektům a polím. - -```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, - }, -}); -``` - -Klient používá syntaxi výběrové sady (selection-set): předáním `true` zahrnete pole, pro argumenty použijte `__args` a pro relace vnořujte objekty. Získáte plné automatické doplňování a kontrolu typů založené na schématu vašeho pracovního prostoru. - - -**CoreApiClient je generován při vývoji/sestavení.** Pokud jej použijete bez předchozího spuštění `yarn twenty dev` nebo `yarn twenty build`, vyvolá chybu. Generování probíhá automaticky — CLI prozkoumá GraphQL schéma vašeho pracovního prostoru a vygeneruje typovaného klienta pomocí `@genql/cli`. - - -#### Použití CoreSchema pro anotace typů - -`CoreSchema` poskytuje typy TypeScriptu odpovídající objektům vašeho pracovního prostoru — hodí se pro typování stavu komponent nebo parametrů funkcí: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`MetadataApiClient` je dodáván předem sestavený v rámci SDK (není vyžadováno žádné generování). Odesílá dotazy na endpoint `/metadata` pro konfiguraci pracovního prostoru, aplikace a nahrávání souborů. - -```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 }, - }, - }, -}); -``` - -#### Nahrávání souborů - -`MetadataApiClient` obsahuje metodu `uploadFile` pro připojování souborů k polím typu souboru: - -```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://...' } -``` - -| Parametr | Typ | Popis | -| ---------------------------------- | -------- | ------------------------------------------------------------------- | -| `fileBuffer` | `Buffer` | Surový obsah souboru | -| `filename` | `string` | Název souboru (používá se pro ukládání a zobrazení) | -| `contentType` | `string` | Typ MIME (pokud je vynechán, výchozí je `application/octet-stream`) | -| `fieldMetadataUniversalIdentifier` | `string` | `universalIdentifier` pole typu souboru ve vašem objektu | - -Hlavní body: -* Používá `universalIdentifier` pole (nikoli jeho ID specifické pro pracovní prostor), takže váš kód pro nahrávání funguje v jakémkoli pracovním prostoru, kde je vaše aplikace nainstalována. -* Vrácená hodnota `url` je podepsaná adresa URL, kterou můžete použít k přístupu k nahranému souboru. - - - - - - Když váš kód běží na Twenty (logické funkce nebo frontendové komponenty), platforma vloží přihlašovací údaje jako proměnné prostředí: - - * `TWENTY_API_URL` — Základní URL Twenty API - * `TWENTY_APP_ACCESS_TOKEN` — krátkodobý klíč s rozsahem omezeným na výchozí roli funkce vaší aplikace - - Není nutné je předávat klientům — čtou je automaticky z `process.env`. Oprávnění API klíče jsou určena rolí uvedenou v `defaultRoleUniversalIdentifier` ve vašem `application-config.ts`. - diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/publishing.mdx deleted file mode 100644 index 7557e432fc..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,254 +0,0 @@ ---- -title: Publikování -icon: nahrát -description: Distribuujte svou aplikaci Twenty do Marketplace nebo ji nasaďte interně. ---- - -## Přehled - -Jakmile je vaše aplikace [sestavena a otestována lokálně](/l/cs/developers/extend/apps/building), máte dvě cesty, jak ji distribuovat: - -* **Nasaďte tarball** — nahrajte svou aplikaci přímo na konkrétní server Twenty pro interní nebo soukromé použití. -* **Publish to npm** — uveďte svou aplikaci v Marketplace Twenty, aby ji mohl kterýkoli pracovní prostor objevit a nainstalovat. - -Obě cesty začínají stejným krokem **build**. - -## Sestavení vaší aplikace - -Spusťte příkaz build ke zkompilování své aplikace a k vygenerování souboru `manifest.json` připraveného k distribuci: - -```bash filename="Terminal" -yarn twenty build -``` - -Tím se zkompilují zdrojové soubory TypeScriptu, transpilují logické funkce a frontendové komponenty a vše se zapíše do `.twenty/output/`. Přidejte `--tarball`, abyste také vytvořili balíček `.tgz` pro ruční distribuci nebo příkaz deploy. - -## Nasazení na server (tarball) - -U aplikací, které nechcete zpřístupnit veřejně — proprietární nástroje, integrace pouze pro enterprise nebo experimentální buildy — můžete nasadit tarball přímo na server Twenty. - -### Předpoklady - -Před nasazením potřebujete nakonfigurovaný vzdálený cíl směřující na cílový server. Vzdálené cíle ukládají adresu URL serveru a přihlašovací údaje lokálně v `~/.twenty/config.json`. - -Přidat vzdálený cíl: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Nasazení - -Sestavte a nahrajte svou aplikaci na server v jednom kroku: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Sdílení nasazené aplikace - - -Sdílení soukromých (tarball) aplikací napříč pracovními prostory je funkcí **Enterprise**. Karta **Distribution** bude místo ovládacích prvků sdílení zobrazovat výzvu k upgradu, dokud váš pracovní prostor nebude mít platný klíč Enterprise. Přejděte do [Nastavení > Admin Panel > Enterprise](/settings/admin-panel#enterprise) a aktivujte ji. - - -Aplikace ve formě tarball nejsou uvedeny ve veřejném tržišti, takže je ostatní pracovní prostory na tomtéž serveru procházením neobjeví. Jakmile je váš pracovní prostor na tarifu Enterprise, můžete sdílet nasazenou aplikaci takto: - -1. Přejděte do **Nastavení > Aplikace > Registrace** a otevřete svou aplikaci -2. Na kartě **Distribuce** klikněte na **Zkopírovat odkaz ke sdílení** -3. Sdílejte tento odkaz s uživateli v jiných pracovních prostorech — zavede je přímo na instalační stránku aplikace - -Odkaz ke sdílení používá základní adresu URL serveru (bez jakékoli subdomény pracovního prostoru), takže funguje pro libovolný pracovní prostor na serveru. - -### Správa verzí - -Při aktualizaci již nasazené tarballové aplikace server vyžaduje, aby hodnota `version` v `package.json` byla **přísně vyšší** (podle řazení [semver](https://semver.org)) než aktuálně nasazená verze. Opětovné nasazení stejné verze nebo odeslání nižší verze je odmítnuto ještě před uložením tarballu — v CLI uvidíte chybu `VERSION_ALREADY_EXISTS`. - -Chcete-li vydat aktualizaci: - -1. Zvyšte hodnotu pole `version` v souboru `package.json` (např. `1.2.3` → `1.2.4`, `1.3.0` nebo `2.0.0`) -2. Spusťte `yarn twenty deploy` (nebo `yarn twenty deploy --remote production`) -3. Pracovní prostory, které mají aplikaci nainstalovanou, uvidí dostupnou aktualizaci ve svém nastavení - - -Předběžné tagy fungují podle očekávání: zvýšení z `1.0.0-rc.1` → `1.0.0-rc.2` je povoleno a finální vydání jako `1.0.0` je správně rozpoznáno jako vyšší než `1.0.0-rc.5`. Verze v `package.json` musí být platným řetězcem semver. - - -{/* TODO: add screenshot of the Upgrade button */} - -## Automatizované CI/CD (předpřipravené workflowy) - -Aplikace vygenerované pomocí `create-twenty-app` jsou hned připravené se dvěma workflowy GitHub Actions ve složce `.github/workflows/`. Jsou připravené ke spuštění hned, jakmile repozitář pushnete na GitHub — pro CI není potřeba žádné další nastavení a CD vyžaduje pouze jeden secret. - -### CI — `ci.yml` - -Automaticky spouští integrační testy při každém pushi do `main` a u pull requestů. - -**K čemu slouží:** - -1. Provede checkout zdrojového kódu vaší aplikace. -2. Spustí izolovanou testovací instanci Twenty pomocí složené akce `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (ekvivalent v CI k `yarn twenty server start --test`). -3. Povolí Corepack, nastaví Node.js podle vašeho `.nvmrc` a nainstaluje závislosti pomocí `yarn install --immutable`. -4. Spustí `yarn test` a předá `TWENTY_API_URL` a `TWENTY_API_KEY` ze spuštěné instance, aby vaše testy mohly komunikovat se skutečným serverem. - -**Konfigurační volby:** - -* `TWENTY_VERSION` (env, výchozí hodnota `latest`) — uzamkněte v CI používanou verzi serveru Twenty úpravou této hodnoty v `ci.yml`. -* Souběžné běhy jsou seskupeny podle `github.ref` a při nových pushích ruší právě probíhající běhy. - -Nejsou potřeba žádné secrety — testovací instance je efemérní a existuje pouze po dobu běhu úlohy. - -### CD — `cd.yml` - -Nasazuje vaši aplikaci na nakonfigurovaný server Twenty při každém pushi do `main` a volitelně také z pull requestu, pokud je přidán štítek `deploy`. - -**K čemu slouží:** - -1. Provede checkout headu PR (u označených PR) nebo pushnutého commitu. -2. Spustí `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — ekvivalent v CI k `yarn twenty deploy`. -3. Spustí `twentyhq/twenty/.github/actions/install-twenty-app@main`, aby se nově nasazená verze nainstalovala do cílového workspace. - -**Požadovaná konfigurace:** - -| Nastavení | Kde | Účel | -| ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `env` v `cd.yml` (výchozí `http://localhost:3000`) | Server Twenty, na který se nasazuje. Před prvním použitím to změňte na skutečnou URL vašeho serveru. | -| `TWENTY_DEPLOY_API_KEY` | GitHub repozitář **Settings → Secrets and variables → Actions** | API klíč s oprávněním k nasazení na cílovém serveru. | - - -Výchozí `TWENTY_DEPLOY_URL` `http://localhost:3000` je pouze zástupná hodnota — z runneru hostovaného GitHubem tato adresa nebude dosažitelná. Před povolením CD ji aktualizujte na veřejnou URL vašeho serveru (nebo použijte self-hosted runner s přístupem do sítě). - - -**Spuštění náhledového nasazení z PR:** - -Přidejte k pull requestu štítek `deploy`. Podmínka `if:` v `cd.yml` spustí úlohu pro dané PR s použitím head commitu PR, což vám umožní ověřit změnu na cílovém serveru před sloučením. - -### Připnutí verzí znovupoužitelných akcí - -Obě workflowy odkazují na znovupoužitelné akce na `@main`, takže aktualizace akcí v repozitáři `twentyhq/twenty` se přeberou automaticky. Pokud chcete deterministická sestavení, nahraďte `@main` v každém řádku `uses:` za commit SHA nebo tag vydání. - -## Publikování na npm - -Publikování na npm zajistí, že bude vaše aplikace dohledatelná v Marketplace Twenty. Jakýkoli pracovní prostor Twenty může procházet, instalovat a aktualizovat aplikace z Marketplace přímo z UI. - -### Požadavky - -* Účet na [npm](https://www.npmjs.com) -* Klíčové slovo `twenty-app` ve vašem poli `keywords` v souboru `package.json` (přidejte je ručně — ve výchozím nastavení není zahrnuto v šabloně `create-twenty-app`) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Metadata tržiště - -Konfigurace `defineApplication()` podporuje volitelná pole, která určují, jak se vaše aplikace zobrazuje v tržišti. Použijte `logoUrl` a `screenshots` k odkazování na obrázky ze složky `public/`: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -Podívejte se na [sekci defineApplication](/l/cs/developers/extend/apps/building#defineentity-functions) na stránce Building Apps pro úplný seznam polí tržiště (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl` atd.). - -### Publikování - -```bash filename="Terminal" -yarn twenty publish -``` - -Chcete-li publikovat pod konkrétním dist-tagem (např. `beta` nebo `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### Jak funguje objevování v tržišti - -Server Twenty synchronizuje svůj katalog tržiště z registru npm **každou hodinu**. - -Synchronizaci můžete spustit okamžitě místo čekání: - -```bash filename="Terminal" -yarn twenty catalog-sync -# To target a specific remote: -# yarn twenty catalog-sync --remote production -``` - -Metadata zobrazená v tržišti pocházejí z vaší konfigurace `defineApplication()` — z polí jako `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` a `termsUrl`. - - -Pokud vaše aplikace nedefinuje `aboutDescription` v `defineApplication()`, tržiště automaticky použije soubor `README.md` vašeho balíčku z npm jako obsah stránky O aplikaci. To znamená, že můžete spravovat jediný soubor README jak pro npm, tak pro tržiště Twenty. Pokud chcete v tržišti jiný popis, explicitně nastavte `aboutDescription`. - - -### Publikování pomocí CI - -Použijte tento pracovní postup GitHub Actions k automatickému publikování při každém vydání (používá [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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Pro jiné systémy CI (GitLab CI, CircleCI atd.) platí stejné tři příkazy: `yarn install`, `yarn twenty build` a poté `npm publish` z `.twenty/output`. - - -**npm provenance** je volitelné, ale doporučené. Publikování s `--provenance` přidá k vašemu záznamu na npm odznak důvěryhodnosti a umožní uživatelům ověřit, že balíček byl sestaven z konkrétního commitu ve veřejné CI pipeline. Pokyny k nastavení najdete v [dokumentaci k npm provenance](https://docs.npmjs.com/generating-provenance-statements). - - -## Instalace aplikací - -Jakmile je aplikace publikována (npm) nebo nasazena (tarball), mohou ji pracovní prostory nainstalovat prostřednictvím uživatelského rozhraní. - -Přejděte na stránku **Nastavení > Aplikace** v Twenty, kde lze procházet a instalovat jak aplikace z tržiště, tak aplikace nasazené jako tarball. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Aplikace můžete nainstalovat také z příkazového řádku: - -```bash filename="Terminal" -yarn twenty install -``` - - -Server při instalaci vynucuje verzování semver a zrcadlí pravidla pro nasazení: - -* Instalace stejné verze, která je již nainstalována ve vašem pracovním prostoru, je odmítnuta s chybou `APP_ALREADY_INSTALLED`. -* Instalace nižší verze, než je aktuálně nainstalovaná, je odmítnuta s chybou `CANNOT_DOWNGRADE_APPLICATION`. - -K instalaci novější verze ji nejprve nasaďte nebo publikujte, poté znovu spusťte `yarn twenty install`. - diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index 1eda6da5b3..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Dovednosti a agenti -description: Definujte dovednosti a agenty AI pro svou aplikaci. -icon: robot ---- - - - Dovednosti a agenti jsou aktuálně v alfa fázi. Funkce funguje, ale stále se vyvíjí. - - -Aplikace mohou definovat schopnosti AI, které fungují přímo v pracovním prostoru — znovupoužitelné pokyny pro dovednosti a agenty s vlastními systémovými prompty. - - - - -Dovednosti definují znovupoužitelné pokyny a schopnosti, které mohou agenti AI používat ve vašem pracovním prostoru. K definování dovedností s vestavěnou validací použijte `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`, -}); -``` - -Hlavní body: -* `name` je jedinečný identifikátor dovednosti (doporučuje se kebab-case). -* `label` je uživatelsky čitelný název zobrazovaný v UI. -* `content` obsahuje pokyny dovednosti — je to text, který agent AI používá. -* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI. -* `description` (volitelné) poskytuje doplňující kontext o účelu dovednosti. - - - - -Agenti jsou asistenti AI, kteří běží ve vašem pracovním prostoru. K vytvoření agentů s vlastním systémovým promptem použijte `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.', -}); -``` - -Hlavní body: -* `name` je jedinečný identifikátor agenta (doporučuje se kebab-case). -* `label` je zobrazovaný název v UI. -* `prompt` je systémový prompt, který definuje chování agenta. -* `description` (volitelné) poskytuje kontext o tom, co agent dělá. -* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI. -* `modelId` (volitelné) přepíše výchozí model AI používaný agentem. - - - diff --git a/packages/twenty-docs/l/cs/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/cs/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 3b1d3237c5..0000000000 --- a/packages/twenty-docs/l/cs/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Aplikace Twenty -description: Vytvářejte a spravujte přizpůsobení Twenty jako kód. ---- - - -Aplikace jsou aktuálně v alfa fázi. Funkce funguje, ale stále se vyvíjí. - - -## Co jsou aplikace? - -Aplikace vám umožňují rozšířit Twenty o vlastní objekty, pole, logické funkce, front-endové komponenty, AI schopnosti a další — vše je spravováno jako kód. Místo konfigurace všeho přes uživatelské rozhraní definujete v TypeScriptu svůj datový model a logiku a nasadíte je do jednoho nebo více pracovních prostorů. - -**Co můžete vytvořit:** - -* **Vlastní objekty a pole** — rozšiřte svůj datový model o nové entity nebo přidejte pole k existujícím objektům, jako jsou Společnost nebo Osoba -* **Logické funkce** — serverové funkce spouštěné událostmi v databázi, plány cronu nebo HTTP routami -* **Front-endové komponenty** — komponenty Reactu, které se vykreslují v uživatelském rozhraní Twenty (stránky záznamů, příkazová nabídka, postranní panely) -* **Dovednosti AI a agenti** — rozšiřte AI v Twenty o vlastní možnosti -* **Zobrazení a navigace** — předkonfigurovaná uložená zobrazení a odkazy v postranním panelu - -## Rychlý start - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Tímto se vytvoří kostra nové aplikace, volitelně se spustí lokální server Twenty a začne sledovat změny ve vašich souborech. Podrobný postup najdete v průvodci [Začínáme](/l/cs/developers/extend/apps/getting-started). - -## Podrobné návody - -| Průvodce | Popis | -| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| [Začínáme](/l/cs/developers/extend/apps/getting-started) | Vytvoření kostry aplikace, nastavení lokálního serveru, struktura projektu, CI | -| [Tvorba aplikací](/l/cs/developers/extend/apps/building) | Definice entit (`defineObject`, `defineLogicFunction`, `defineFrontComponent` atd.), klienti API, balíčky npm, veřejná aktiva, testování | -| [Publikování](/l/cs/developers/extend/apps/publishing) | Nasazení na server, publikování na npm, tržiště | - -## Klíčové pojmy - -### Detekce entit - -SDK detekuje entity prohledáváním vašich souborů TypeScript a hledá volání `export default define({...})`. Pojmenování souborů a struktura složek jsou flexibilní — detekce je založená na AST, nikoli na cestách. - -### Dostupné typy entit - -| Funkce | Účel | -| ---------------------------------- | ------------------------------------------------ | -| `defineApplication()` | Metadata aplikace (povinné, jedno na aplikaci) | -| `defineObject()` | Vlastní objekty s poli | -| `defineField()` | Pole u existujících objektů | -| `defineLogicFunction()` | Serverová logika se spouštěči | -| `defineFrontComponent()` | Komponenty Reactu v uživatelském rozhraní Twenty | -| `defineRole()` | Role oprávnění | -| `defineView()` | Konfigurace uložených zobrazení | -| `defineNavigationMenuItem()` | Odkazy postranní navigace | -| `defineSkill()` | Dovednosti agenta AI | -| `defineAgent()` | AI agenti s prompty | -| `definePageLayout()` | Vlastní rozvržení stránek záznamu | -| `definePreInstallLogicFunction()` | Spouští se před instalací aplikace | -| `definePostInstallLogicFunction()` | Spouští se po instalaci aplikace | - -### Vývojový postup - -1. **`yarn twenty dev`** — sleduje zdrojové soubory, při změně znovu sestaví, synchronizuje se serverem a generuje typované klienty API -2. **`yarn twenty build`** — vytvoří distribuovatelný build -3. **`yarn twenty deploy`** — nasadí na vzdálený server Twenty -4. **`yarn twenty add`** — interaktivně vytvoří kostru nové entity - -### Referenční dokumentace CLI - -```bash filename="Terminal" -yarn twenty help # List all commands -yarn twenty server start # Start local dev server -yarn twenty remote add # Connect to a Twenty server -yarn twenty exec -n fn # Execute a logic function -yarn twenty logs -n fn # Stream function logs -``` - -Úplný přehled příkazů CLI najdete v průvodci [Začínáme](/l/cs/developers/extend/apps/getting-started). diff --git a/packages/twenty-docs/l/cs/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/cs/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index fc45e0e004..0000000000 --- a/packages/twenty-docs/l/cs/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Nastavení vydání -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Funkce v Labu - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Přejděte do **Nastavení → Vydání** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/de/developers/extend/apps/building.mdx b/packages/twenty-docs/l/de/developers/extend/apps/building.mdx deleted file mode 100644 index 106fdd1a98..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Architektur -description: Wie Twenty-Apps funktionieren — Sandboxing, Lebenszyklus und Bausteine. -icon: sitemap ---- - -Twenty-Apps sind TypeScript-Pakete, die Ihren Arbeitsbereich mit benutzerdefinierten Objekten, Logik, UI-Komponenten und KI-Funktionen erweitern. Sie laufen auf der Twenty-Plattform mit vollständigem Sandboxing und Berechtigungsverwaltung. - -## Wie Apps funktionieren - -Eine App ist eine Sammlung von **Entitäten**, die mithilfe von `defineEntity()`-Funktionen aus dem Paket `twenty-sdk` deklariert werden. Das SDK erkennt diese Deklarationen zur Build-Zeit per AST-Analyse und erzeugt ein **Manifest** — eine vollständige Beschreibung dessen, was Ihre App zu einem Arbeitsbereich hinzufügt. - -``` -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 -``` - - - **Die Dateiorganisation liegt bei Ihnen.** Die Entitätserkennung ist AST-basiert — das SDK findet Aufrufe von `export default defineEntity(...)`, unabhängig davon, wo sich die Datei befindet. Die obige Ordnerstruktur ist eine Konvention, keine Anforderung. - - -## Entitätstypen - -| Entität | Zweck | Dokumentation | -| -------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------- | -| **Anwendung** | App-Identität, Berechtigungen, Variablen | [Datenmodell](/l/de/developers/extend/apps/data-model) | -| **Rolle** | Berechtigungssätze für Objekte und Felder | [Datenmodell](/l/de/developers/extend/apps/data-model) | -| **Object** | Benutzerdefinierte Datentabellen mit Feldern | [Datenmodell](/l/de/developers/extend/apps/data-model) | -| **Feld** | Bestehende Objekte erweitern, Relationen definieren | [Datenmodell](/l/de/developers/extend/apps/data-model) | -| **Logikfunktion** | Serverseitiges TypeScript mit Triggern | [Logikfunktionen](/l/de/developers/extend/apps/logic-functions) | -| **Frontend-Komponente** | Sandboxed React-UI auf der Twenty-Seite | [Frontend-Komponenten](/l/de/developers/extend/apps/front-components) | -| **Skill** | Wiederverwendbare Anweisungen für KI-Agenten | [Skills & Agenten](/l/de/developers/extend/apps/skills-and-agents) | -| **Agent** | KI-Assistenten mit benutzerdefinierten Prompts | [Skills & Agenten](/l/de/developers/extend/apps/skills-and-agents) | -| **Ansicht** | Vorkonfigurierte Listenansichten für Datensätze | [Layout](/l/de/developers/extend/apps/layout) | -| **Navigationsmenüeintrag** | Benutzerdefinierte Seitenleisten-Einträge | [Layout](/l/de/developers/extend/apps/layout) | -| **Seitenlayout** | Benutzerdefinierte Registerkarten und Widgets auf Datensatzseiten | [Layout](/l/de/developers/extend/apps/layout) | - -## Sandboxing - -* **Logikfunktionen** laufen in isolierten Node.js-Prozessen auf dem Server. Sie greifen nur über den typisierten API-Client auf Daten zu, begrenzt durch die Rollenberechtigungen der App. -* **Frontend-Komponenten** laufen in Web Workers mit Remote DOM — von der Hauptseite isoliert, rendern aber native DOM-Elemente (keine iframes). Sie kommunizieren über eine Message-Passing-Host-API mit Twenty. -* **Berechtigungen** werden auf API-Ebene durchgesetzt. Das Laufzeit-Token (`TWENTY_APP_ACCESS_TOKEN`) wird aus der in `defineApplication()` definierten Rolle abgeleitet. - -## App-Lebenszyklus - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ Install flow │ -│ upload → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -├─────────────────────────────────────────────────────────┤ -│ Publish │ -│ npm publish → appears in Twenty marketplace │ -└─────────────────────────────────────────────────────────┘ -``` - -* **`yarn twenty dev`** — überwacht Ihre Quelldateien und synchronisiert Änderungen in Echtzeit mit einem verbundenen Twenty-Server. Der typisierte API-Client wird automatisch neu erzeugt, wenn sich das Schema ändert. -* **`yarn twenty build`** — kompiliert TypeScript, bündelt Logikfunktionen und Frontend-Komponenten mit esbuild und erzeugt ein Manifest. -* **Pre/Post-Install-Hooks** — optionale Logikfunktionen, die während der Installation ausgeführt werden. Details finden Sie unter [Logikfunktionen](/l/de/developers/extend/apps/logic-functions). - -## Nächste Schritte - - - - Objekte, Felder, Rollen und Relationen definieren. - - - Serverseitige Funktionen mit HTTP-, cron- und Ereignis-Triggern. - - - Sandboxed React-Komponenten innerhalb der UI von Twenty. - - - Ansichten, Navigationseinträge und Layouts von Datensatzseiten. - - - KI-Skills und Agenten mit benutzerdefinierten Prompts. - - - CLI-Befehle, Tests, Assets, Remotes und CI. - - - Auf einem Server bereitstellen oder auf dem Marktplatz veröffentlichen. - - diff --git a/packages/twenty-docs/l/de/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/de/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index 22f10eafbd..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI & Tests -description: CLI-Befehle, Test-Setup, öffentliche Assets, npm-Pakete, Remotes und CI-Konfiguration. -icon: terminal ---- - -## Öffentliche Assets (Ordner `public/`) - -Der Ordner `public/` im Stammverzeichnis Ihrer App enthält statische Dateien — Bilder, Icons, Schriftarten oder sonstige Assets, die Ihre App zur Laufzeit benötigt. Diese Dateien werden automatisch in Builds aufgenommen, während des Dev-Modus synchronisiert und auf den Server hochgeladen. - -Für Dateien im Verzeichnis `public/` gilt: - -* **Öffentlich zugänglich** — nach der Synchronisierung mit dem Server werden Assets unter einer öffentlichen URL bereitgestellt. Zum Zugriff ist keine Authentifizierung erforderlich. -* **In Frontend-Komponenten verfügbar** — verwenden Sie Asset-URLs, um Bilder, Icons oder andere Medien in Ihren React-Komponenten anzuzeigen. -* **In Logikfunktionen verfügbar** — referenzieren Sie Asset-URLs in E-Mails, API-Antworten oder in beliebiger serverseitiger Logik. -* **Für Marketplace-Metadaten verwendet** — die Felder `logoUrl` und `screenshots` in `defineApplication()` referenzieren Dateien aus diesem Ordner (z. B. `public/logo.png`). Diese werden im Marketplace angezeigt, wenn Ihre App veröffentlicht wird. -* **Im Dev-Modus automatisch synchronisiert** — wenn Sie in `public/` eine Datei hinzufügen, aktualisieren oder löschen, wird sie automatisch mit dem Server synchronisiert. Kein Neustart erforderlich. -* **In Builds enthalten** — `yarn twenty build` bündelt alle öffentlichen Assets in der Distributionsausgabe. - -### Zugriff auf öffentliche Assets mit `getPublicAssetUrl` - -Verwenden Sie den Helper `getPublicAssetUrl` aus `twenty-sdk`, um die vollständige URL einer Datei in Ihrem `public/`-Verzeichnis zu erhalten. Dies funktioniert sowohl in Logikfunktionen als auch in Frontend-Komponenten. - -**In einer Logikfunktion:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, -}); -``` - -**In einer Frontend-Komponente:** - -```tsx src/front-components/company-card.tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -Das Argument `path` ist relativ zum `public/`-Ordner Ihrer App. Sowohl `getPublicAssetUrl('logo.png')` als auch `getPublicAssetUrl('public/logo.png')` ergeben dieselbe URL — das Präfix `public/` wird, falls vorhanden, automatisch entfernt. - -## Verwendung von npm-Paketen - -Sie können in Ihrer App beliebige npm-Pakete installieren und verwenden. Sowohl Logikfunktionen als auch Frontend-Komponenten werden mit [esbuild](https://esbuild.github.io/) gebündelt, das alle Abhängigkeiten in die Ausgabe einbettet — zur Laufzeit sind keine `node_modules` erforderlich. - -### Ein Paket installieren - -```bash filename="Terminal" -yarn add axios -``` - -Importieren Sie es anschließend in Ihrem Code: - -```ts src/logic-functions/fetch-data.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import axios from 'axios'; - -const handler = async (): Promise => { - 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, -}); -``` - -Dasselbe funktioniert für Frontend-Komponenten: - -```tsx src/front-components/chart.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { format } from 'date-fns'; - -const DateWidget = () => { - return

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### Wie das Bundling funktioniert - -Der Build-Schritt verwendet esbuild, um pro Logikfunktion und pro Frontend-Komponente eine einzelne, in sich geschlossene Datei zu erzeugen. Alle importierten Pakete werden in das Bundle eingebettet. - -**Logikfunktionen** laufen in einer Node.js-Umgebung. Eingebaute Node.js-Module (`fs`, `path`, `crypto`, `http` usw.) stehen zur Verfügung und müssen nicht installiert werden. - -**Frontend-Komponenten** laufen in einem Web Worker. Eingebaute Node.js-Module sind **nicht** verfügbar — nur Browser-APIs und npm-Pakete, die in einer Browserumgebung funktionieren. - -In beiden Umgebungen stehen `twenty-client-sdk/core` und `twenty-client-sdk/metadata` als vorab bereitgestellte Module zur Verfügung — sie werden nicht gebündelt, sondern zur Laufzeit vom Server aufgelöst. - -## Ihre App testen - -Das SDK stellt programmgesteuerte APIs bereit, mit denen Sie Ihre App aus Testcode heraus bauen, bereitstellen, installieren und deinstallieren können. In Kombination mit [Vitest](https://vitest.dev/) und den typisierten API-Clients können Sie Integrationstests schreiben, die prüfen, dass Ihre App End-to-End gegen einen echten Twenty-Server funktioniert. - -### Einrichtung - -Die erzeugte App enthält bereits Vitest. Wenn Sie es manuell einrichten, installieren Sie die Abhängigkeiten: - -```bash filename="Terminal" -yarn add -D vitest vite-tsconfig-paths -``` - -Erstellen Sie eine `vitest.config.ts` im Stammverzeichnis Ihrer App: - -```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', - }, - }, -}); -``` - -Erstellen Sie eine Setup-Datei, die vor dem Testlauf überprüft, dass der Server erreichbar ist: - -```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), - ); -}); -``` - -### Programmgesteuerte SDK-APIs - -Der Subpfad `twenty-sdk/cli` exportiert Funktionen, die Sie direkt aus Testcode aufrufen können: - -| Funktion | Beschreibung | -| -------------- | ----------------------------------------------------- | -| `appBuild` | Die App bauen und optional ein Tarball packen | -| `appDeploy` | Ein Tarball auf den Server hochladen | -| `appInstall` | Die App im aktiven Arbeitsbereich installieren | -| `appUninstall` | Die App aus dem aktiven Arbeitsbereich deinstallieren | - -Jede Funktion gibt ein Ergebnisobjekt mit `success: boolean` und entweder `data` oder `error` zurück. - -### Einen Integrationstest schreiben - -Hier ist ein vollständiges Beispiel, das die App baut, bereitstellt und installiert und anschließend prüft, dass sie im Arbeitsbereich erscheint: - -```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(); - }); -}); -``` - -### Tests ausführen - -Stellen Sie sicher, dass Ihr lokaler Twenty-Server läuft, und führen Sie dann Folgendes aus: - -```bash filename="Terminal" -yarn test -``` - -Oder im Watch-Modus während der Entwicklung: - -```bash filename="Terminal" -yarn test:watch -``` - -### Typprüfung - -Sie können die Typprüfung Ihrer App auch ohne Tests ausführen: - -```bash filename="Terminal" -yarn twenty typecheck -``` - -Dies führt `tsc --noEmit` aus und meldet etwaige Typfehler. - -## CLI-Referenz - -Zusätzlich zu `dev`, `build`, `add` und `typecheck` bietet die CLI Befehle zum Ausführen von Funktionen, Anzeigen von Logs und Verwalten von App-Installationen. - -### Funktionen ausführen (`yarn twenty exec`) - -Eine Logikfunktion manuell ausführen, ohne sie über HTTP, Cron oder ein Datenbankereignis auszulösen: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Funktionsprotokolle ansehen (`yarn twenty logs`) - -Ausführungsprotokolle für die Logikfunktionen Ihrer App streamen: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -Dies unterscheidet sich von `yarn twenty server logs`, das die Docker-Container-Logs anzeigt. `yarn twenty logs` zeigt die Funktionsausführungsprotokolle Ihrer App vom Twenty-Server. - - -### Eine App deinstallieren (`yarn twenty uninstall`) - -Entfernen Sie Ihre App aus dem aktiven Arbeitsbereich: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## Remotes verwalten - -Ein **Remote** ist ein Twenty-Server, mit dem sich Ihre App verbindet. Während der Einrichtung erstellt das Scaffolding-Tool automatisch eines für Sie. Sie können jederzeit weitere Remotes hinzufügen oder zwischen ihnen wechseln. - -```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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Ihre Anmeldedaten werden in `~/.twenty/config.json` gespeichert. - -## CI mit GitHub Actions - -Das Scaffolding-Tool erzeugt einen einsatzbereiten GitHub-Actions-Workflow in `.github/workflows/ci.yml`. Er führt Ihre Integrationstests automatisch bei jedem Push auf `main` und bei Pull Requests aus. - -Der Workflow: - -1. Checkt Ihren Code aus -2. Startet einen temporären Twenty-Server mit der Aktion `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Installiert Abhängigkeiten mit `yarn install --immutable` -4. Führt `yarn test` aus, wobei `TWENTY_API_URL` und `TWENTY_API_KEY` aus den Aktionsausgaben injiziert werden. - -```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 }} -``` - -Sie müssen keine Secrets konfigurieren — die Aktion `spawn-twenty-docker-image` startet einen flüchtigen Twenty-Server direkt im Runner und gibt die Verbindungsdetails aus. Das Secret `GITHUB_TOKEN` wird automatisch von GitHub bereitgestellt. - -Um eine bestimmte Twenty-Version statt `latest` festzulegen, ändern Sie die Umgebungsvariable `TWENTY_VERSION` oben im Workflow. diff --git a/packages/twenty-docs/l/de/developers/extend/apps/connections.mdx b/packages/twenty-docs/l/de/developers/extend/apps/connections.mdx deleted file mode 100644 index 7614d2f9c1..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/connections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Verbindungen -description: Ermöglichen Sie Ihrer App, im Namen eines Benutzers über OAuth in Diensten von Drittanbietern zu handeln. -icon: plug ---- - -Verbindungen sind Anmeldedaten, die ein Benutzer für einen externen Dienst besitzt (Linear, GitHub, Slack, ...). Ihre App legt fest, **wie** diese Anmeldedaten bezogen werden — ein **Verbindungsanbieter** — und verwendet sie zur Laufzeit, um authentifizierte Aufrufe an die Drittanbieter-API zu tätigen. - -Derzeit wird nur OAuth 2.0 unterstützt. Zukünftige Anmeldedatentypen (Personal Access Tokens, API-Schlüssel, Basic Auth) werden in dieselbe Oberfläche integriert — Apps, die bereits `defineConnectionProvider({ type: 'oauth', ... })` müssen nicht migriert werden. - - - - - -Ein Verbindungsanbieter beschreibt den OAuth-Handshake, den Ihre App benötigt. Der Benutzer klickt in den Einstellungen Ihrer App auf "Verbindung hinzufügen", schließt den Zustimmungsbildschirm des Anbieters ab, und in seinem Arbeitsbereich wird eine `ConnectedAccount`-Zeile erstellt. - -Eine funktionierende Einrichtung benötigt **zwei Dateien** — den Verbindungsanbieter und eine passende `serverVariables`-Deklaration in `defineApplication`, die die OAuth-Client-Anmeldedaten enthält. - -```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.', - defaultRoleUniversalIdentifier: '...', - // 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, - }, - }, -}); -``` - -Hauptpunkte: - -* `name` ist die eindeutige Bezeichner-Zeichenfolge, die in `listConnections({ providerName })` verwendet wird (kebab-case, muss `^[a-z][a-z0-9-]*$` entsprechen). -* `displayName` wird im Einstellungs-Tab der jeweiligen App und in der KI-Toolliste angezeigt. -* `clientIdVariable` / `clientSecretVariable` sind **Namen**, keine Werte — sie müssen den in `defineApplication.serverVariables` deklarierten Schlüsseln entsprechen. Die tatsächlichen `client_id` und `client_secret` werden vom Serveradministrator über die App-Registrierungsoberfläche eingegeben und niemals in Ihr Repository eingecheckt. -* Verwenden Sie `serverVariables` (nicht `applicationVariables`) — OAuth-Anmeldedaten gelten serverweit und es gibt eine OAuth-App pro Twenty-Server. -* Solange beide `serverVariables` nicht ausgefüllt sind, zeigt der Einstellungs-Tab pro App den Hinweis "Benötigt Server-Admin" an und der Button "Verbindung hinzufügen" ist deaktiviert. -* `type: 'oauth'` ist derzeit der einzige unterstützte Wert. Der Diskriminator ist vorwärtskompatibel: zukünftige Typen (`'pat'`, `'api-key'`, ...) werden neue Unterkonfigurationsblöcke neben `oauth` hinzufügen. - -Die OAuth-Callback-URL, die Ihr Anbieter auf die Whitelist setzen muss, lautet: - -``` -https:///apps/oauth/callback -``` - - - - - -Innerhalb eines Logikfunktions-Handlers gibt `listConnections({ providerName })` die `ConnectedAccount`-Zeilen dieser App für den angegebenen Anbieter zurück, mit aktualisierten Zugriffstoken. - -```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 }; -}; -``` - -Jede Verbindung hat: - -| Feld | Beschreibung | -| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | -| `id` | Eindeutige Zeilen-ID; an `getConnection(id)` übergeben, um eine einzelne Verbindung erneut abzurufen | -| `sichtbarkeit` | `'user'` (privat für ein Mitglied des Arbeitsbereichs) oder `'workspace'` (mit allen Mitgliedern geteilt) | -| `geltungsbereiche` | Vom Upstream-Anbieter gewährte OAuth-Berechtigungen (unabhängig von `visibility` — diese sind nicht miteinander verknüpft) | -| `userWorkspaceId` | Die userWorkspace-ID des Eigentümers — nützlich, um "die Verbindung des anfragenden Benutzers" in HTTP-Routen-Triggern auszuwählen | -| `accessToken` | Frisches OAuth-Zugriffstoken (wird bei Ablauf automatisch erneuert) | -| `name` / `handle` | Anzeigename der Verbindung (automatisch beim OAuth-Callback abgeleitet, vom Benutzer umbenennbar) | -| `authFailedAt` | Gesetzt, wenn die jüngste Aktualisierung fehlgeschlagen ist; der Benutzer muss die Verbindung erneut herstellen | - -Hauptpunkte: - -* Übergeben Sie `{ providerName }`, um nach Anbieter zu filtern; lassen Sie es weg, um alle Verbindungen dieser App über alle Anbieter hinweg zu erhalten. -* Der Server aktualisiert das Zugriffstoken vor der Rückgabe transparent. Ihr Handler sieht stets ein verwendbares Token (oder `authFailedAt` ist gesetzt). -* `getConnection(id)` ist das Pendant für eine einzelne Zeile. - - - - - -Wenn ein Benutzer auf "Verbindung hinzufügen" klickt, wird er aufgefordert, eine Sichtbarkeit auszuwählen: - -* **Nur für mich** — die Anmeldedaten sind für den sich verbindenden Benutzer privat. Jede Logikfunktion, die in seinem/ihrem Auftrag aufgerufen wird (HTTP-Routen-Trigger mit `isAuthRequired: true`), sieht sie; Cron-Trigger und Datenbankereignisse nicht. -* **Im Arbeitsbereich geteilt** — jedes Arbeitsbereichsmitglied kann die Anmeldedaten verwenden. Cron-/Datenbank-Trigger sehen sie ebenfalls, da sie keinen anfragenden Benutzer haben. - -Verwenden Sie für jeden Handler die richtige Option: - -```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'); -``` - -Mehrere Verbindungen pro (Benutzer, Anbieter) sind erlaubt, sodass derselbe Benutzer "Persönliches Linear" und "Arbeits-Linear" nebeneinander haben kann. - - - - - -Für jeden Verbindungsanbieter muss der Serveradministrator zunächst eine OAuth-App beim Drittanbieter registrieren. - -1. Gehen Sie zu den Entwickler-Einstellungen des Anbieters (z. B. https://linear.app/settings/api/applications/new). -2. Setzen Sie die **Redirect-URI** auf `\/apps/oauth/callback`. -3. Kopieren Sie die generierte **Client ID** und das **Client Secret**. -4. Öffnen Sie die installierte App in Twenty als Serveradministrator → setzen Sie die Werte in den entsprechenden `serverVariables`. -5. Mitglieder des Arbeitsbereichs können dann Verbindungen im **Verbindungen**-Abschnitt der jeweiligen App hinzufügen. - - - - diff --git a/packages/twenty-docs/l/de/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/de/developers/extend/apps/data-model.mdx deleted file mode 100644 index e0d2c080d0..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,493 +0,0 @@ ---- -title: Datenmodell -description: Definieren Sie Objekte, Felder, Rollen und Anwendungsmetadaten mit dem Twenty SDK. -icon: database ---- - -Das Paket `twenty-sdk` stellt `defineEntity`-Funktionen bereit, um das Datenmodell Ihrer App zu deklarieren. Sie müssen `export default defineEntity({...})` verwenden, damit das SDK Ihre Entitäten erkennt. Diese Funktionen validieren Ihre Konfiguration zur Build-Zeit und bieten IDE-Autovervollständigung sowie Typsicherheit. - - - **Die Dateiorganisation liegt bei Ihnen.** - Die Entitätserkennung ist AST-basiert — das SDK findet Aufrufe von `export default defineEntity(...)`, unabhängig davon, wo sich die Datei befindet. Das Gruppieren von Dateien nach Typ (z. B. `logic-functions/`, `roles/`) ist lediglich eine Konvention, keine Voraussetzung. - - - - - -Rollen kapseln Berechtigungen für die Objekte und Aktionen Ihres Workspaces. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -Jede App muss genau einen Aufruf von `defineApplication` haben, der Folgendes beschreibt: - -* **Identität**: Bezeichner, Anzeigename und Beschreibung. -* **Berechtigungen**: welche Rolle ihre Funktionen und Frontend-Komponenten verwenden. -* **(Optional) Variablen**: Schlüssel–Wert-Paare, die Ihren Funktionen als Umgebungsvariablen zur Verfügung gestellt werden. -* **(Optional) Pre-/Post-Installationsfunktionen**: Logikfunktionen, die vor oder nach der Installation ausgeführt werden. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -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, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Notizen: -* `universalIdentifier`-Felder sind deterministische IDs, die Ihnen gehören. Erzeugen Sie sie einmal und halten Sie sie über Synchronisierungen hinweg stabil. -* `applicationVariables` werden zu Umgebungsvariablen für Ihre Funktionen und Frontend-Komponenten (z. B. ist `DEFAULT_RECIPIENT_NAME` als `process.env.DEFAULT_RECIPIENT_NAME` verfügbar). -* `defaultRoleUniversalIdentifier` muss auf eine mit `defineRole()` definierte Rolle verweisen (siehe oben). -* Pre- und Post-Installationsfunktionen werden während des Manifest-Builds automatisch erkannt — Sie müssen sie in `defineApplication()` nicht referenzieren. - -#### Marktplatz-Metadaten - -Wenn Sie planen, [Ihre App zu veröffentlichen](/l/de/developers/extend/apps/publishing), steuern diese optionalen Felder, wie Ihre App im Marktplatz erscheint: - -| Feld | Beschreibung | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------- | -| `author` | Name des Autors oder des Unternehmens | -| `category` | App-Kategorie für die Filterung im Marktplatz | -| `logoUrl` | Pfad zu Ihrem App-Logo (z. B. `public/logo.png`) | -| `screenshots` | Array von Screenshot-Pfaden (z. B. `public/screenshot-1.png`) | -| `aboutDescription` | Längere Markdown-Beschreibung für den Tab "Info". Wenn weggelassen, verwendet der Marktplatz die `README.md` des Pakets von npm | -| `websiteUrl` | Link zu Ihrer Website | -| `termsUrl` | Link zu den Nutzungsbedingungen | -| `emailSupport` | Support-E-Mail-Adresse | -| `issueReportUrl` | Link zum Issue-Tracker | - -#### Rollen und Berechtigungen - -Das Feld `defaultRoleUniversalIdentifier` in `application-config.ts` legt die Standardrolle fest, die von den Logikfunktionen und Frontend-Komponenten Ihrer App verwendet wird. Details finden Sie oben unter `defineRole`. - -* Das zur Laufzeit als `TWENTY_APP_ACCESS_TOKEN` injizierte Token wird aus dieser Rolle abgeleitet. -* Der typisierte Client ist auf die dieser Rolle gewährten Berechtigungen beschränkt. -* Befolgen Sie das Least-Privilege-Prinzip: Erstellen Sie eine dedizierte Rolle nur mit den Berechtigungen, die Ihre Funktionen benötigen. - -##### Standard-Funktionsrolle - -Wenn Sie eine neue App erzeugen, erstellt die CLI eine Standard-Rolldatei: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -Der `universalIdentifier` dieser Rolle wird in `application-config.ts` als `defaultRoleUniversalIdentifier` referenziert: - -* **\*.role.ts** definiert, was die Rolle darf. -* **application-config.ts** verweist auf diese Rolle, sodass Ihre Funktionen deren Berechtigungen erben. - -Notizen: -* Beginnen Sie mit der vorab erstellten Rolle und schränken Sie sie schrittweise gemäß dem Least-Privilege-Prinzip ein. -* Ersetzen Sie `objectPermissions` und `fieldPermissions` durch die Objekte und Felder, die Ihre Funktionen tatsächlich benötigen. -* `permissionFlags` steuern den Zugriff auf Funktionen auf Plattformebene. Halten Sie sie minimal. -* Ein funktionierendes Beispiel finden Sie unter: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Benutzerdefinierte Objekte beschreiben sowohl Schema als auch Verhalten für Datensätze in Ihrem Workspace. Verwenden Sie `defineObject()`, um Objekte mit eingebauter Validierung zu definieren: - -```ts postCard.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, - }, - ], -}); -``` - -Hauptpunkte: - -* Verwenden Sie `defineObject()` für eingebaute Validierung und bessere IDE-Unterstützung. -* Der `universalIdentifier` muss eindeutig und über Deployments hinweg stabil sein. -* Jedes Feld benötigt `name`, `type`, `label` und einen eigenen stabilen `universalIdentifier`. -* Das Array `fields` ist optional — Sie können Objekte ohne benutzerdefinierte Felder definieren. -* Sie können mit `yarn twenty add` neue Objekte erzeugen; der Assistent führt Sie durch Benennung, Felder und Beziehungen. - - -**Basisfelder werden automatisch erstellt.** Wenn Sie ein benutzerdefiniertes Objekt definieren, fügt Twenty automatisch Standardfelder hinzu -wie `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` und `deletedAt`. -Sie müssen diese nicht in Ihrem `fields`-Array definieren — fügen Sie nur Ihre benutzerdefinierten Felder hinzu. -Sie können Standardfelder überschreiben, indem Sie in Ihrem `fields`-Array ein Feld mit demselben Namen definieren, -dies wird jedoch nicht empfohlen. - - - - - -Verwenden Sie `defineField()`, um Objekten, die Ihnen nicht gehören — etwa Standardobjekten von Twenty (Person, Company usw.) — Felder hinzuzufügen oder Objekten aus anderen Apps. Im Gegensatz zu Inline-Feldern in `defineObject()` benötigen eigenständige Felder einen `objectUniversalIdentifier`, um anzugeben, welches Objekt sie erweitern: - -```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' }, - ], -}); -``` - -Hauptpunkte: -* Der `objectUniversalIdentifier` identifiziert das Zielobjekt. Für Standardobjekte verwenden Sie `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`, die aus `twenty-sdk` exportiert werden. -* Wenn Sie Felder inline in `defineObject()` definieren, benötigen Sie `objectUniversalIdentifier` **nicht** — er wird vom übergeordneten Objekt geerbt. -* `defineField()` ist die einzige Möglichkeit, Felder zu Objekten hinzuzufügen, die Sie nicht mit `defineObject()` erstellt haben. - - - - -Relationen verbinden Objekte miteinander. In Twenty sind Relationen stets **bidirektional** — Sie definieren beide Seiten, und jede Seite referenziert die andere. - -Es gibt zwei Relationstypen: - -| Beziehungstyp | Beschreibung | Fremdschlüssel vorhanden? | -| ------------- | ----------------------------------------------------------------------- | ------------------------- | -| `MANY_TO_ONE` | Viele Datensätze dieses Objekts verweisen auf einen Datensatz des Ziels | Ja (`joinColumnName`) | -| `ONE_TO_MANY` | Ein Datensatz dieses Objekts hat viele Datensätze des Ziels | Nein (inverse Seite) | - -#### Wie Relationen funktionieren - -Jede Relation erfordert **zwei Felder**, die sich gegenseitig referenzieren: - -1. Die **MANY_TO_ONE**-Seite — befindet sich auf dem Objekt, das den Fremdschlüssel hält -2. Die **ONE_TO_MANY**-Seite — befindet sich auf dem Objekt, dem die Sammlung gehört - -Beide Felder verwenden `FieldType.RELATION` und verweisen über `relationTargetFieldMetadataUniversalIdentifier` gegenseitig aufeinander. - -#### Beispiel: Postkarte hat viele Empfänger - -Angenommen, eine `PostCard` kann an viele `PostCardRecipient`-Datensätze gesendet werden. Jeder Empfänger gehört genau zu einer Postkarte. - -**Schritt 1: Definieren Sie die ONE_TO_MANY-Seite auf PostCard** (die "eine" Seite): - -```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, - }, -}); -``` - -**Schritt 2: Definieren Sie die MANY_TO_ONE-Seite auf PostCardRecipient** (die "viele" Seite — hält den Fremdschlüssel): - -```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', - }, -}); -``` - - -**Zyklische Importe:** Beide Relationsfelder referenzieren gegenseitig den `universalIdentifier` des jeweils anderen. Um Probleme mit zyklischen Importen zu vermeiden, exportieren Sie Ihre Feld-IDs als benannte Konstanten aus jeder Datei und importieren Sie sie in der jeweils anderen Datei. Das Build-System löst dies zur Kompilierzeit auf. - - -#### Relationen zu Standardobjekten - -Um eine Relation mit einem integrierten Twenty-Objekt (Person, Company usw.) zu erstellen, verwenden Sie `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', - }, -}); -``` - -#### Eigenschaften von Relationsfeldern - -| Eigenschaft | Erforderlich | Beschreibung | -| ------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------- | -| `type` | Ja | Muss `FieldType.RELATION` sein | -| `relationTargetObjectMetadataUniversalIdentifier` | Ja | Der `universalIdentifier` des Zielobjekts | -| `relationTargetFieldMetadataUniversalIdentifier` | Ja | Der `universalIdentifier` des entsprechenden Felds auf dem Zielobjekt | -| `universalSettings.relationType` | Ja | `RelationType.MANY_TO_ONE` oder `RelationType.ONE_TO_MANY` | -| `universalSettings.onDelete` | Nur für MANY_TO_ONE | Was passiert, wenn der referenzierte Datensatz gelöscht wird: `CASCADE`, `SET_NULL`, `RESTRICT` oder `NO_ACTION` | -| `universalSettings.joinColumnName` | Nur für MANY_TO_ONE | Datenbankspaltenname für den Fremdschlüssel (z. B. `postCardId`) | - -#### Inline-Relationsfelder in defineObject - -Sie können Relationsfelder auch direkt innerhalb von `defineObject()` definieren. In diesem Fall lassen Sie `objectUniversalIdentifier` weg — er wird vom übergeordneten Objekt geerbt: - -```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 - ], -}); -``` - - - -## Entitäten mit `yarn twenty add` erstellen - -Anstatt Entitätsdateien manuell zu erstellen, können Sie den interaktiven Scaffolder verwenden: - -```bash filename="Terminal" -yarn twenty add -``` - -Dies fordert Sie auf, einen Entitätstyp auszuwählen, und führt Sie durch die erforderlichen Felder. Er erzeugt eine einsatzbereite Datei mit einem stabilen `universalIdentifier` und dem korrekten `defineEntity()`-Aufruf. - -Sie können den Entitätstyp auch direkt übergeben, um die erste Eingabeaufforderung zu überspringen: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Verfügbare Entitätstypen - -| Entitätstyp | Befehl | Generierte Datei | -| ---------------------- | ------------------------------------ | ------------------------------------------------------- | -| Objekt | `yarn twenty add object` | `src/objects/\.ts` | -| Feld | `yarn twenty add field` | `src/fields/\.ts` | -| Logikfunktion | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Frontend-Komponente | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Rolle | `yarn twenty add role` | `src/roles/\.ts` | -| Skill | `yarn twenty add skill` | `src/skills/\.ts` | -| Agent | `yarn twenty add agent` | `src/agents/\.ts` | -| Ansicht | `yarn twenty add view` | `src/views/\.ts` | -| Navigationsmenüeintrag | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Seitenlayout | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### Was der Scaffolder generiert - -Jeder Entitätstyp hat seine eigene Vorlage. Zum Beispiel fragt `yarn twenty add object` nach: - -1. **Name (Singular)** — z. B. `invoice` -2. **Name (Plural)** — z. B. `invoices` -3. **Label (Singular)** — automatisch aus dem Namen befüllt (z. B. `Invoice`) -4. **Label (Plural)** — automatisch befüllt (z. B. `Invoices`) -5. **Ansicht und Navigationseintrag erstellen?** — wenn Sie mit Ja antworten, erzeugt der Scaffolder außerdem eine passende Ansicht und einen Sidebar-Link für das neue Objekt. - -Andere Entitätstypen haben einfachere Eingabeaufforderungen — die meisten fragen nur nach einem Namen. - -Der Entitätstyp `field` ist detaillierter: Er fragt nach Feldname, Label, Typ (aus einer Liste aller verfügbaren Feldtypen wie `TEXT`, `NUMBER`, `SELECT`, `RELATION` usw.) sowie dem `universalIdentifier` des Zielobjekts. - -### Benutzerdefinierter Ausgabepfad - -Verwenden Sie den Schalter `--path`, um die generierte Datei an einem benutzerdefinierten Ort abzulegen: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/de/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/de/developers/extend/apps/front-components.mdx deleted file mode 100644 index 891b547a9c..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: Frontend-Komponenten -description: Erstellen Sie React-Komponenten, die innerhalb der Twenty-UI gerendert werden und durch eine Sandbox isoliert sind. -icon: window-maximize ---- - -Front-Komponenten sind React-Komponenten, die direkt innerhalb der Twenty-UI gerendert werden. Sie laufen in einem **isolierten Web Worker** unter Verwendung von Remote DOM — Ihr Code wird in einer Sandbox ausgeführt, rendert jedoch nativ auf der Seite, nicht in einem iframe. - -## Wo Front-Komponenten verwendet werden können - -Front-Komponenten können an zwei Stellen innerhalb von Twenty gerendert werden: - -* **Seitenpanel** — Nicht-Headless-Front-Komponenten werden im rechten Seitenpanel geöffnet. Dies ist das Standardverhalten, wenn eine Front-Komponente über das Befehlsmenü ausgelöst wird. -* **Widgets (Dashboards und Datensatzseiten)** — Front-Komponenten können als Widgets in Seitenlayouts eingebettet werden. Beim Konfigurieren eines Dashboards oder eines Datensatzseiten-Layouts können Benutzer ein Front-Komponenten-Widget hinzufügen. - -## Einfaches Beispiel - -Der schnellste Weg, eine Front-Komponente in Aktion zu sehen, ist, sie als **Befehlsmenüeintrag** zu registrieren. Verwende `defineCommandMenuItem` in einer separaten Datei, damit die Komponente als Schnellaktionsschaltfläche oben rechts auf der Seite erscheint: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -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', -}); -``` - -Nach dem Synchronisieren mit `yarn twenty dev` (oder durch einmaliges Ausführen von `yarn twenty dev --once`) erscheint die Schnellaktion oben rechts auf der Seite: - -
- Schnellaktionsschaltfläche oben rechts -
- -Klicken Sie darauf, um die Komponente inline zu rendern. - -## Konfigurationsfelder - -| Feld | Erforderlich | Beschreibung | -| --------------------- | ------------ | --------------------------------------------------------------------------- | -| `universalIdentifier` | Ja | Stabile eindeutige ID für diese Komponente | -| `component` | Ja | Eine React-Komponentenfunktion | -| `name` | Nein | Anzeigename | -| `description` | Nein | Beschreibung dessen, was die Komponente macht | -| `isHeadless` | Nein | Auf `true` setzen, wenn die Komponente keine sichtbare UI hat (siehe unten) | - -## Eine Front-Komponente auf einer Seite platzieren - -Über Befehle hinaus können Sie eine Front-Komponente direkt in eine Datensatzseite einbetten, indem Sie sie als Widget in einem **Seitenlayout** hinzufügen. Details finden Sie im Abschnitt [definePageLayout](/l/de/developers/extend/apps/skills-and-agents#definepagelayout). - -## Headless vs. Nicht-Headless - -Front-Komponenten gibt es in zwei Rendering-Modi, die durch die Option `isHeadless` gesteuert werden: - -**Nicht-Headless (Standard)** — Die Komponente rendert eine sichtbare UI. Wird sie über das Befehlsmenü ausgelöst, öffnet sie sich im Seitenpanel. Dies ist das Standardverhalten, wenn `isHeadless` `false` ist oder weggelassen wird. - -**Headless (`isHeadless: true`)** — Die Komponente wird unsichtbar im Hintergrund gemountet. Sie öffnet das Seitenpanel nicht. Headless-Komponenten sind für Aktionen konzipiert, die Logik ausführen und sich anschließend selbst unmounten — zum Beispiel das Ausführen einer asynchronen Aufgabe, das Navigieren zu einer Seite oder das Anzeigen eines Bestätigungsdialogs. Sie lassen sich gut mit den unten beschriebenen SDK-Command-Komponenten kombinieren. - -```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, -}); -``` - -Da die Komponente `null` zurückgibt, überspringt Twenty das Rendern eines Containers dafür — im Layout entsteht kein Leerraum. Die Komponente hat dennoch Zugriff auf alle Hooks und die Host-Kommunikations-API. - -## SDK-Command-Komponenten - -Das Paket `twenty-sdk` stellt vier Command-Hilfskomponenten bereit, die für Headless-Front-Komponenten ausgelegt sind. Jede Komponente führt beim Mounten eine Aktion aus, behandelt Fehler durch Anzeige einer Snackbar-Benachrichtigung und unmountet die Front-Komponente nach Abschluss automatisch. - -Importieren Sie sie aus `twenty-sdk/command`: - -* **`Command`** — Führt einen asynchronen Callback über das Prop `execute` aus. -* **`CommandLink`** — Navigiert zu einem App-Pfad. Props: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — Öffnet einen Bestätigungsdialog. Bestätigt der Benutzer, wird der Callback `execute` ausgeführt. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — Öffnet eine bestimmte Seite im Seitenpanel. Props: `page`, `pageTitle`, `pageIcon`. - -Hier ist ein vollständiges Beispiel einer Headless-Front-Komponente, die `Command` verwendet, um eine Aktion aus dem Befehlsmenü auszuführen: - -```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 ; -}; - -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', -}); -``` - -Und ein Beispiel, das `CommandModal` verwendet, um vor der Ausführung um Bestätigung zu bitten: - -```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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, -}); -``` - -## Zugriff auf den Laufzeitkontext - -Verwenden Sie innerhalb Ihrer Komponente SDK-Hooks, um auf den aktuellen Benutzer, den Datensatz und die Komponenteninstanz zuzugreifen: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Verfügbare Hooks: - -| Hook | Gibt zurück | Beschreibung | -| --------------------------------------------- | -------------------- | --------------------------------------------------------------------------- | -| `useUserId()` | `string` oder `null` | Die ID des aktuellen Benutzers | -| `useSelectedRecordIds()` | `Zeichenkette[]` | Alle ausgewählten Datensatz-IDs (leeres Array, wenn keine ausgewählt sind) | -| `useRecordId()` | `string` oder `null` | **Veraltet.** Verwenden Sie stattdessen `useSelectedRecordIds()` | -| `useFrontComponentId()` | `string` | Die ID dieser Komponenteninstanz | -| `useFrontComponentExecutionContext(selector)` | variiert | Zugriff auf den vollständigen Ausführungskontext mit einer Selektorfunktion | - -## Host-Kommunikations-API - -Front-Komponenten können Navigation, Modals und Benachrichtigungen mittels Funktionen aus `twenty-sdk` auslösen: - -| Funktion | Beschreibung | -| ----------------------------------------------- | ----------------------------------------- | -| `navigate(to, params?, queryParams?, options?)` | Zu einer Seite in der App navigieren | -| `openSidePanelPage(params)` | Ein Seitenpanel öffnen | -| `closeSidePanel()` | Seitenpanel schließen | -| `openCommandConfirmationModal(params)` | Einen Bestätigungsdialog anzeigen | -| `enqueueSnackbar(params)` | Eine Toast-Benachrichtigung anzeigen | -| `unmountFrontComponent()` | Die Komponente entfernen | -| `updateProgress(progress)` | Einen Fortschrittsindikator aktualisieren | - -Hier ist ein Beispiel, das die Host-API verwendet, um nach Abschluss einer Aktion eine Snackbar anzuzeigen und das Seitenpanel zu schließen: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -### Mit mehreren Datensätzen arbeiten - -Verwenden Sie `useSelectedRecordIds()`, um mehrere ausgewählte Datensätze zu verwalten. Dies ist nützlich für Stapelvorgänge: - -```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 ( -
-

Export {selectedRecordIds.length} selected record(s)?

- -
- ); -}; - -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, - }, -}); -``` - -## defineCommandMenuItem - -Verwende `defineCommandMenuItem`, um eine Front-Komponente im Befehlsmenü (Cmd+K) zu registrieren. Wenn `isPinned` `true` ist, erscheint sie außerdem als Schnellaktionsschaltfläche oben rechts auf der Seite. - -```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', -}); -``` - -| Feld | Erforderlich | Beschreibung | -| --------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `universalIdentifier` | Ja | Stabile eindeutige ID für den Befehl | -| `label` | Ja | Vollständiges Label, das im Befehlsmenü (Cmd+K) angezeigt wird | -| `frontComponentUniversalIdentifier` | Ja | Der `universalIdentifier` der Front-Komponente, die dieser Befehl öffnet | -| `shortLabel` | Nein | Kürzeres Label, das auf der angehefteten Schnellaktionsschaltfläche angezeigt wird | -| `icon` | Nein | Neben dem Label angezeigter Icon-Name (z. B. 'IconBolt', 'IconSend') | -| `isPinned` | Nein | Bei `true` wird der Befehl als Schnellaktionsschaltfläche oben rechts auf der Seite angezeigt | -| `availabilityType` | Nein | Steuert, wo der Befehl erscheint: 'GLOBAL' (immer verfügbar), 'RECORD_SELECTION' (nur wenn Datensätze ausgewählt sind) oder 'FALLBACK' (wird angezeigt, wenn keine anderen Befehle passen) | -| `availabilityObjectUniversalIdentifier` | Nein | Beschränken Sie den Befehl auf Seiten eines bestimmten Objekttyps (z. B. nur bei Company-Datensätzen) | -| `conditionalAvailabilityExpression` | Nein | Ein boolescher Ausdruck, um dynamisch zu steuern, ob der Befehl sichtbar ist (siehe unten) | - -## Bedingte Verfügbarkeitsausdrücke - -Mit dem Feld `conditionalAvailabilityExpression` können Sie basierend auf dem aktuellen Seitenkontext steuern, wann ein Befehl sichtbar ist. Importieren Sie typisierte Variablen und Operatoren aus `twenty-sdk`, um Ausdrücke zu erstellen: - -```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, - ), -}); -``` - -**Kontextvariablen** — sie repräsentieren den aktuellen Zustand der Seite: - -| Variable | Typ | Beschreibung | -| ------------------------------ | --------- | --------------------------------------------------------------- | -| `pageType` | `string` | Aktueller Seitentyp (z. B. 'RecordIndexPage', 'RecordShowPage') | -| `isInSidePanel` | `boolean` | Ob die Komponente in einem Seitenpanel gerendert wird | -| `numberOfSelectedRecords` | `number` | Anzahl der aktuell ausgewählten Datensätze | -| `isSelectAll` | `boolean` | Ob „Alle auswählen“ aktiv ist | -| `selectedRecords` | `array` | Die ausgewählten Datensatzobjekte | -| `favoriteRecordIds` | `array` | IDs der favorisierten Datensätze | -| `objectPermissions` | `object` | Berechtigungen für den aktuellen Objekttyp | -| `targetObjectReadPermissions` | `object` | Leseberechtigungen für das Zielobjekt | -| `targetObjectWritePermissions` | `object` | Schreibberechtigungen für das Zielobjekt | -| `featureFlags` | `object` | Aktive Feature-Flags | -| `objectMetadataItem` | `object` | Metadaten des aktuellen Objekttyps | -| `hasAnySoftDeleteFilterOnView` | `boolean` | Ob die aktuelle Ansicht einen Soft-Delete-Filter hat | - -**Operatoren** — Variablen zu booleschen Ausdrücken kombinieren: - -| Operator | Beschreibung | -| ----------------------------------- | ------------------------------------------------------------------------------------------- | -| `isDefined(value)` | `true`, wenn der Wert nicht null/undefined ist | -| `isNonEmptyString(value)` | `true`, wenn der Wert eine nicht leere Zeichenfolge ist | -| `includes(array, value)` | `true`, wenn das Array den Wert enthält | -| `includesEvery(array, prop, value)` | `true`, wenn die Eigenschaft jedes Elements den Wert enthält | -| `every(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element truthy ist | -| `everyDefined(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element definiert ist | -| `everyEquals(array, prop, value)` | `true`, wenn die Eigenschaft bei jedem Element dem Wert entspricht | -| `some(array, prop)` | `true`, wenn die Eigenschaft bei mindestens einem Element truthy ist | -| `someDefined(array, prop)` | `true`, wenn die Eigenschaft bei mindestens einem Element definiert ist | -| `someEquals(array, prop, value)` | `true`, wenn die Eigenschaft bei mindestens einem Element dem Wert entspricht | -| `someNonEmptyString(array, prop)` | `true`, wenn die Eigenschaft bei mindestens einem Element eine nicht leere Zeichenfolge ist | -| `none(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element falsy ist | -| `noneDefined(array, prop)` | `true`, wenn die Eigenschaft bei jedem Element undefined ist | -| `noneEquals(array, prop, value)` | `true`, wenn die Eigenschaft bei keinem Element dem Wert entspricht | - -## Öffentliche Assets - -Front-Komponenten können mit `getPublicAssetUrl` auf Dateien aus dem `public/`-Verzeichnis der App zugreifen: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -Details finden Sie im Abschnitt [Öffentliche Assets](/l/de/developers/extend/apps/cli-and-testing#public-assets-public-folder). - -## Styling - -Front-Komponenten unterstützen mehrere Styling-Ansätze. Sie können verwenden: - -* **Inline-Styles** — `style={{ color: 'red' }}` -* **Twenty-UI-Komponenten** — Import aus `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar und mehr) -* **Emotion** — CSS-in-JS mit `@emotion/react` -* **Styled-components** — `styled.div`-Muster -* **Tailwind CSS** — Utility-Klassen -* **Beliebige CSS-in-JS-Bibliothek**, die mit React kompatibel ist - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/de/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/de/developers/extend/apps/getting-started.mdx deleted file mode 100644 index dd2b70bb9a..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Erste Schritte -icon: rocket -description: Erstellen Sie in wenigen Minuten Ihre erste Twenty-App. ---- - -## Voraussetzungen - -* **Node.js 24+** — [Hier herunterladen](https://nodejs.org/) -* **Yarn 4** — Wird mit Node.js über Corepack mitgeliefert. Aktivieren Sie es: `corepack enable` -* **Docker** — [Hier herunterladen](https://www.docker.com/products/docker-desktop/). Erforderlich, um einen lokalen Twenty-Server auszuführen. Überspringen Sie dies, wenn Twenty bereits anderswo läuft. - -Das Erstellen einer Twenty-App umfasst drei Phasen. Das Scaffolding-Tool fasst sie zu einem einzigen Happy-Path-Befehl zusammen, aber jede Phase ist ein eigenes Konzept — wenn etwas fehlschlägt, hilft Ihnen das Wissen, in welcher Phase Sie sich befinden, zu erkennen, was zu beheben ist. - -| Phase | Was Sie tun | Tool | Ergebnis | -| ----------------------- | ------------------------------------------------------- | ----------------------------- | ---------------------------------------------------- | -| **1. Gerüst erstellen** | Den Quellcode der App erzeugen | `npx create-twenty-app` | Ein TypeScript-Projekt auf der Festplatte | -| **2. Server starten** | Einen Twenty-Server starten, in den synchronisiert wird | Docker + `yarn twenty server` | Eine laufende Twenty-Instanz | -| **3. Synchronisieren** | Ihren Code live mit dem Server synchronisieren | `yarn twenty dev` | Ihre Änderungen erscheinen in der Benutzeroberfläche | - ---- - -## Phase 1 — Projektgerüst erstellen - -Erstellen Sie eine neue App aus der Vorlage: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Sie werden nach einem Namen und einer Beschreibung gefragt — drücken Sie **Enter** für die Standardwerte. Dadurch wird ein TypeScript-Projekt in `my-twenty-app/` erzeugt, mit einer Startdatei `application-config.ts`, einer Standardrolle, einem CI-Workflow und einem Integrationstest. - -**Nach dieser Phase:** Sie haben den Quellcode einer App auf Ihrem Rechner. Es läuft noch nicht — das ist Phase 2. - ---- - -## Phase 2 — Einen lokalen Twenty-Server starten - -Ihre App benötigt einen Twenty-Server, in den sie synchronisieren kann. Der Server ist eine vollständige Twenty-Instanz — UI, GraphQL-API, PostgreSQL — die lokal in Docker läuft. Ihr lokaler Code lädt seine Definitionen auf diesen Server hoch, wodurch sie in der Benutzeroberfläche erscheinen. - -Das Scaffolding-Tool bietet an, einen für Sie zu starten: - -> **Möchten Sie eine lokale Twenty-Instanz einrichten?** - -* **Ja (empfohlen)** — lädt das Docker-Image `twentycrm/twenty-app-dev` herunter und startet es auf Port `2020`. Stellen Sie sicher, dass Docker läuft. -* **Nein** — wählen Sie dies, wenn Sie bereits einen Twenty-Server haben, mit dem Sie sich verbinden möchten. Sie können die Verbindung später mit `yarn twenty remote add` herstellen. - -
- Soll die lokale Instanz gestartet werden? -
- -Sobald der Server läuft, öffnet sich ein Browser zur Anmeldung. Verwenden Sie das vorab eingerichtete Demo-Konto: - -* **E-Mail:** `tim@apple.dev` -* **Passwort:** `tim@apple.dev` - -
- Twenty-Anmeldebildschirm -
- -Klicken Sie auf dem nächsten Bildschirm auf **Authorize** — dadurch erhält die CLI Zugriff auf Ihren Arbeitsbereich. - -
- Twenty-CLI-Autorisierungsbildschirm -
- -Ihr Terminal bestätigt, dass alles eingerichtet ist. - -
- App-Gerüst erfolgreich erstellt -
- -**Nach dieser Phase:** Sie haben einen laufenden Twenty-Server unter [http://localhost:2020](http://localhost:2020), und Ihre CLI ist autorisiert, mit ihm zu synchronisieren. - - -Wenn Docker nicht installiert ist oder nicht läuft, zeigt das Scaffolding-Tool den richtigen Startbefehl für Ihr Betriebssystem an. Sobald Docker läuft, können Sie mit `yarn twenty server start` fortfahren — ein erneutes Scaffolding ist nicht nötig. - - ---- - -## Phase 3 — Ihre Änderungen synchronisieren - -Das ist die innere Schleife, in der Sie die meiste Zeit verbringen werden. - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Dies überwacht `src/`, baut bei jeder Änderung neu und synchronisiert das Ergebnis mit dem Server. Bearbeiten Sie eine Datei, speichern Sie, und innerhalb einer Sekunde spiegelt der Server die Änderung wider. Sie sehen eine Live-Statusanzeige in Ihrem Terminal. - -Für ausführlichere Ausgaben (Build-Protokolle, Sync-Anfragen, Fehlerspuren) fügen Sie `--verbose` hinzu. - -
- Terminalausgabe im Dev-Modus -
- -Öffnen Sie [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Unter **Your Apps** sollte Ihre App angezeigt werden. - -
- Liste "Your Apps", die "My twenty app" anzeigt -
- -Klicken Sie auf **My twenty app**, um die **Anwendungsregistrierung** anzuzeigen — ein serverseitiger Datensatz, der Ihre App beschreibt (Name, Bezeichner, OAuth-Anmeldedaten, Quelle). Eine Registrierung kann in mehreren Arbeitsbereichen auf demselben Server installiert werden. - -
- Details der Anwendungsregistrierung -
- -Klicken Sie auf **View installed app**, um die Installation im Arbeitsbereich anzuzeigen. Die Registerkarte **About** zeigt die Version und Verwaltungsoptionen. - -
- Installierte App -
- -**Nach dieser Phase:** Sie haben eine Live-Entwicklungsschleife. Bearbeiten Sie eine beliebige Datei in `src/`, und sie erscheint in der Benutzeroberfläche. - -### Einmalige Synchronisierung für CI und Skripte - -Verwenden Sie `--once`, um einen einzelnen Build + Sync auszuführen und zu beenden — gleiche Pipeline, kein Watcher: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Befehl | Verhalten | Wann verwenden | -| ------------------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------- | -| `yarn twenty dev` | Überwacht und synchronisiert bei jeder Änderung erneut. Läuft, bis Sie es stoppen. | Interaktive lokale Entwicklung. | -| `yarn twenty dev --once` | Einmaliger Build + Sync, beendet sich mit `0` bei Erfolg, mit `1` bei Fehler. | CI, Pre-Commit-Hooks, KI-Agenten, skriptgesteuerte Workflows. | - -Beide Modi benötigen einen Server im Entwicklungsmodus und eine authentifizierte Remote-Verbindung. - - -Der Dev-Modus ist nur auf Twenty-Instanzen verfügbar, die im Entwicklungsmodus laufen (`NODE_ENV=development`). Produktionsinstanzen lehnen Dev-Sync-Anfragen ab — verwenden Sie `yarn twenty deploy`, um auf Produktionsserver bereitzustellen. Siehe [Apps veröffentlichen](/l/de/developers/extend/apps/publishing). - - ---- - -## Was Sie erstellen können - -Apps bestehen aus **Entitäten** — jede ist als TypeScript-Datei mit einem einzigen `export default` definiert: - -| Entität | Was sie macht | -| -------------------------- | ------------------------------------------------------------------------------------------------- | -| **Objekte & Felder** | Benutzerdefinierte Datenmodelle (Postkarte, Rechnung usw.) mit typisierten Feldern | -| **Logikfunktionen** | Serverseitiges TypeScript, ausgelöst durch HTTP-Routen, Cron-Zeitpläne oder Datenbankereignisse | -| **Frontend-Komponenten** | React-Komponenten, die in der UI von Twenty gerendert werden (Seitenleiste, Widgets, Befehlsmenü) | -| **Fähigkeiten & Agenten** | KI-Funktionen — wiederverwendbare Anweisungen und autonome Assistenten | -| **Ansichten & Navigation** | Vorkonfigurierte Listenansichten und Seitenleisteneinträge | -| **Seitenlayouts** | Benutzerdefinierte Datensatz-Detailseiten mit Tabs und Widgets | - -Vollständige Referenz: [Apps entwickeln](/l/de/developers/extend/apps/building). - -## Projektstruktur - -```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 -``` - -| Datei / Ordner | Zweck | -| ---------------------------------------- | ------------------------------------------------------------------------------- | -| `src/application-config.ts` | **Erforderlich.** Die Hauptkonfigurationsdatei für Ihre App. | -| `src/default-role.ts` | Standardrolle, die steuert, worauf Ihre Logikfunktionen zugreifen können. | -| `src/constants/universal-identifiers.ts` | Automatisch erzeugte UUIDs und Metadaten (Anzeigename, Beschreibung). | -| `src/__tests__/` | Integrationstests (Setup + Beispieltest). | -| `public/` | Statische Assets (Bilder, Schriftarten), die mit Ihrer App ausgeliefert werden. | - -### Mit einem Beispiel beginnen - -Verwenden Sie `--example`, um mit einem vollständigeren Projekt zu starten (benutzerdefinierte Objekte, Felder, Logikfunktionen, Front-End-Komponenten): - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Die Beispiele befinden sich unter [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). Sie können auch einzelne Entitäten in einem bestehenden Projekt mit `yarn twenty add` erzeugen — siehe [Apps entwickeln](/l/de/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add). - ---- - -## Lokalen Server verwalten - -Verwenden Sie `yarn twenty server`, um den lokalen Twenty-Container zu steuern: - -| Befehl | Was es tut | -| -------------------------------------- | --------------------------------------------------- | -| `yarn twenty server start` | Server starten (lädt das Image bei Bedarf herunter) | -| `yarn twenty server start --port 3030` | Auf einem benutzerdefinierten Port starten | -| `yarn twenty server stop` | Server stoppen (Daten bleiben erhalten) | -| `yarn twenty server status` | URL, Version und Anmeldedaten anzeigen | -| `yarn twenty server logs` | Serverprotokolle streamen | -| `yarn twenty server reset` | Alle Daten löschen und neu starten | -| `yarn twenty server upgrade` | Das neueste `twenty-app-dev`-Image herunterladen | -| `yarn twenty server upgrade 2.2.0` | Auf eine bestimmte Version aktualisieren | - -Daten bleiben über Neustarts hinweg in zwei Docker-Volumes bestehen (`twenty-app-dev-data` für PostgreSQL, `twenty-app-dev-storage` für Dateien). Verwenden Sie `reset`, um alles zu löschen. - -### Aktualisieren des Server-Images - -`yarn twenty server upgrade` lädt das neueste Image herunter, vergleicht die Digests und erstellt den Container nur neu, wenn sich tatsächlich etwas geändert hat. Die Volumes bleiben erhalten — nur der Container wird ersetzt. Wenn ein neues Image heruntergeladen wurde und der Container lief, startet das Upgrade automatisch einen neuen Container; führen Sie anschließend `yarn twenty server start` aus, um zu warten, bis er betriebsbereit ist. - -```bash filename="Terminal" -yarn twenty server upgrade # Latest -yarn twenty server upgrade 2.2.0 # Specific version -``` - -Überprüfen Sie die laufende Version mit `yarn twenty server status` (dies zeigt die im Container enthaltene `APP_VERSION` an). - -### Eine parallele Testinstanz ausführen - -Übergeben Sie `--test` an jeden `server`-Befehl, um eine zweite, vollständig isolierte Instanz zu verwalten — nützlich für Integrationstests oder Experimente, ohne Ihre Hauptentwicklungsdaten anzutasten: - -| Befehl | Was es tut | -| ----------------------------------- | ------------------------------------------------- | -| `yarn twenty server start --test` | Die Testinstanz starten (standardmäßig Port 2021) | -| `yarn twenty server stop --test` | Anhalten | -| `yarn twenty server status --test` | Status anzeigen | -| `yarn twenty server logs --test` | Protokolle streamen | -| `yarn twenty server reset --test` | Daten löschen | -| `yarn twenty server upgrade --test` | Image aktualisieren | - -Die Testinstanz hat ihren eigenen Container (`twenty-app-dev-test`), eigene Volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) und eine eigene Konfiguration — sie läuft parallel zu Ihrer Hauptinstanz ohne Konflikte. Kombinieren Sie `--test` mit `--port`, um den Port 2021 zu überschreiben. - ---- - -## Manuelle Einrichtung (ohne Scaffolder) - -Überspringen Sie das Scaffolding-Tool, wenn Sie das SDK zu einem bestehenden Projekt hinzufügen: - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -Fügen Sie der `package.json` das Skript hinzu: - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -Sie können jetzt `yarn twenty dev`, `yarn twenty server start` und den Rest ausführen. - - -Installieren Sie `twenty-sdk` nicht global — fixieren Sie es pro Projekt, damit jede App ihre eigene Version verwendet. - - ---- - -## Fehlerbehebung - -* **Docker-Fehler** — Stellen Sie sicher, dass Docker Desktop (oder der Daemon) läuft, bevor Sie `yarn twenty server start` ausführen. Die Fehlermeldung zeigt den richtigen Startbefehl für Ihr Betriebssystem an. -* **Falsche Node-Version** — 24+ erforderlich. Prüfen Sie mit `node -v`. -* **Yarn 4 fehlt** — Führen Sie `corepack enable` aus. -* **Abhängigkeiten defekt** — `rm -rf node_modules && yarn install`. - -Hängen Sie fest? Bitten Sie im [Twenty-Discord](https://discord.com/channels/1130383047699738754/1130386664812982322) um Hilfe. diff --git a/packages/twenty-docs/l/de/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/de/developers/extend/apps/layout.mdx deleted file mode 100644 index 817e58dd6a..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Layout -description: Definieren Sie Ansichten, Navigationsmenüeinträge und Seitenlayouts, um das Erscheinungsbild Ihrer App in Twenty zu gestalten. -icon: table-columns ---- - -Layout-Entitäten steuern, wie Ihre App innerhalb der Benutzeroberfläche von Twenty dargestellt wird — was in der Seitenleiste angezeigt wird, welche gespeicherten Ansichten mit der App ausgeliefert werden und wie eine Detailseite eines Datensatzes angeordnet ist. - -## Layout-Konzepte - -| Konzept | Was es steuert | Entität | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -| **Ansicht** | Eine gespeicherte Listen-Konfiguration für ein Objekt — sichtbare Felder, Reihenfolge, Filter, Gruppen | `defineView` | -| **Navigationsmenüeintrag** | Ein Eintrag in der linken Seitenleiste, der auf eine Ansicht oder eine externe URL verweist | `defineNavigationMenuItem` | -| **Seitenlayout** | Die Tabs und Widgets, aus denen die Detailseite eines Datensatzes besteht | `definePageLayout` | -| **Seitenlayout-Registerkarte** | Eine eigenständige Registerkarte, die an ein vorhandenes Seitenlayout angehängt ist (Standard oder das Ihrer eigenen App) | `definePageLayoutTab` | - -Ansichten, Navigationsmenüeinträge und Seitenlayouts verweisen über `universalIdentifier` aufeinander: - -* Ein **Navigationsmenüeintrag** vom Typ `VIEW` verweist auf einen `defineView`-Bezeichner, sodass der Seitenleistenlink diese gespeicherte Ansicht öffnet. -* Ein **Seitenlayout** vom Typ `RECORD_PAGE` zielt auf ein Objekt ab und kann [Frontkomponenten](/l/de/developers/extend/apps/front-components) innerhalb seiner Tabs als Widgets einbetten. - - - - -Ansichten sind gespeicherte Konfigurationen dafür, wie Datensätze eines Objekts angezeigt werden — einschließlich sichtbarer Felder, deren Reihenfolge sowie angewendeter Filter oder Gruppen. Verwenden Sie `defineView()`, um vorkonfigurierte Ansichten mit Ihrer App auszuliefern: - -```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, - }, - ], -}); -``` - -Hauptpunkte: -* `objectUniversalIdentifier` gibt an, auf welches Objekt diese Ansicht angewendet wird. -* `key` bestimmt den Ansichtstyp (z. B. `ViewKey.INDEX` für die Hauptlistenansicht). -* `fields` steuert, welche Spalten erscheinen und in welcher Reihenfolge. Jedes Feld referenziert einen `fieldMetadataUniversalIdentifier`. -* Für erweiterte Konfigurationen können Sie außerdem `filters`, `filterGroups`, `groups` und `fieldGroups` definieren. -* `position` steuert die Reihenfolge, wenn mehrere Ansichten für dasselbe Objekt existieren. - - - - -Navigationsmenüeinträge fügen der Workspace-Seitenleiste benutzerdefinierte Einträge hinzu. Verwenden Sie `defineNavigationMenuItem()`, um auf Ansichten, externe URLs oder Objekte zu verlinken: - -```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, -}); -``` - -Hauptpunkte: -* `type` bestimmt, worauf der Menüeintrag verweist: `NavigationMenuItemType.VIEW` für eine gespeicherte Ansicht oder `NavigationMenuItemType.LINK` für eine externe URL. -* Für Ansichtslinks setzen Sie `viewUniversalIdentifier`. Für externe Links setzen Sie `link`. -* `position` steuert die Reihenfolge in der Seitenleiste. -* `icon` und `color` (optional) passen das Erscheinungsbild an. - - - - -Seitenlayouts ermöglichen es Ihnen, das Aussehen einer Datensatzdetailseite anzupassen — welche Tabs erscheinen, welche Widgets sich in jedem Tab befinden und wie sie angeordnet sind. Verwenden Sie `definePageLayout()`, um benutzerdefinierte Layouts mit Ihrer App auszuliefern: - -```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, - }, - }, - ], - }, - ], -}); -``` - -Hauptpunkte: -* `type` ist typischerweise `'RECORD_PAGE'`, um die Detailansicht eines bestimmten Objekts anzupassen. -* `objectUniversalIdentifier` gibt an, auf welches Objekt dieses Layout angewendet wird. -* Jeder `tab` definiert einen Abschnitt der Seite mit `title`, `position` und `layoutMode` (`CANVAS` für ein freies Layout). -* Jedes `widget` innerhalb eines Tabs kann eine Frontend-Komponente, eine Relationenliste oder andere eingebaute Widget-Typen rendern. -* `position` auf Tabs steuert deren Reihenfolge. Verwenden Sie höhere Werte (z. B. 50), um benutzerdefinierte Tabs hinter den integrierten zu platzieren. - - - - -`definePageLayoutTab` ermöglicht es Ihrer App, eine einzelne Registerkarte — mit optionalen Widgets — an ein **bestehendes** Seitenlayout anzuhängen. Der häufigste Anwendungsfall ist das Hinzufügen einer benutzerdefinierten Registerkarte (z. B. einer Analytics- oder KI-Zusammenfassungs-Registerkarte) zu einer der in Twenty integrierten Datensatzseiten oder zu einem Seitenlayout, das Ihre eigene App bereits mitliefert. - -Das Zielseitenlayout muss entweder ein **Standard**-Seitenlayout von Twenty sein oder eines, das von **Ihrer eigenen App** definiert wird; appübergreifende Verweise auf Seitenlayouts, die einer anderen installierten App gehören, werden derzeit nicht unterstützt. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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, - }, - }, - ], -}); -``` - -Hauptpunkte: -* `pageLayoutUniversalIdentifier` ist **erforderlich** bei der Verwendung von `definePageLayoutTab` und muss auf ein Seitenlayout verweisen, das zum Installationszeitpunkt bereits existiert (Standard oder das Ihrer App). Wenn das übergeordnete Seitenlayout fehlt, schlägt die Installation mit einem eindeutigen Validierungsfehler fehl. -* `widgets` sind ausschließlich auf diese Registerkarte beschränkt — sie verweisen auf Frontend-Komponenten, Ansichten usw., genau wie Widgets, die inline in `definePageLayout` definiert sind. -* `position` steuert die Reihenfolge im Zielseitenlayout relativ zu den vorhandenen Registerkarten. Wählen Sie einen Wert, der Ihre Registerkarte relativ zu integrierten Registerkarten an die gewünschte Position bringt. -* Verwenden Sie dies anstelle von `definePageLayout`, wenn Sie einem vorhandenen Layout nur etwas **hinzufügen** möchten. Verwenden Sie `definePageLayout`, wenn Sie das gesamte Layout besitzen (typischerweise eine `RECORD_PAGE` für ein Objekt, das Sie in Ihrer App ausliefern, oder eine `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/de/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/de/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index 5f463956a3..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,565 +0,0 @@ ---- -title: Logikfunktionen -description: Definieren Sie serverseitige TypeScript-Funktionen mit HTTP-, cron- und Datenbankereignis-Triggern. -icon: bolt ---- - -Logikfunktionen sind serverseitige TypeScript-Funktionen, die auf der Twenty-Plattform ausgeführt werden. Sie können durch HTTP-Anfragen, cron-Zeitpläne oder Datenbankereignisse ausgelöst werden — und außerdem als Tools für KI-Agenten bereitgestellt werden. - - - - -Jede Funktionsdatei verwendet `defineLogicFunction()`, um eine Konfiguration mit einem Handler und optionalen Triggern zu exportieren. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Verfügbare Trigger-Typen: -* **httpRoute**: Stellt Ihre Funktion unter einem HTTP-Pfad und einer Methode **unter dem Endpunkt `/s/`** bereit: -> z. B. `path: '/post-card/create'` ist unter `https://your-twenty-server.com/s/post-card/create` aufrufbar -* **cron**: Führt Ihre Funktion nach Zeitplan mithilfe eines CRON-Ausdrucks aus. -* **databaseEvent**: Wird bei Lebenszyklusereignissen von Workspace-Objekten ausgeführt. Wenn die Ereignisoperation `updated` ist, können bestimmte zu überwachende Felder im Array `updatedFields` angegeben werden. Wenn das Array undefiniert oder leer ist, löst jede Aktualisierung die Funktion aus. -> z. B. `person.updated`, `*.created`, `company.*` - - -Sie können eine Funktion auch manuell über die CLI ausführen: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Sie können Protokolle mit folgendem Befehl ansehen: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Routen-Trigger-Payload - -Wenn ein Route-Trigger Ihre Logikfunktion aufruft, erhält sie ein `RoutePayload`-Objekt, das dem [AWS-HTTP-API-v2-Format](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) folgt. -Importieren Sie den Typ `RoutePayload` aus `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -Der Typ `RoutePayload` hat die folgende Struktur: - - | Eigenschaft | Typ | Beschreibung | Beispiel | - | ---------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | HTTP-Header (nur die in `forwardedRequestHeaders` aufgelisteten) | siehe Abschnitt unten | - | `queryStringParameters` | `Record\` | Query-String-Parameter (mehrere Werte mit Kommas verbunden) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Aus dem Routenmuster extrahierte Pfadparameter | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Geparster Request-Body (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Ursprünglicher UTF-8-Request-Body vor dem JSON-Parsing. Nützlich zur Verifizierung von Webhook-Signaturen im HMAC-Stil (z. B. GitHubs `X-Hub-Signature-256`, Stripe). `undefined`, wenn die Laufzeitumgebung es nicht beibehalten hat. | | - | `isBase64Encoded` | `boolean` | Gibt an, ob der Body Base64-codiert ist | | - | `requestContext.http.method` | `Zeichenkette` | HTTP-Methode (GET, POST, PUT, PATCH, DELETE) | | - | `requestContext.http.path` | `Zeichenkette` | Rohpfad der Anfrage | | - - -#### forwardedRequestHeaders - -Standardmäßig werden HTTP-Header von eingehenden Anfragen aus Sicherheitsgründen nicht an Ihre Logikfunktion weitergegeben. -Um auf bestimmte Header zuzugreifen, listen Sie diese im Array `forwardedRequestHeaders` auf: - -```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'], - }, -}); -``` - -Greifen Sie in Ihrem Handler wie folgt auf die weitergeleiteten Header zu: - -```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 }; -}; -``` - - -Header-Namen werden in Kleinbuchstaben normalisiert. Greifen Sie mit Schlüsseln in Kleinbuchstaben darauf zu (z. B. `event.headers['content-type']`). - - -#### Eine Funktion als KI-Tool oder Workflow-Aktion verfügbar machen - -Logikfunktionen können auf zwei Oberflächen verfügbar gemacht werden, jeweils mit eigenem Trigger: - -* **`toolTriggerSettings`** — macht die Funktion über die KI-Funktionen von Twenty (Chat, MCP, Funktionsaufrufe) auffindbar. Verwendet das standardmäßige JSON Schema, das Format, das LLMs nativ verstehen. -* **`workflowActionTriggerSettings`** — lässt die Funktion als Schritt im visuellen Workflow-Builder erscheinen. Verwendet das umfangreiche `InputSchema` von Twenty, sodass der Builder geeignete Feldeditoren, Variablenauswahlen und Beschriftungen rendern kann. - -Eine Funktion kann sich für eine, die andere oder beide entscheiden. Sie stehen neben `cronTriggerSettings`, `databaseEventTriggerSettings` und `httpRouteTriggerSettings` — gleiches Muster, gleiche Struktur. - -```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: {}, -}); -``` - -Hauptpunkte: - -* Eine Funktion kann Oberflächen mischen — deklarieren Sie sowohl `toolTriggerSettings` als auch `workflowActionTriggerSettings`, um sie im Chat UND im Workflow-Builder bereitzustellen. -* `toolTriggerSettings.inputSchema` und `workflowActionTriggerSettings.inputSchema` sind beide optional. Wenn sie weggelassen werden, leitet der Manifest-Builder sie aus dem Handler-Quellcode ab (JSON Schema für das KI-Tool, das `InputSchema` von Twenty für die Workflow-Aktion). Geben Sie eines explizit an, wenn Sie eine reichere Typisierung wünschen — zum Beispiel mit `FieldMetadataType`-fähigen Feldern wie `CURRENCY` oder `RELATION` für den Workflow-Builder oder mit `description`-Feldern, die der KI-Agent lesen kann: - -```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'], - }, - }, -}); -``` - - -**Schreiben Sie eine gute `description`.** KI-Agenten verlassen sich auf das `description`-Feld der Funktion, um zu entscheiden, wann das Tool verwendet werden soll. Seien Sie konkret darin, was das Tool tut und wann es aufgerufen werden soll. - - - - - -Eine Post-Installationsfunktion ist eine Logikfunktion, die automatisch ausgeführt wird, nachdem Ihre App in einem Arbeitsbereich installiert wurde. Der Server führt sie **nach** der Synchronisierung der Metadaten der App und der Generierung des SDK-Clients aus, sodass der Arbeitsbereich vollständig einsatzbereit ist und das neue Schema bereitsteht. Typische Anwendungsfälle umfassen das Befüllen von Standarddaten, das Erstellen anfänglicher Datensätze, das Konfigurieren von Arbeitsbereichseinstellungen oder das Bereitstellen von Ressourcen bei Diensten von Drittanbietern. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Sie können die Post-Installationsfunktion auch jederzeit manuell über die CLI ausführen: - -```bash filename="Terminal" -yarn twenty exec --postInstall -``` - -Hauptpunkte: -* Post-Installationsfunktionen verwenden `definePostInstallLogicFunction()` — eine spezialisierte Variante, die Trigger-Einstellungen (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`) weglässt. -* Der Handler erhält ein `InstallPayload` mit `{ previousVersion?: string; newVersion: string }` — `newVersion` ist die zu installierende Version, und `previousVersion` ist die zuvor installierte Version (oder `undefined` bei einer Neuinstallation). Verwenden Sie diese Werte, um Neuinstallationen von Upgrades zu unterscheiden und versionsspezifische Migrationslogik auszuführen. -* **Wann der Hook ausgeführt wird**: standardmäßig nur bei Neuinstallationen. Übergeben Sie `shouldRunOnVersionUpgrade: true`, wenn er auch beim Upgrade der App von einer vorherigen Version ausgeführt werden soll. Wenn weggelassen, ist das Flag standardmäßig `false` und Upgrades überspringen den Hook. -* **Ausführungsmodell — standardmäßig asynchron, synchron optional**: Das Flag `shouldRunSynchronously` steuert, *wie* Post-Install ausgeführt wird. - * `shouldRunSynchronously: false` *(Standard)* — der Hook wird **in die Nachrichtenwarteschlange eingereiht** mit `retryLimit: 3` und läuft asynchron in einem Worker. Die Installationsantwort kommt zurück, sobald der Job eingereiht ist, sodass ein langsamer oder fehlschlagender Handler den Aufrufer nicht blockiert. Der Worker versucht es bis zu dreimal erneut. **Verwenden Sie dies für lang laufende Jobs** — das Befüllen großer Datensätze, Aufrufe langsamer Drittanbieter-APIs, Bereitstellung externer Ressourcen, alles, was ein vernünftiges HTTP-Antwortfenster überschreiten könnte. - * `shouldRunSynchronously: true` — der Hook wird **inline während des Installationsablaufs** ausgeführt (gleicher Executor wie bei Pre-Install). Die Installationsanforderung blockiert, bis der Handler fertig ist, und wenn er einen Fehler wirft, erhält der Installationsaufrufer einen `POST_INSTALL_ERROR`. Keine automatischen Wiederholungen. **Verwenden Sie dies für schnelle Aufgaben, die vor der Antwort abgeschlossen sein müssen** — z. B. um dem Benutzer einen Validierungsfehler auszugeben oder für eine schnelle Einrichtung, auf die der Client unmittelbar nach der Rückkehr des Installationsaufrufs angewiesen ist. Beachten Sie, dass die Metadatenmigration bereits angewendet wurde, wenn Post-Install läuft, sodass ein Fehler im Synchronmodus die Schemaänderungen **nicht** rückgängig macht — er zeigt lediglich den Fehler an. -* Stellen Sie sicher, dass Ihr Handler idempotent ist. Im asynchronen Modus kann die Warteschlange bis zu dreimal erneut versuchen; in beiden Modi kann der Hook bei Upgrades erneut laufen, wenn `shouldRunOnVersionUpgrade: true`. -* Die Umgebungsvariablen `APPLICATION_ID`, `APP_ACCESS_TOKEN` und `API_URL` sind im Handler verfügbar (wie bei jeder anderen Logikfunktion), sodass Sie die Twenty API mit einem auf Ihre App beschränkten Anwendungszugriffstoken aufrufen können. -* Pro Anwendung ist nur eine Post-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird. -* Die `universalIdentifier`, `shouldRunOnVersionUpgrade` und `shouldRunSynchronously` der Funktion werden während des Builds automatisch dem Anwendungsmanifest unter dem Feld `postInstallLogicFunction` hinzugefügt — Sie müssen sie in `defineApplication()` nicht referenzieren. -* Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Einrichtungsvorgänge wie Daten-Seeding zu ermöglichen. -* **Nicht im Dev-Modus ausgeführt**: Wenn eine App lokal registriert ist (über `yarn twenty dev`), überspringt der Server den Installationsablauf vollständig und synchronisiert Dateien direkt über den CLI-Watcher — daher läuft Post-Install im Dev-Modus nie, unabhängig von `shouldRunSynchronously`. Verwenden Sie `yarn twenty exec --postInstall`, um es manuell gegen einen laufenden Workspace auszulösen. - - - - -Eine Pre-Install-Funktion ist eine Logikfunktion, die automatisch während der Installation ausgeführt wird, **bevor die Metadatenmigration des Workspaces angewendet wird**. Sie hat die gleiche Payload-Struktur wie Post-Install (`InstallPayload`), ist aber früher im Installationsablauf positioniert, sodass sie Zustände vorbereiten kann, von denen die bevorstehende Migration abhängt — typische Anwendungsfälle sind das Sichern von Daten, die Validierung der Kompatibilität mit dem neuen Schema oder das Archivieren von Datensätzen, die umstrukturiert oder entfernt werden sollen. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Sie können die Pre-Installationsfunktion auch jederzeit manuell über die CLI ausführen: - -```bash filename="Terminal" -yarn twenty exec --preInstall -``` - -Hauptpunkte: -* Pre-Install-Funktionen verwenden `definePreInstallLogicFunction()` — dieselbe spezialisierte Konfiguration wie bei Post-Install, nur an einen anderen Lifecycle-Slot gebunden. -* Sowohl Pre- als auch Post-Install-Handler erhalten denselben `InstallPayload`-Typ: `{ previousVersion?: string; newVersion: string }`. Importieren Sie ihn einmal und verwenden Sie ihn für beide Hooks wieder. -* **Wann der Hook ausgeführt wird**: positioniert direkt vor der Metadatenmigration des Workspaces (`synchronizeFromManifest`). Vor der Ausführung führt der Server einen rein additiven "pared-down sync" durch, der die Pre-Install-Funktion der **neuen** Version in den Workspace-Metadaten registriert — sonst wird nichts angefasst — und führt sie dann aus. Da dieser Sync nur additiv ist, sind die Objekte, Felder und Daten der vorherigen Version noch intakt, wenn Ihr Handler läuft: Sie können den Zustand vor der Migration gefahrlos lesen und sichern. -* **Ausführungsmodell**: Pre-Install wird **synchron** ausgeführt und **blockiert die Installation**. Wenn der Handler einen Fehler wirft, wird die Installation abgebrochen, bevor Schemaänderungen angewendet werden — der Workspace verbleibt in der vorherigen Version in einem konsistenten Zustand. Das ist beabsichtigt: Pre-Install ist Ihre letzte Chance, ein riskantes Upgrade abzulehnen. -* Wie bei Post-Install ist pro Anwendung nur eine Pre-Installationsfunktion zulässig. Sie wird während des Builds automatisch dem Anwendungsmanifest unter `preInstallLogicFunction` hinzugefügt. -* **Nicht im Dev-Modus ausgeführt**: wie bei Post-Install — der Installationsablauf wird für lokal registrierte Apps vollständig übersprungen, daher läuft Pre-Install unter `yarn twenty dev` nie. Verwenden Sie `yarn twenty exec --preInstall`, um es manuell auszulösen. - - - - -Beide Hooks sind Teil desselben Installationsablaufs und erhalten dasselbe `InstallPayload`. Der Unterschied besteht darin, **wann** sie relativ zur Metadatenmigration des Workspaces ausgeführt werden, und das ändert, auf welche Daten sie gefahrlos zugreifen können. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -Pre-Install ist immer **synchron** (blockiert die Installation und kann sie abbrechen). Post-Install ist **standardmäßig asynchron** — in einen Worker eingereiht mit automatischen Wiederholungen — kann aber per `shouldRunSynchronously: true` in die synchrone Ausführung wechseln. Siehe das Akkordeon zu `definePostInstallLogicFunction` oben, wann welcher Modus zu verwenden ist. - -**Verwenden Sie `post-install` für alles, wofür das neue Schema existieren muss.** Dies ist der Regelfall: - -* Standarddaten befüllen (Anlegen anfänglicher Datensätze, Standardansichten, Demo-Inhalte) für neu hinzugefügte Objekte und Felder. -* Registrieren von Webhooks bei Drittanbieter-Diensten, jetzt, da die App ihre Anmeldedaten hat. -* Aufrufen Ihrer eigenen API, um eine Einrichtung abzuschließen, die von den synchronisierten Metadaten abhängt. -* Idempotente "Stelle sicher, dass dies existiert"-Logik, die bei jedem Upgrade den Zustand abgleichen soll — kombinieren Sie dies mit `shouldRunOnVersionUpgrade: true`. - -Beispiel — nach der Installation einen Standard-`PostCard`-Datensatz anlegen: - -```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 => { - 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, -}); -``` - -**Verwenden Sie `pre-install`, wenn eine Migration ansonsten vorhandene Daten löschen oder beschädigen würde.** Da Pre-Install gegen das vorherige Schema läuft und ein Fehlschlag das Upgrade zurückrollt, ist es der richtige Ort für alles Riskante: - -* **Sichern von Daten, die gleich gelöscht oder umstrukturiert werden** — z. B. Sie entfernen in v2 ein Feld und müssen dessen Werte vor der Migration in ein anderes Feld kopieren oder in einen Speicher exportieren. -* **Archivieren von Datensätzen, die eine neue Einschränkung ungültig machen würde** — z. B. ein Feld wird `NOT NULL` und Sie müssen zuerst Zeilen mit Null-Werten löschen oder korrigieren. -* **Kompatibilität validieren und das Upgrade ablehnen, wenn die aktuellen Daten nicht sauber migriert werden können** — werfen Sie im Handler einen Fehler, und die Installation wird ohne Änderungen abgebrochen. Das ist sicherer, als die Inkompatibilität mitten in der Migration zu entdecken. -* **Daten umbenennen oder Schlüssel neu zuweisen** vor einer Schemaänderung, bei der sonst die Zuordnung verloren ginge. - -Beispiel — Datensätze vor einer destruktiven Migration archivieren: - -```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 => { - // 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, -}); -``` - -**Faustregel:** - -| Sie möchten ... | Verwenden | -| ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| Standarddaten befüllen, den Workspace konfigurieren, externe Ressourcen registrieren | `post-install` | -| Lang laufendes Seeding oder Drittanbieteraufrufe ausführen, die die Installationsantwort nicht blockieren sollten | `post-install` (Standard — `shouldRunSynchronously: false`, mit Worker-Wiederholungen) | -| Schnelle Einrichtung ausführen, auf die sich der Aufrufer unmittelbar nach der Rückkehr des Installationsaufrufs verlassen wird | `post-install` mit `shouldRunSynchronously: true` | -| Daten lesen oder sichern, die bei der bevorstehenden Migration verloren gingen | `pre-install` | -| Ein Upgrade ablehnen, das vorhandene Daten beschädigen würde | `pre-install` (`throw` im Handler) | -| Bei jedem Upgrade einen Abgleich ausführen | `post-install` mit `shouldRunOnVersionUpgrade: true` | -| Einmalige Einrichtung nur bei der ersten Installation durchführen | `post-install` mit `shouldRunOnVersionUpgrade: false` (Standard) | - - -Im Zweifel auf **Post-Install** setzen. Greifen Sie nur zu Pre-Install, wenn die Migration selbst destruktiv ist und Sie den vorherigen Zustand abfangen müssen, bevor er verloren geht. - - - - - -## Typisierte API-Clients (twenty-client-sdk) - -Das Paket `twenty-client-sdk` stellt zwei typisierte GraphQL-Clients bereit, um aus Ihren Logikfunktionen und Frontend-Komponenten mit der Twenty-API zu interagieren. - -| Client | Importieren | Endpunkt | Generiert? | -| ------------------- | ---------------------------- | --------------------------------------------------------- | ------------------------------------ | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — Arbeitsbereichsdaten (Datensätze, Objekte) | Ja, zur Entwicklungs-/Build-Zeit | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — Arbeitsbereichskonfiguration, Datei-Uploads | Nein, wird vorgefertigt ausgeliefert | - - - - -Der `CoreApiClient` ist der Haupt-Client zum Abfragen und Ändern von Arbeitsbereichsdaten. Er wird während `yarn twenty dev` oder `yarn twenty build` **aus Ihrem Arbeitsbereichsschema generiert** und ist daher vollständig typisiert, passend zu Ihren Objekten und Feldern. - -```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, - }, -}); -``` - -Der Client verwendet eine Selection-Set-Syntax: Übergeben Sie `true`, um ein Feld einzuschließen, verwenden Sie `__args` für Argumente, und verschachteln Sie Objekte für Relationen. Sie erhalten vollständige Autovervollständigung und Typprüfung basierend auf Ihrem Arbeitsbereichsschema. - - -**Der CoreApiClient wird zur Entwicklungs-/Build-Zeit generiert.** Wenn Sie ihn verwenden, ohne zuvor `yarn twenty dev` oder `yarn twenty build` ausgeführt zu haben, wird ein Fehler ausgelöst. Die Generierung erfolgt automatisch — die CLI inspiziert das GraphQL-Schema Ihres Arbeitsbereichs und erzeugt mit `@genql/cli` einen typisierten Client. - - -#### Verwendung von CoreSchema für Typannotationen - -`CoreSchema` stellt TypeScript-Typen bereit, die Ihren Arbeitsbereichsobjekten entsprechen — nützlich zum Typisieren von Komponentenzustand oder Funktionsparametern: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`MetadataApiClient` ist im SDK bereits vorgefertigt enthalten (keine Generierung erforderlich). Er fragt den Endpunkt `/metadata` nach Arbeitsbereichskonfiguration, Anwendungen und Datei-Uploads ab. - -```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 }, - }, - }, -}); -``` - -#### Dateien hochladen - -Der `MetadataApiClient` enthält eine Methode `uploadFile`, um Dateien an Felder des Typs Datei anzuhängen: - -```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://...' } -``` - -| Parameter | Typ | Beschreibung | -| ---------------------------------- | -------- | --------------------------------------------------------------------- | -| `fileBuffer` | `Buffer` | Der Rohinhalt der Datei | -| `filename` | `string` | Der Name der Datei (wird für Speicherung und Anzeige verwendet) | -| `contentType` | `string` | MIME-Typ (standardmäßig `application/octet-stream`, wenn weggelassen) | -| `fieldMetadataUniversalIdentifier` | `string` | Der `universalIdentifier` des Dateityp-Felds in Ihrem Objekt | - -Hauptpunkte: -* Sie verwendet den `universalIdentifier` des Feldes (nicht dessen arbeitsbereichsspezifische ID), sodass Ihr Upload-Code in jedem Arbeitsbereich funktioniert, in dem Ihre App installiert ist. -* Die zurückgegebene `url` ist eine signierte URL, mit der Sie auf die hochgeladene Datei zugreifen können. - - - - - - Wenn Ihr Code auf Twenty ausgeführt wird (Logikfunktionen oder Frontend-Komponenten), injiziert die Plattform Anmeldedaten als Umgebungsvariablen: - - * `TWENTY_API_URL` — Basis-URL der Twenty-API - * `TWENTY_APP_ACCESS_TOKEN` — Kurzlebiger Schlüssel, der auf die Standard-Funktionsrolle Ihrer Anwendung begrenzt ist - - Sie müssen diese **nicht** an die Clients übergeben — sie lesen automatisch aus `process.env`. Die Berechtigungen des API-Schlüssels werden durch die Rolle bestimmt, auf die in `defaultRoleUniversalIdentifier` in Ihrer `application-config.ts` verwiesen wird. - diff --git a/packages/twenty-docs/l/de/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/de/developers/extend/apps/publishing.mdx deleted file mode 100644 index 0608c57705..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Veröffentlichen -icon: hochladen -description: Veröffentlichen Sie Ihre Twenty-App auf dem Twenty-Marktplatz oder stellen Sie sie intern bereit. ---- - -## Übersicht - -Sobald Ihre App [lokal gebaut und getestet](/l/de/developers/extend/apps/building) wurde, haben Sie zwei Möglichkeiten, sie zu verteilen: - -* **Einen Tarball bereitstellen** — Laden Sie Ihre App direkt auf einen bestimmten Twenty-Server für die interne oder private Nutzung hoch. -* **Auf npm veröffentlichen** — führen Sie Ihre App im Twenty-Marktplatz auf, damit jeder Arbeitsbereich sie entdecken und installieren kann. - -Beide Pfade beginnen mit demselben **Build**-Schritt. - -## Erstellen Ihrer App - -Führen Sie den Build-Befehl aus, um Ihre App zu kompilieren und eine distributionsfertige `manifest.json` zu erzeugen: - -```bash filename="Terminal" -yarn twenty build -``` - -Dabei werden TypeScript-Quelltexte kompiliert, Logikfunktionen und Frontend-Komponenten transpiliert und alles in `.twenty/output/` geschrieben. Fügen Sie `--tarball` hinzu, um zusätzlich ein `.tgz`-Paket für die manuelle Verteilung oder den Deploy-Befehl zu erzeugen. - -## Bereitstellung auf einem Server (Tarball) - -Für Apps, die Sie nicht öffentlich verfügbar machen möchten — proprietäre Tools, ausschließlich für Unternehmen bestimmte Integrationen oder experimentelle Builds — können Sie einen Tarball direkt auf einem Twenty-Server bereitstellen. - -### Voraussetzungen - -Bevor Sie bereitstellen, benötigen Sie ein konfiguriertes Remote, das auf den Zielserver zeigt. Remotes speichern die Server-URL und Anmeldeinformationen lokal in `~/.twenty/config.json`. - -Ein Remote hinzufügen: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Bereitstellen - -Bauen und laden Sie Ihre App in einem Schritt auf den Server hoch: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Eine bereitgestellte App freigeben - - -Das Teilen privater (Tarball-)Apps über Arbeitsbereiche hinweg ist eine **Enterprise**-Funktion. Die Registerkarte **Distribution** zeigt anstelle der Freigabeoptionen eine Aufforderung zum Upgrade an, bis Ihr Arbeitsbereich über einen gültigen Enterprise-Schlüssel verfügt. Gehen Sie zu [Einstellungen > Admin-Panel > Enterprise](/settings/admin-panel#enterprise), um es zu aktivieren. - - -Tarball-Apps werden nicht im öffentlichen Marktplatz gelistet, daher entdecken andere Arbeitsbereiche auf demselben Server sie nicht durch Stöbern. Sobald sich Ihr Arbeitsbereich im Enterprise-Plan befindet, können Sie eine bereitgestellte App wie folgt freigeben: - -1. Gehen Sie zu **Einstellungen > Anwendungen > Registrierungen** und öffnen Sie Ihre App -2. Klicken Sie im Tab **Distribution** auf **Freigabelink kopieren** -3. Teilen Sie diesen Link mit Nutzern in anderen Arbeitsbereichen — er führt sie direkt zur Installationsseite der App - -Der Freigabelink verwendet die Basis-URL des Servers (ohne Workspace-Subdomain), sodass er für jeden Arbeitsbereich auf dem Server funktioniert. - -### Versionsverwaltung - -Beim Aktualisieren einer bereits bereitgestellten Tarball-App verlangt der Server, dass die `version` in `package.json` **strikt höher** (gemäß der [semver](https://semver.org)-Reihenfolge) ist als die derzeit bereitgestellte Version. Das erneute Bereitstellen derselben Version oder das Pushen einer niedrigeren Version wird abgelehnt, bevor das Tarball gespeichert wird — in der CLI wird ein `VERSION_ALREADY_EXISTS`-Fehler angezeigt. - -So veröffentlichen Sie ein Update: - -1. Erhöhen Sie das Feld `version` in Ihrer `package.json` (z. B. `1.2.3` → `1.2.4`, `1.3.0` oder `2.0.0`). -2. Führen Sie `yarn twenty deploy` aus (oder `yarn twenty deploy --remote production`) -3. Arbeitsbereiche, die die App installiert haben, sehen in ihren Einstellungen, dass ein Upgrade verfügbar ist. - - -Pre-Release-Tags funktionieren wie erwartet: Das Erhöhen von `1.0.0-rc.1` → `1.0.0-rc.2` ist zulässig, und eine finale Version wie `1.0.0` wird korrekt als höher als `1.0.0-rc.5` erkannt. Die Version in `package.json` muss selbst eine gültige semver-Zeichenfolge sein. - - -{/* TODO: add screenshot of the Upgrade button */} - -### Kompatibilität der Serverversionen - -Wenn Ihre App eine Funktion verwendet, die in einer bestimmten Twenty-Serverversion eingeführt wurde (z. B. OAuth-Anbieter, die in v2.3.0 hinzugefügt wurden), sollten Sie die minimale Serverversion, die Ihre App benötigt, mithilfe des Felds `engines.twenty` in `package.json` angeben: - -```json filename="package.json" -{ - "name": "twenty-my-app", - "version": "1.0.0", - "engines": { - "node": "^24.5.0", - "twenty": ">=2.3.0" - } -} -``` - -Der Wert ist ein standardmäßiger [semver-Bereich](https://github.com/npm/node-semver#ranges). Häufige Muster: - -| Bereich | Bedeutung | -| ---------------------------------- | ------------------------------------------------------ | -| `>=2.3.0` | Jeder Server ab 2.3.0 | -| `>=2.3.0 \<3.0.0` | 2.3.0 oder höher, aber unter der nächsten Hauptversion | -| `^2.3.0` | Entspricht `>=2.3.0 \<3.0.0` | - -**Was bei Bereitstellung und Installation passiert:** - -* Wenn `engines.twenty` gesetzt ist und die Version des Zielservers den Bereich nicht erfüllt, wird die Bereitstellung (Tarball-Upload) oder Installation mit dem Fehler `SERVER_VERSION_INCOMPATIBLE` abgelehnt, zusammen mit einer Meldung, die sowohl den erforderlichen Bereich als auch die tatsächliche Serverversion angibt. -* Wenn `engines.twenty` nicht gesetzt ist, wird die App auf jeder Serverversion akzeptiert (abwärtskompatibel mit bestehenden Apps). -* Wenn auf dem Server keine `APP_VERSION` konfiguriert ist, wird die Prüfung übersprungen. - - -Der Server ist die maßgebliche Prüfinstanz — er validiert `engines.twenty` sowohl beim Tarball-Upload als auch bei der Workspace-Installation. Auch wenn Sie ein Tarball außerhalb des regulären Prozesses bereitstellen oder aus dem Marketplace installieren, erzwingt der Server weiterhin die Kompatibilität. - - -## Automatisiertes CI/CD (vorgefertigte Workflows) - -Apps, die mit `create-twenty-app` erzeugt wurden, enthalten von Haus aus zwei GitHub-Actions-Workflows unter `.github/workflows/`. Sie sind einsatzbereit, sobald Sie das Repository zu GitHub pushen — für CI ist keine zusätzliche Einrichtung erforderlich, und für CD ist nur ein einziges Secret nötig. - -### CI — `ci.yml` - -Führt Ihre Integrationstests bei jedem Push auf `main` und bei Pull Requests aus. - -**Was sie macht:** - -1. Checkt den Quellcode Ihrer App aus. -2. Startet eine isolierte Twenty-Testinstanz mithilfe der Composite-Action `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (das CI-Äquivalent zu `yarn twenty server start --test`). -3. Aktiviert Corepack, richtet Node.js anhand Ihrer `.nvmrc` ein und installiert Abhängigkeiten mit `yarn install --immutable`. -4. Führt `yarn test` aus und übergibt `TWENTY_API_URL` und `TWENTY_API_KEY` aus der gestarteten Instanz, damit Ihre Tests mit einem echten Server kommunizieren können. - -**Konfigurationsoptionen:** - -* `TWENTY_VERSION` (env, standardmäßig `latest`) — fixieren Sie die in CI verwendete Twenty-Server-Version, indem Sie dies in `ci.yml` anpassen. -* Die Parallelität wird nach `github.ref` gruppiert und bricht laufende Ausführungen bei neuen Pushes ab. - -Es sind keine Secrets erforderlich — die Testinstanz ist flüchtig und existiert nur für die Dauer des Jobs. - -### CD — `cd.yml` - -Stellt Ihre App bei jedem Push auf `main` auf einem konfigurierten Twenty-Server bereit und optional aus einem Pull Request, wenn das Label `deploy` gesetzt ist. - -**Was sie macht:** - -1. Checkt den PR-Head (bei PRs mit Label) oder den gepushten Commit aus. -2. Führt `twentyhq/twenty/.github/actions/deploy-twenty-app@main` aus — das CI-Äquivalent zu `yarn twenty deploy`. -3. Führt `twentyhq/twenty/.github/actions/install-twenty-app@main` aus, damit die neu bereitgestellte Version in den Ziel-Workspace installiert wird. - -**Erforderliche Konfiguration:** - -| Einstellung | Wo | Zweck | -| ----------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `env` in `cd.yml` (standardmäßig `http://localhost:3000`) | Der Twenty-Server, auf den bereitgestellt werden soll. Ändern Sie dies vor der ersten Verwendung auf die echte Server-URL. | -| `TWENTY_DEPLOY_API_KEY` | GitHub-Repository **Settings → Secrets and variables → Actions** | API-Schlüssel mit Berechtigung zum Bereitstellen auf dem Zielserver. | - - -Der Standardwert von `TWENTY_DEPLOY_URL` (`http://localhost:3000`) ist ein Platzhalter — von einem GitHub-gehosteten Runner ist er nicht erreichbar. Aktualisieren Sie sie auf die öffentliche URL Ihres Servers (oder verwenden Sie einen selbstgehosteten Runner mit Netzwerkzugriff), bevor Sie CD aktivieren. - - -**Eine Vorschau-Bereitstellung aus einem PR auslösen:** - -Fügen Sie einem Pull Request das Label `deploy` hinzu. Die `if:`-Bedingung in `cd.yml` führt den Job für diesen PR mit dem Head-Commit des PR aus, sodass Sie eine Änderung auf dem Zielserver vor dem Mergen validieren können. - -### Fixieren der wiederverwendbaren Actions - -Beide Workflows verweisen auf wiederverwendbare Actions mit `@main`, sodass Aktualisierungen der Actions im Repository `twentyhq/twenty` automatisch übernommen werden. Wenn Sie deterministische Builds möchten, ersetzen Sie `@main` in jeder `uses:`-Zeile durch eine Commit-SHA oder einen Release-Tag. - -## Auf npm veröffentlichen - -Die Veröffentlichung auf npm macht Ihre App im Twenty-Marktplatz auffindbar. Jeder Twenty-Arbeitsbereich kann Marktplatz-Apps direkt über die Benutzeroberfläche durchsuchen, installieren und aktualisieren. - -### Anforderungen - -* Ein [npm](https://www.npmjs.com)-Konto -* Das Schlüsselwort `twenty-app` in Ihrem `package.json`-Array `keywords` (manuell hinzufügen — es ist in der `create-twenty-app`-Vorlage standardmäßig nicht enthalten) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Marktplatz-Metadaten - -Die `defineApplication()`-Konfiguration unterstützt optionale Felder, die steuern, wie Ihre App im Marktplatz erscheint. Verwenden Sie `logoUrl` und `screenshots`, um Bilder aus dem Ordner `public/` zu referenzieren: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -Siehe das [defineApplication-Akkordeon](/l/de/developers/extend/apps/building#defineentity-functions) auf der Seite Building Apps für die vollständige Liste der Marktplatzfelder (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl` usw.). - -#### Empfohlene Abmessungen für Screenshots - -Der Marktplatz stellt `screenshots` in einem festen `8:5`-Container dar (zum Beispiel `1600×1000 px`). - - -Screenshots mit beliebigem Seitenverhältnis werden vollständig angezeigt und niemals beschnitten, aber alles, was deutlich höher oder schmaler als `8:5` ist, zeigt an den Seiten leere Balken. - - -### Veröffentlichen - -```bash filename="Terminal" -yarn twenty publish -``` - -Um unter einem bestimmten dist-tag zu veröffentlichen (z. B. `beta` oder `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### So funktioniert die Marktplatz-Erkennung - -Der Twenty-Server synchronisiert seinen Marktplatzkatalog **stündlich** aus der npm-Registry. - -Sie können die Synchronisierung sofort auslösen, anstatt zu warten: - -```bash filename="Terminal" -yarn twenty server catalog-sync -# To target a specific remote: -# yarn twenty server catalog-sync --remote production -``` - -Die im Marktplatz angezeigten Metadaten stammen aus Ihrer `defineApplication()`-Konfiguration — Felder wie `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` und `termsUrl`. - - -Wenn deine App keine `aboutDescription` in `defineApplication()` definiert, verwendet der Marktplatz automatisch die `README.md` deines Pakets von npm als Inhalt der Über-uns-Seite. Das bedeutet, dass du eine einzige README sowohl für npm als auch für den Twenty-Marktplatz pflegen kannst. Wenn du im Marktplatz eine andere Beschreibung möchtest, setze `aboutDescription` explizit. - - -### CI-Veröffentlichung - -Verwenden Sie diesen GitHub-Actions-Workflow, um bei jedem Release automatisch zu veröffentlichen (verwendet [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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Für andere CI-Systeme (GitLab CI, CircleCI usw.) gelten die gleichen drei Befehle: `yarn install`, `yarn twenty build` und anschließend `npm publish` aus `.twenty/output`. - - -**npm-Provenance** ist optional, wird jedoch empfohlen. Das Veröffentlichen mit `--provenance` fügt Ihrem npm-Eintrag ein Vertrauensabzeichen hinzu, sodass Nutzer überprüfen können, dass das Paket aus einem bestimmten Commit in einer öffentlichen CI-Pipeline gebaut wurde. Siehe die [npm-Provenance-Dokumentation](https://docs.npmjs.com/generating-provenance-statements) für Einrichtungshinweise. - - -## Apps installieren - -Sobald eine App veröffentlicht (npm) oder bereitgestellt (Tarball) wurde, können Arbeitsbereiche sie über die Benutzeroberfläche installieren. - -Gehen Sie zur Seite **Einstellungen > Anwendungen** in Twenty, auf der sowohl Marktplatz- als auch per Tarball bereitgestellte Apps durchsucht und installiert werden können. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Sie können Apps auch über die Befehlszeile installieren: - -```bash filename="Terminal" -yarn twenty install -``` - - -Der Server erzwingt bei der Installation semver-Versionierung und spiegelt damit die Regeln beim Bereitstellen wider: - -* Die Installation derselben Version, die in Ihrem Arbeitsbereich bereits installiert ist, wird mit einem `APP_ALREADY_INSTALLED`-Fehler abgelehnt. -* Die Installation einer niedrigeren Version als die aktuell installierte wird mit einem `CANNOT_DOWNGRADE_APPLICATION`-Fehler abgelehnt. - -Um eine neuere Version zu installieren, stellen Sie sie zuerst bereit oder veröffentlichen Sie sie und führen Sie dann `yarn twenty install` erneut aus. - diff --git a/packages/twenty-docs/l/de/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/de/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index 18a23be9d0..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Fähigkeiten & Agenten -description: Definieren Sie KI-Skills und Agenten für Ihre App. -icon: robot ---- - - - Fähigkeiten und Agenten befinden sich derzeit in der Alpha-Phase. Die Funktion ist funktionsfähig, entwickelt sich jedoch noch weiter. - - -Apps können KI-Funktionen definieren, die im Arbeitsbereich verfügbar sind — wiederverwendbare Skill-Anweisungen und Agenten mit benutzerdefinierten System-Prompts. - - - - -Skills definieren wiederverwendbare Anweisungen und Fähigkeiten, die KI-Agenten in Ihrem Arbeitsbereich verwenden können. Verwenden Sie `defineSkill()`, um Skills mit eingebauter Validierung zu definieren: - -```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`, -}); -``` - -Hauptpunkte: -* `name` ist eine eindeutige Kennung (als Zeichenfolge) für den Skill (kebab-case empfohlen). -* `label` ist der menschenlesbare Anzeigename, der in der UI angezeigt wird. -* `content` enthält die Skill-Anweisungen — dies ist der Text, den der KI-Agent verwendet. -* `icon` (optional) legt das in der UI angezeigte Symbol fest. -* `description` (optional) liefert zusätzlichen Kontext zum Zweck des Skills. - - - - -Agenten sind KI-Assistenten, die innerhalb Ihres Arbeitsbereichs leben. Verwenden Sie `defineAgent()`, um Agenten mit einem benutzerdefinierten System-Prompt zu erstellen: - -```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.', -}); -``` - -Hauptpunkte: -* `name` ist eine eindeutige Kennung (als Zeichenfolge) für den Agenten (kebab-case empfohlen). -* `label` ist der in der UI angezeigte Anzeigename. -* `prompt` ist der System-Prompt, der das Verhalten des Agenten definiert. -* `description` (optional) liefert Kontext dazu, was der Agent tut. -* `icon` (optional) legt das in der UI angezeigte Symbol fest. -* `modelId` (optional) überschreibt das vom Agenten verwendete Standard-KI-Modell. - - - diff --git a/packages/twenty-docs/l/de/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/de/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 788e9581ff..0000000000 --- a/packages/twenty-docs/l/de/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Twenty-Apps -description: Twenty-Anpassungen als Code erstellen und verwalten. ---- - - -Apps befinden sich derzeit in der Alpha-Phase. Die Funktion ist funktionsfähig, entwickelt sich jedoch noch weiter. - - -## Was sind Apps? - -Apps ermöglichen es Ihnen, Twenty mit benutzerdefinierten Objekten, Feldern, Logikfunktionen, Frontend-Komponenten, KI-Fähigkeiten und mehr zu erweitern — alles als Code verwaltet. Anstatt alles über die UI zu konfigurieren, definieren Sie Ihr Datenmodell und Ihre Logik in TypeScript und stellen es in einem oder mehreren Workspaces bereit. - -**Was Sie erstellen können:** - -* **Benutzerdefinierte Objekte und Felder** — erweitern Sie Ihr Datenmodell mit neuen Entitäten oder fügen Sie bestehenden Objekten wie Company oder Person Felder hinzu -* **Logikfunktionen** — serverseitige Funktionen, die durch Datenbankereignisse, Cron-Zeitpläne oder HTTP-Routen ausgelöst werden -* **Frontend-Komponenten** — React-Komponenten, die innerhalb der UI von Twenty gerendert werden (Datensatzseiten, Befehlsmenü, Seitenpanels) -* **KI-Fähigkeiten und -Agenten** — erweitern Sie die KI von Twenty mit benutzerdefinierten Fähigkeiten -* **Ansichten und Navigation** — vorkonfigurierte gespeicherte Ansichten und Seitenleistenlinks - -## Schnellstart - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Dies erstellt das Grundgerüst für eine neue App, startet optional einen lokalen Twenty-Server und beginnt, Ihre Dateien auf Änderungen zu überwachen. Den vollständigen Ablauf finden Sie im Leitfaden [Erste Schritte](/l/de/developers/extend/apps/getting-started). - -## Detaillierte Anleitungen - -| Leitfaden | Beschreibung | -| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| [Erste Schritte](/l/de/developers/extend/apps/getting-started) | App-Gerüst erstellen, lokalen Server einrichten, Projektstruktur, CI | -| [Apps entwickeln](/l/de/developers/extend/apps/building) | Entitätsdefinitionen (`defineObject`, `defineLogicFunction`, `defineFrontComponent` usw.), API-Clients, npm-Pakete, öffentliche Assets, Tests | -| [Veröffentlichen](/l/de/developers/extend/apps/publishing) | Auf einem Server bereitstellen, auf npm veröffentlichen, Marktplatz | - -## Wichtige Konzepte - -### Entitätserkennung - -Das SDK erkennt Entitäten, indem es Ihre TypeScript-Dateien nach Aufrufen von `export default define({...})` scannt. Dateibenennung und Ordnerstruktur sind flexibel — die Erkennung ist AST-basiert, nicht pfadbasiert. - -### Verfügbare Entitätstypen - -| Funktion | Zweck | -| ---------------------------------- | ------------------------------------------------ | -| `defineApplication()` | Anwendungsmetadaten (erforderlich, eine pro App) | -| `defineObject()` | Benutzerdefinierte Objekte mit Feldern | -| `defineField()` | Felder bei bestehenden Objekten | -| `defineLogicFunction()` | Serverseitige Logik mit Triggern | -| `defineFrontComponent()` | React-Komponenten in der UI von Twenty | -| `defineRole()` | Berechtigungsrollen | -| `defineView()` | Konfigurationen gespeicherter Ansichten | -| `defineNavigationMenuItem()` | Navigationslinks in der Seitenleiste | -| `defineSkill()` | Fähigkeiten von KI-Agenten | -| `defineAgent()` | KI-Agenten mit Prompts | -| `definePageLayout()` | Benutzerdefinierte Layouts für Datensatzseiten | -| `definePreInstallLogicFunction()` | Wird vor der App-Installation ausgeführt | -| `definePostInstallLogicFunction()` | Wird nach der App-Installation ausgeführt | - -### Entwicklungs-Workflow - -1. **`yarn twenty dev`** — überwacht Quelldateien, baut bei Änderungen neu, synchronisiert mit dem Server, generiert typisierte API-Clients -2. **`yarn twenty build`** — erzeugt ein auslieferbares Build -3. **`yarn twenty deploy`** — stellt auf einem entfernten Twenty-Server bereit -4. **`yarn twenty add`** — erstellt interaktiv das Gerüst für eine neue Entität - -### CLI-Referenz - -```bash filename="Terminal" -yarn twenty help # Alle Befehle auflisten -yarn twenty server start # Lokalen Dev-Server starten -yarn twenty remote add # Mit einem Twenty-Server verbinden -yarn twenty exec -n fn # Eine Logikfunktion ausführen -yarn twenty logs -n fn # Funktionsprotokolle streamen -``` - -Die vollständige CLI-Referenz finden Sie im Leitfaden [Erste Schritte](/l/de/developers/extend/apps/getting-started). diff --git a/packages/twenty-docs/l/de/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/de/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 3761545592..0000000000 --- a/packages/twenty-docs/l/de/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Veröffentlichungseinstellungen -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Laborfunktionen - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Gehen Sie zu **Einstellungen → Veröffentlichungen** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/es/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/es/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 7a24d55b5b..0000000000 --- a/packages/twenty-docs/l/es/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,650 +0,0 @@ ---- -title: Aplicaciones de Twenty -description: Crea y gestiona personalizaciones de Twenty como código. ---- - - - Las aplicaciones están actualmente en pruebas alfa. La funcionalidad es operativa, pero sigue evolucionando. - - -## ¿Qué son las aplicaciones? - -Las aplicaciones te permiten crear y administrar personalizaciones de Twenty **como código**. En lugar de configurar todo a través de la interfaz de usuario, defines tu modelo de datos y funciones de lógica en código, lo que hace más rápido crear, mantener y desplegar en múltiples espacios de trabajo. - -**Lo que puedes hacer hoy:** - -* Define objetos y campos personalizados como código (modelo de datos gestionado) -* Crea funciones de lógica con desencadenadores personalizados -* Despliega la misma aplicación en múltiples espacios de trabajo - -**Próximamente:** - -* Diseños y componentes de la interfaz de usuario personalizados - -## Prerrequisitos - -* Node.js 24+ y Yarn 4 -* Un espacio de trabajo de Twenty y una clave de API (créala en https://app.twenty.com/settings/api-webhooks) - -## Primeros pasos - -Crea una aplicación nueva usando el generador oficial, luego autentícate y comienza a desarrollar: - -```bash filename="Terminal" -# Crear la estructura de una nueva aplicación -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app - -# Si no usas yarn@4 -corepack enable -yarn install - -# Autentícate con tu clave de API (se te pedirá) -yarn auth:login - -# Inicia el modo de desarrollo: sincroniza automáticamente los cambios locales con tu espacio de trabajo -yarn app:dev -``` - -Desde aquí usted puede: - -```bash filename="Terminal" -# Añade una nueva entidad a tu aplicación (guiado) -yarn entity:add - -# Supervisa los registros de funciones de tu aplicación -yarn function:logs - -# Ejecuta una función por nombre -yarn function:execute -n my-function -p '{\"name\": \"test\"}' - -# Desinstala la aplicación del espacio de trabajo actual -yarn app:uninstall - -# Muestra la ayuda de los comandos -yarn help -``` - -Consulta también: las páginas de referencia de la CLI para [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) y [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk). - -## Estructura del proyecto (generada) - -Cuando ejecutas `npx create-twenty-app@latest my-twenty-app`, el generador: - -* Copia una aplicación base mínima en `my-twenty-app/` -* Añade una dependencia local de `twenty-sdk` y la configuración de Yarn 4 -* Crea archivos de configuración y scripts vinculados a la CLI `twenty` -* Genera una configuración de aplicación predeterminada y un rol de función predeterminado - -Una aplicación recién generada se ve así: - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .yarn/ - install-state.gz - .oxlintrc.json - tsconfig.json - README.md - public/ # Carpeta de recursos públicos (imágenes, fuentes, etc.) - src/ - application.config.ts # Requerido - configuración principal de la aplicación - default-function.role.ts # Rol predeterminado para funciones sin servidor - hello-world.function.ts # Ejemplo de función sin servidor - hello-world.front-component.tsx # Ejemplo de componente frontal - // tus entidades (*.object.ts, *.function.ts, *.front-component.tsx, *.role.ts) -``` - -### Convención sobre configuración - -Las aplicaciones usan un enfoque de **convención sobre configuración** en el que las entidades se detectan por su sufijo de archivo. Esto permite una organización flexible dentro de la carpeta `src/app/`: - -| Sufijo de archivo | Tipo de entidad | -| ----------------------- | --------------------------------------- | -| `*.object.ts` | Definiciones de objetos personalizados | -| `*.function.ts` | Definiciones de funciones sin servidor | -| `*.front-component.tsx` | Definiciones de componentes de interfaz | -| `*.role.ts` | Definiciones de roles | - -### Organizaciones de carpetas compatibles - -Puedes organizar tus entidades con cualquiera de estos patrones: - -**Tradicional (por tipo):** - -```text -src/ -├── application.config.ts -├── objects/ -│ └── postCard.object.ts -├── functions/ -│ └── createPostCard.function.ts -├── components/ -│ └── card.front-component.tsx -└── roles/ - └── admin.role.ts -``` - -**Basado en funcionalidades:** - -```text -src/ -├── application.config.ts -└── post-card/ - ├── postCard.object.ts - ├── createPostCard.function.ts - ├── card.front-component.tsx - └── postCardAdmin.role.ts -``` - -**Plano:** - -```text -src/ -├── application.config.ts -├── postCard.object.ts -├── createPostCard.function.ts -├── card.front-component.tsx -└── admin.role.ts -``` - -A grandes rasgos: - -* **package.json**: Declara el nombre de la aplicación, la versión, los entornos (Node 24+, Yarn 4) y agrega `twenty-sdk` además de scripts como `app:dev`, `entity:add`, `function:logs`, `function:execute`, `app:uninstall` y `auth:login` que delegan en la CLI local `twenty`. -* **.gitignore**: Ignora artefactos comunes como `node_modules`, `.yarn`, `generated/` (cliente tipado), `dist/`, `build/`, carpetas de cobertura, archivos de registro y archivos `.env*`. -* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Bloquean y configuran la cadena de herramientas Yarn 4 utilizada por el proyecto. -* **.nvmrc**: Fija la versión de Node.js esperada por el proyecto. -* **.oxlintrc.json** y **tsconfig.json**: Proporcionan linting y configuración de TypeScript para las fuentes de TypeScript de tu aplicación. -* **README.md**: Un README breve en la raíz de la aplicación con instrucciones básicas. -* **public/**: Una carpeta para almacenar recursos públicos (imágenes, fuentes, archivos estáticos) que se servirán con tu aplicación. Los archivos colocados aquí se cargan durante la sincronización y son accesibles en tiempo de ejecución. -* **src/**: El lugar principal donde defines tu aplicación como código: - * `application.config.ts`: Configuración global de tu aplicación (metadatos y vinculación en tiempo de ejecución). Consulta "Configuración de la aplicación" más abajo. - * `*.role.ts`: Definiciones de roles usadas por tus funciones de lógica. Consulta "Rol de función predeterminado" más abajo. - * `*.object.ts`: Definiciones de objetos personalizados. - * `*.function.ts`: Definiciones de funciones de lógica. - * `*.front-component.tsx`: Definiciones de componentes de interfaz. - -Comandos posteriores añadirán más archivos y carpetas: - -* `yarn app:dev` genera automáticamente el cliente Twenty tipado en `node_modules/twenty-sdk/generated`. -* `yarn entity:add` añadirá archivos de definición de entidades en `src/` para tus objetos, funciones, componentes de interfaz o roles personalizados. - -## Autenticación - -La primera vez que ejecutes `yarn auth:login`, se te solicitará: - -* URL de la API (por defecto http://localhost:3000 o el perfil de tu espacio de trabajo actual) -* Clave de API - -Tus credenciales se almacenan por usuario en `~/.twenty/config.json`. Puedes mantener varios perfiles y cambiar entre ellos. - -### Gestión de espacios de trabajo - -```bash filename="Terminal" -# Login interactively (recommended) -yarn auth:login - -# Login to a specific workspace profile -yarn auth:login --workspace my-custom-workspace - -# List all configured workspaces -yarn auth:list - -# Switch the default workspace (interactive) -yarn auth:switch - -# Switch to a specific workspace -yarn auth:switch production - -# Check current authentication status -yarn auth:status -``` - -Una vez que hayas cambiado de espacio de trabajo con `auth:switch`, todos los comandos posteriores usarán ese espacio de trabajo de forma predeterminada. Aún puedes anularlo temporalmente con `--workspace `. - -## Usa los recursos del SDK (tipos y configuración) - -El twenty-sdk proporciona bloques de construcción tipados y funciones auxiliares que utilizas dentro de tu aplicación. A continuación, las partes clave que usarás con más frecuencia. - -### Funciones auxiliares - -El SDK proporciona cuatro funciones auxiliares con validación incorporada para definir las entidades de tu aplicación: - -| Función | Propósito | -| --------------------- | ---------------------------------------------- | -| `defineApplication()` | Configura los metadatos de la aplicación | -| `defineObject()` | Define objetos personalizados con campos | -| `defineFunction()` | Define funciones de lógica con controladores | -| `defineRole()` | Configura permisos de roles y acceso a objetos | - -Estas funciones validan tu configuración en tiempo de ejecución y proporcionan un mejor autocompletado en el IDE y seguridad de tipos. - -### Definir objetos - -Los objetos personalizados describen tanto el esquema como el comportamiento de los registros en tu espacio de trabajo. Usa `defineObject()` para definir objetos con validación incorporada: - -```typescript -// src/app/postCard.object.ts -import { defineObject, FieldType } from 'twenty-sdk'; - -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, - }, - ], -}); -``` - -Puntos clave: - -* Usa `defineObject()` para validación incorporada y mejor soporte del IDE. -* El `universalIdentifier` debe ser único y estable entre implementaciones. -* Cada campo requiere `name`, `type`, `label` y su propio `universalIdentifier` estable. -* La matriz `fields` es opcional: puedes definir objetos sin campos personalizados. -* Puedes generar nuevos objetos usando `yarn entity:add`, que te guía por el nombrado, los campos y las relaciones. - - - **Los campos base se crean automáticamente.** Cuando defines un objeto personalizado, Twenty añade automáticamente campos estándar como `name`, `createdAt`, `updatedAt`, `createdBy`, `position` y `deletedAt`. No necesitas definir estos en tu matriz `fields` — solo agrega tus campos personalizados. - - -### Configuración de la aplicación (application.config.ts) - -Cada aplicación tiene un único archivo `application.config.ts` que describe: - -* **Qué es la aplicación**: identificadores, nombre para mostrar y descripción. -* **Cómo se ejecutan sus funciones**: qué rol usan para permisos. -* **Variables (opcionales)**: pares clave–valor expuestos a tus funciones como variables de entorno. - -Use `defineApplication()` to define your application configuration: - -```typescript -// src/app/application.config.ts -import { defineApplication } from 'twenty-sdk'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role'; - -export default defineApplication({ - universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - roleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Notas: - -* Los campos `universalIdentifier` son ID deterministas bajo tu control; genéralos una vez y mantenlos estables entre sincronizaciones. -* Las `applicationVariables` se convierten en variables de entorno para tus funciones (por ejemplo, `DEFAULT_RECIPIENT_NAME` está disponible como `process.env.DEFAULT_RECIPIENT_NAME`). -* `roleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below). - -#### Roles y permisos - -Las aplicaciones pueden definir roles que encapsulan permisos sobre los objetos y acciones de tu espacio de trabajo. The field `roleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's logic functions. - -* La clave de API en tiempo de ejecución inyectada como `TWENTY_API_KEY` se deriva de este rol de función predeterminado. -* El cliente tipado estará restringido a los permisos otorgados a ese rol. -* Sigue el principio de mínimo privilegio: crea un rol dedicado con solo los permisos que necesitan tus funciones y luego referencia su identificador universal. - -##### Rol de función predeterminado (\*.role.ts) - -Cuando generas una nueva aplicación, la CLI también crea un archivo de rol predeterminado. Usa `defineRole()` para definir roles con validación incorporada: - -```typescript -// src/app/default-function.role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - label: 'Default function role', - description: 'Default role for function Twenty client', - canReadAllObjectRecords: false, - canUpdateAllObjectRecords: false, - canSoftDeleteAllObjectRecords: false, - canDestroyAllObjectRecords: false, - canUpdateAllSettings: false, - canBeAssignedToAgents: false, - canBeAssignedToUsers: false, - canBeAssignedToApiKeys: false, - objectPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - canReadObjectRecords: true, - canUpdateObjectRecords: true, - canSoftDeleteObjectRecords: false, - canDestroyObjectRecords: false, - }, - ], - fieldPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff', - canReadFieldValue: false, - canUpdateFieldValue: false, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - -The `universalIdentifier` of this role is then referenced in `application.config.ts` as `roleUniversalIdentifier`. En otras palabras: - -* **\*.role.ts** define lo que puede hacer el rol de función predeterminado. -* **application.config.ts** apunta a ese rol para que tus funciones hereden sus permisos. - -Notas: - -* Parte del rol generado y luego restríngele progresivamente siguiendo el principio de mínimo privilegio. -* Reemplaza `objectPermissions` y `fieldPermissions` con los objetos/campos que necesitan tus funciones. -* `permissionFlags` controla el acceso a capacidades a nivel de plataforma. Mantenlos al mínimo; agrega solo lo que necesites. -* Consulta un ejemplo funcional en la aplicación Hello World: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - -### Configuración y punto de entrada de funciones de lógica - -Cada archivo de función usa `defineFunction()` para exportar una configuración con un controlador y desencadenadores opcionales. Usa el sufijo de archivo `*.function.ts` para la detección automática. - -```typescript -// src/app/createPostCard.function.ts -import { defineFunction } from 'twenty-sdk'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk'; -import Twenty, { type Person } from '~/generated'; - -const handler = async (params: RoutePayload) => { - const client = new Twenty(); // generated typed client - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : 'Hello world'; - - const result = await client.mutation({ - createPostCard: { - __args: { data: { name } }, - id: true, - name: true, - }, - }); - return result; -}; - -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'create-new-post-card', - timeoutSeconds: 2, - handler, - triggers: [ - // Public HTTP route trigger '/s/post-card/create' - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/post-card/create', - httpMethod: 'GET', - isAuthRequired: false, - }, - // Cron trigger (CRON pattern) - // { - // universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2', - // type: 'cron', - // pattern: '0 0 1 1 *', - // }, - // Database event trigger - // { - // universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156', - // type: 'databaseEvent', - // eventName: 'person.updated', - // updatedFields: ['name'], - // }, - ], -}); -``` - -Tipos de desencadenadores comunes: - -* **route**: Expone tu función en una ruta y método HTTP **bajo el endpoint `/s/`**: - -> p. ej. `path: '/post-card/create',` -> llamar en `/s/post-card/create` - -* **cron**: Ejecuta tu función en un horario usando una expresión CRON. -* **databaseEvent**: Se ejecuta en eventos del ciclo de vida de objetos del espacio de trabajo. Cuando la operación del evento es `updated`, se pueden especificar campos específicos que se deben escuchar en el arreglo `updatedFields`. Si se deja sin definir o vacío, cualquier actualización activará la función. - -> p. ej., `person.updated` - -Notas: - -* La matriz `triggers` es opcional. Las funciones sin desencadenadores pueden usarse como funciones utilitarias llamadas por otras funciones. -* Puedes combinar múltiples tipos de desencadenadores en una sola función. - -### Carga útil del disparador de ruta - - - **Cambio no retrocompatible (v1.16, enero de 2026):** El formato de la carga útil del disparador de ruta ha cambiado. Antes de la v1.16, los parámetros de consulta, los parámetros de ruta y el cuerpo se enviaban directamente como la carga útil. A partir de la v1.16, están anidados dentro de un objeto `RoutePayload` estructurado. - - **Antes de la v1.16:** - - ```typescript - const handler = async (params) => { - const { param1, param2 } = params; // Direct access - }; - ``` - - **Después de la v1.16:** - - ```typescript - const handler = async (event: RoutePayload) => { - const { param1, param2 } = event.body; // Access via .body - const { queryParam } = event.queryStringParameters; - const { id } = event.pathParameters; - }; - ``` - - **Para migrar las funciones existentes:** Actualiza tu controlador para desestructurar desde `event.body`, `event.queryStringParameters` o `event.pathParameters` en lugar de hacerlo directamente desde el objeto params. - - -Cuando un disparador de ruta invoca tu función de lógica, esta recibe un objeto `RoutePayload` que sigue el formato de AWS HTTP API v2. Importa el tipo desde `twenty-sdk`: - -```typescript -import { defineFunction, type RoutePayload } from 'twenty-sdk'; - -const handler = async (event: RoutePayload) => { - // Access request data - const { headers, queryStringParameters, pathParameters, body } = event; - - // HTTP method and path are available in requestContext - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -El tipo `RoutePayload` tiene la siguiente estructura: - -| Propiedad | Tipo | Descripción | -| ---------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------- | -| `headers` | `Record` | Encabezados HTTP (solo aquellos listados en `forwardedRequestHeaders`) | -| `queryStringParameters` | `Record` | Parámetros de consulta (valores múltiples unidos con comas) | -| `pathParameters` | `Record` | Parámetros de ruta extraídos del patrón de ruta (p. ej., `/users/:id` → `{ id: '123' }`) | -| `cuerpo` | `object \| null` | Cuerpo de la solicitud analizado (JSON) | -| `isBase64Encoded` | `booleano` | Indica si el cuerpo está codificado en base64 | -| `requestContext.http.method` | `string` | Método HTTP (GET, POST, PUT, PATCH, DELETE) | -| `requestContext.http.path` | `string` | Ruta de la solicitud sin procesar | - -### Reenvío de encabezados HTTP - -De forma predeterminada, los encabezados HTTP de las solicitudes entrantes **no** se pasan a tu función de lógica por razones de seguridad. Para acceder a encabezados específicos, enuméralos explícitamente en el arreglo `forwardedRequestHeaders`: - -```typescript -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'webhook-handler', - handler, - triggers: [ - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/webhook', - httpMethod: 'POST', - isAuthRequired: false, - forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], - }, - ], -}); -``` - -En tu controlador, luego puedes acceder a estos encabezados: - -```typescript -const handler = async (event: RoutePayload) => { - const signature = event.headers['x-webhook-signature']; - const contentType = event.headers['content-type']; - - // Validate webhook signature... - return { received: true }; -}; -``` - - - Los nombres de los encabezados se normalizan a minúsculas. Accede a ellos usando claves en minúsculas (por ejemplo, `event.headers['content-type']`). - - -Puedes crear funciones nuevas de dos maneras: - -* **Generado**: Ejecuta `yarn entity:add` y elige la opción para añadir una nueva función. Esto genera un archivo inicial con un controlador y configuración. -* **Manual**: Crea un nuevo archivo `*.function.ts` y usa `defineFunction()`, siguiendo el mismo patrón. - -### Cliente tipado generado - -`yarn app:dev` genera automáticamente el cliente Twenty tipado en `node_modules/twenty-sdk/generated`. Úsalo en tus funciones: - -```typescript -import Twenty from '~/generated'; - -const client = new Twenty(); -const { me } = await client.query({ me: { id: true, displayName: true } }); -``` - -El cliente se regenera automáticamente durante la ejecución de `app:dev`. Reinicia `app:dev` después de cambiar tus objetos o al incorporarte a un nuevo espacio de trabajo. - -#### Credenciales en tiempo de ejecución en funciones de lógica - -Cuando tu función se ejecuta en Twenty, la plataforma inyecta credenciales como variables de entorno antes de que tu código se ejecute: - -* `TWENTY_API_URL`: URL base de la API de Twenty a la que apunta tu aplicación. -* `TWENTY_API_KEY`: Clave de corta duración con alcance al rol de función predeterminado de tu aplicación. - -Notas: - -* No necesitas pasar la URL ni la clave de API al cliente generado. Lee `TWENTY_API_URL` y `TWENTY_API_KEY` de process.env en tiempo de ejecución. -* The API key's permissions are determined by the role referenced in your `application.config.ts` via `roleUniversalIdentifier`. Este es el rol predeterminado que usan las funciones de lógica de tu aplicación. -* Las aplicaciones pueden definir roles para seguir el principio de mínimo privilegio. Grant only the permissions your functions need, then point `roleUniversalIdentifier` to that role's universal identifier. - -### Ejemplo Hello World - -Explora un ejemplo mínimo de extremo a extremo que demuestra objetos, funciones y múltiples desencadenadores [aquí](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world): - -## Configuración manual (sin el generador) - -Aunque recomendamos usar `create-twenty-app` para la mejor experiencia de inicio, también puedes configurar un proyecto manualmente. No instales la CLI globalmente. En su lugar, agrega `twenty-sdk` como dependencia local y conecta scripts en tu package.json: - -```bash filename="Terminal" -yarn add -D twenty-sdk -``` - -Luego agrega scripts como estos: - -```json filename="package.json" -{ - "scripts": { - "auth:login": "twenty auth:login", - "auth:logout": "twenty auth:logout", - "auth:status": "twenty auth:status", - "auth:switch": "twenty auth:switch", - "auth:list": "twenty auth:list", - "app:dev": "twenty app:dev", - "app:uninstall": "twenty app:uninstall", - "entity:add": "twenty entity:add", - "function:logs": "twenty function:logs", - "function:execute": "twenty function:execute", - "help": "twenty help" - } -} -``` - -Ahora puedes ejecutar los mismos comandos mediante Yarn, p. ej., `yarn app:dev`, etc. - -## Solución de problemas - -* Errores de autenticación: ejecuta `yarn auth:login` y asegúrate de que tu clave de API tenga los permisos necesarios. -* No se puede conectar al servidor: verifica la URL de la API y que el servidor de Twenty sea accesible. -* Tipos o cliente faltantes/obsoletos: reinicia `yarn app:dev`. -* El modo de desarrollo no sincroniza: asegúrate de que `yarn app:dev` esté ejecutándose y de que los cambios no sean ignorados por tu entorno. - -Canal de ayuda en Discord: https://discord.com/channels/1130383047699738754/1130386664812982322 diff --git a/packages/twenty-docs/l/es/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/es/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4180b67161..0000000000 --- a/packages/twenty-docs/l/es/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Features - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/fr/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/fr/developers/extend/capabilities/apps.mdx deleted file mode 100644 index dcd51147a0..0000000000 --- a/packages/twenty-docs/l/fr/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,650 +0,0 @@ ---- -title: Applications Twenty -description: Créez et gérez les personnalisations Twenty sous forme de code. ---- - - - Les applications sont actuellement en phase de test alpha. La fonctionnalité est fonctionnelle mais encore en évolution. - - -## Que sont les applications ? - -Les applications vous permettent de créer et de gérer des personnalisations Twenty **sous forme de code**. Au lieu de tout configurer via l’interface utilisateur, vous définissez votre modèle de données et vos fonctions logiques dans le code — ce qui accélère la création, la maintenance et le déploiement sur plusieurs espaces de travail. - -**Ce que vous pouvez faire aujourd'hui:** - -* Définir des objets et des champs personnalisés sous forme de code (modèle de données géré) -* Créer des fonctions logiques avec des déclencheurs personnalisés -* Déployer la même application sur plusieurs espaces de travail - -**Bientôt disponible :** - -* Mises en page et composants d’interface utilisateur personnalisés - -## Prérequis - -* Node.js 24+ et Yarn 4 -* Un espace de travail Twenty et une clé API (créez-en une sur https://app.twenty.com/settings/api-webhooks) - -## Prise en main - -Créez une nouvelle application avec l’outil d’amorçage officiel, puis authentifiez-vous et commencez à développer : - -```bash filename="Terminal" -# Générez une nouvelle application -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app - -# Si vous n'utilisez pas yarn@4 -corepack enable -yarn install - -# Authentifiez-vous avec votre clé API (une invite s'affichera) -yarn auth:login - -# Démarrez le mode développement : synchronise automatiquement les modifications locales avec votre espace de travail -yarn app:dev -``` - -À partir d'ici, vous pouvez : - -```bash filename="Terminal" -# Ajouter une nouvelle entité à votre application (assisté) -yarn entity:add - -# Surveiller les journaux des fonctions de votre application -yarn function:logs - -# Exécuter une fonction par nom -yarn function:execute -n my-function -p '{"name": "test"}' - -# Désinstaller l'application de l'espace de travail actuel -yarn app:uninstall - -# Afficher l'aide des commandes -yarn help -``` - -Voir aussi : les pages de référence CLI pour [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) et [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk). - -## Structure du projet (générée) - -Lorsque vous exécutez `npx create-twenty-app@latest my-twenty-app`, l’outil de scaffolding : - -* Copie une application de base minimale dans `my-twenty-app/` -* Ajoute une dépendance locale à `twenty-sdk` et la configuration Yarn 4 -* Crée des fichiers de configuration et des scripts reliés à la CLI `twenty` -* Génère une configuration d’application par défaut et un rôle de fonction par défaut - -Une application nouvellement générée ressemble à ceci : - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .yarn/ - install-state.gz - .oxlintrc.json - tsconfig.json - README.md - public/ # Dossier de ressources publiques (images, polices, etc.) - src/ - application.config.ts # Obligatoire - configuration principale de l'application - default-function.role.ts # Rôle par défaut pour les fonctions serverless - hello-world.function.ts # Exemple de fonction serverless - hello-world.front-component.tsx # Exemple de composant frontal - // vos entités (*.object.ts, *.function.ts, *.front-component.tsx, *.role.ts) -``` - -### Convention plutôt que configuration - -Les applications adoptent une approche **convention plutôt que configuration** où les entités sont détectées par leur suffixe de fichier. Cela permet une organisation flexible dans le dossier `src/app/` : - -| Suffixe de fichier | Type d’entité | -| ----------------------- | ------------------------------------- | -| `*.object.ts` | Définitions d’objets personnalisés | -| `*.function.ts` | Définitions de fonctions sans serveur | -| `*.front-component.tsx` | Définitions des composants frontaux | -| `*.role.ts` | Définitions de rôles | - -### Structures de dossiers prises en charge - -Vous pouvez organiser vos entités selon l’un des schémas suivants : - -**Traditionnelle (par type) :** - -```text -src/ -├── application.config.ts -├── objects/ -│ └── postCard.object.ts -├── functions/ -│ └── createPostCard.function.ts -├── components/ -│ └── card.front-component.tsx -└── roles/ - └── admin.role.ts -``` - -**Par fonctionnalité :** - -```text -src/ -├── application.config.ts -└── post-card/ - ├── postCard.object.ts - ├── createPostCard.function.ts - ├── card.front-component.tsx - └── postCardAdmin.role.ts -``` - -**À plat :** - -```text -src/ -├── application.config.ts -├── postCard.object.ts -├── createPostCard.function.ts -├── card.front-component.tsx -└── admin.role.ts -``` - -Dans les grandes lignes : - -* **package.json** : Déclare le nom de l’application, la version, les moteurs (Node 24+, Yarn 4), et ajoute `twenty-sdk` ainsi que des scripts comme `app:dev`, `entity:add`, `function:logs`, `function:execute`, `app:uninstall` et `auth:login` qui délèguent à la CLI locale `twenty`. -* **.gitignore** : Ignore les artefacts courants tels que `node_modules`, `.yarn`, `generated/` (client typé), `dist/`, `build/`, les dossiers de couverture, les fichiers journaux et les fichiers `.env*`. -* **yarn.lock**, **.yarnrc.yml**, **.yarn/** : Verrouillent et configurent la chaîne d’outils Yarn 4 utilisée par le projet. -* **.nvmrc** : Fige la version de Node.js attendue par le projet. -* **.oxlintrc.json** et **tsconfig.json** : Fournissent la configuration de linting et TypeScript pour les sources TypeScript de votre application. -* **README.md** : Un bref README à la racine de l’application avec des instructions de base. -* **public/**: Un dossier pour stocker des ressources publiques (images, polices, fichiers statiques) qui seront servies avec votre application. Les fichiers placés ici sont téléversés lors de la synchronisation et accessibles à l'exécution. -* **src/** : L’endroit principal où vous définissez votre application sous forme de code : - * `application.config.ts` : Configuration globale de votre application (métadonnées et liaisons d’exécution). Voir « Configuration de l’application » ci-dessous. - * `*.role.ts` : Définitions de rôles utilisées par vos fonctions logiques. Voir « Rôle de fonction par défaut » ci-dessous. - * `*.object.ts` : Définitions d’objets personnalisés. - * `*.function.ts` : Définitions de fonctions logiques. - * `*.front-component.tsx` : Définitions des composants front-end. - -Des commandes ultérieures ajouteront d’autres fichiers et dossiers : - -* `yarn app:dev` génère automatiquement le client Twenty typé dans `node_modules/twenty-sdk/generated`. -* `yarn entity:add` ajoutera des fichiers de définition d’entité sous `src/` pour vos objets, fonctions, composants front-end ou rôles personnalisés. - -## Authentification - -La première fois que vous exécutez `yarn auth:login`, il vous sera demandé : - -* URL de l’API (par défaut http://localhost:3000 ou votre profil d’espace de travail actuel) -* Clé API - -Vos identifiants sont stockés par utilisateur dans `~/.twenty/config.json`. Vous pouvez gérer plusieurs profils et basculer entre eux. - -### Gestion des espaces de travail - -```bash filename="Terminal" -# Login interactively (recommended) -yarn auth:login - -# Login to a specific workspace profile -yarn auth:login --workspace my-custom-workspace - -# List all configured workspaces -yarn auth:list - -# Switch the default workspace (interactive) -yarn auth:switch - -# Switch to a specific workspace -yarn auth:switch production - -# Check current authentication status -yarn auth:status -``` - -Une fois que vous avez changé d'espace de travail avec `auth:switch`, toutes les commandes suivantes utiliseront cet espace de travail par défaut. Vous pouvez toujours le surcharger temporairement avec `--workspace `. - -## Utiliser les ressources du SDK (types et configuration) - -Le paquet twenty-sdk fournit des blocs de construction typés et des fonctions utilitaires que vous utilisez dans votre application. Voici les éléments clés que vous manipulerez le plus souvent. - -### Fonctions utilitaires - -Le SDK fournit quatre fonctions utilitaires avec validation intégrée pour définir les entités de votre application : - -| Fonction | Objectif | -| ------------------ | ---------------------------------------------------------- | -| `defineApplication()` | Configurer les métadonnées de l’application | -| `defineObject()` | Définir des objets personnalisés avec des champs | -| `defineFunction()` | Définir des fonctions logiques avec des gestionnaires | -| `defineRole()` | Configurer les autorisations de rôle et l’accès aux objets | - -Ces fonctions valident votre configuration à l’exécution et offrent une meilleure autocomplétion IDE et une sécurité de typage accrue. - -### Définir des objets - -Les objets personnalisés décrivent à la fois le schéma et le comportement des enregistrements dans votre espace de travail. Utilisez `defineObject()` pour définir des objets avec validation intégrée : - -```typescript -// src/app/postCard.object.ts -import { defineObject, FieldType } from 'twenty-sdk'; - -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, - }, - ], -}); -``` - -Points clés : - -* Utilisez `defineObject()` pour une validation intégrée et une meilleure prise en charge par l’IDE. -* Le `universalIdentifier` doit être unique et stable entre les déploiements. -* Chaque champ nécessite un `name`, un `type`, un `label` et son propre `universalIdentifier` stable. -* Le tableau `fields` est facultatif — vous pouvez définir des objets sans champs personnalisés. -* Vous pouvez générer de nouveaux objets avec `yarn entity:add`, qui vous guide à travers le nommage, les champs et les relations. - - - **Les champs de base sont créés automatiquement.** Lorsque vous définissez un objet personnalisé, Twenty ajoute automatiquement des champs standard tels que `name`, `createdAt`, `updatedAt`, `createdBy`, `position` et `deletedAt`. Vous n'avez pas besoin de les définir dans votre tableau `fields` — ajoutez uniquement vos champs personnalisés. - - -### Configuration de l’application (application.config.ts) - -Chaque application dispose d’un seul fichier `application.config.ts` qui décrit : - -* **Identité de l’application** : identifiants, nom d’affichage et description. -* **Exécution des fonctions** : le rôle utilisé pour les autorisations. -* **Variables (facultatif)** : paires clé–valeur exposées à vos fonctions en tant que variables d’environnement. - -Utilisez `defineApplication()` pour définir la configuration de votre application : - -```typescript -// src/app/application.config.ts -import { defineApplication } from 'twenty-sdk'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role'; - -export default defineApplication({ - universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Notes : - -* Les champs `universalIdentifier` sont des identifiants déterministes que vous possédez ; générez-les une fois et conservez-les stables entre les synchronisations. -* `applicationVariables` deviennent des variables d’environnement pour vos fonctions (par exemple, `DEFAULT_RECIPIENT_NAME` est disponible sous `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` doit correspondre au rôle que vous définissez dans votre fichier `*.role.ts` (voir ci-dessous). - -#### Rôles et autorisations - -Les applications peuvent définir des rôles qui encapsulent des autorisations sur les objets et actions de votre espace de travail. Le champ `defaultRoleUniversalIdentifier` dans `application.config.ts` désigne le rôle par défaut utilisé par les fonctions logiques de votre application. - -* La clé API d’exécution injectée sous `TWENTY_API_KEY` est dérivée de ce rôle de fonction par défaut. -* Le client typé sera limité aux autorisations accordées à ce rôle. -* Appliquez le principe du moindre privilège : créez un rôle dédié avec uniquement les autorisations nécessaires à vos fonctions, puis référencez son identifiant universel. - -##### Rôle de fonction par défaut (\*.role.ts) - -Lorsque vous générez une nouvelle application, la CLI crée également un fichier de rôle par défaut. Utilisez `defineRole()` pour définir des rôles avec validation intégrée : - -```typescript -// src/app/default-function.role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - label: 'Default function role', - description: 'Default role for function Twenty client', - canReadAllObjectRecords: false, - canUpdateAllObjectRecords: false, - canSoftDeleteAllObjectRecords: false, - canDestroyAllObjectRecords: false, - canUpdateAllSettings: false, - canBeAssignedToAgents: false, - canBeAssignedToUsers: false, - canBeAssignedToApiKeys: false, - objectPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - canReadObjectRecords: true, - canUpdateObjectRecords: true, - canSoftDeleteObjectRecords: false, - canDestroyObjectRecords: false, - }, - ], - fieldPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff', - canReadFieldValue: false, - canUpdateFieldValue: false, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - -Le `universalIdentifier` de ce rôle est ensuite référencé dans `application.config.ts` en tant que `defaultRoleUniversalIdentifier`. En d’autres termes : - -* **\*.role.ts** définit ce que le rôle de fonction par défaut peut faire. -* **application.config.ts** pointe vers ce rôle afin que vos fonctions héritent de ses autorisations. - -Notes : - -* Partez du rôle généré, puis restreignez-le progressivement en suivant le principe du moindre privilège. -* Remplacez `objectPermissions` et `fieldPermissions` par les objets/champs dont vos fonctions ont besoin. -* `permissionFlags` contrôlent l’accès aux capacités au niveau de la plateforme. Gardez-les au minimum ; n’ajoutez que ce dont vous avez besoin. -* Voir un exemple fonctionnel dans l’application Hello World : [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - -### Configuration et point d’entrée des fonctions logiques - -Chaque fichier de fonction utilise `defineFunction()` pour exporter une configuration avec un gestionnaire et des déclencheurs facultatifs. Utilisez le suffixe de fichier `*.function.ts` pour la détection automatique. - -```typescript -// src/app/createPostCard.function.ts -import { defineFunction } from 'twenty-sdk'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk'; -import Twenty, { type Person } from '~/generated'; - -const handler = async (params: RoutePayload) => { - const client = new Twenty(); // generated typed client - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : 'Hello world'; - - const result = await client.mutation({ - createPostCard: { - __args: { data: { name } }, - id: true, - name: true, - }, - }); - return result; -}; - -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'create-new-post-card', - timeoutSeconds: 2, - handler, - triggers: [ - // Public HTTP route trigger '/s/post-card/create' - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/post-card/create', - httpMethod: 'GET', - isAuthRequired: false, - }, - // Cron trigger (CRON pattern) - // { - // universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2', - // type: 'cron', - // pattern: '0 0 1 1 *', - // }, - // Database event trigger - // { - // universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156', - // type: 'databaseEvent', - // eventName: 'person.updated', - // updatedFields: ['name'], - // }, - ], -}); -``` - -Types de déclencheurs courants : - -* **route** : Expose votre fonction sur un chemin et une méthode HTTP **sous l’endpoint `/s/`** : - -> p. ex. `path: '/post-card/create',` -> appel sur `/s/post-card/create` - -* **cron** : Exécute votre fonction selon une planification à l’aide d’une expression CRON. -* **databaseEvent**: S'exécute lors des événements du cycle de vie des objets de l'espace de travail. Lorsque l'opération de l'événement est `updated`, des champs spécifiques à surveiller peuvent être spécifiés dans le tableau `updatedFields`. S'il est laissé indéfini ou vide, toute mise à jour déclenchera la fonction. - -> p. ex. `person.updated` - -Notes : - -* Le tableau `triggers` est facultatif. Les fonctions sans déclencheurs peuvent servir de fonctions utilitaires appelées par d’autres fonctions. -* Vous pouvez combiner plusieurs types de déclencheurs dans une seule fonction. - -### Charge utile du déclencheur de route - - - **Changement incompatible (v1.16, janvier 2026):** Le format de la charge utile du déclencheur de route a changé. Avant la v1.16, les paramètres de requête, les paramètres de chemin et le corps de la requête étaient envoyés directement en tant que charge utile. À partir de la v1.16, ils sont imbriqués dans un objet `RoutePayload` structuré. - - **Avant la v1.16 :** - - ```typescript - const handler = async (params) => { - const { param1, param2 } = params; // Direct access - }; - ``` - - **Après la v1.16 :** - - ```typescript - const handler = async (event: RoutePayload) => { - const { param1, param2 } = event.body; // Access via .body - const { queryParam } = event.queryStringParameters; - const { id } = event.pathParameters; - }; - ``` - - **Pour migrer les fonctions existantes :** Mettez à jour votre gestionnaire pour déstructurer à partir de `event.body`, `event.queryStringParameters` ou `event.pathParameters` plutôt que directement à partir de l'objet params. - - -Lorsqu’un déclencheur de route appelle votre fonction logique, elle reçoit un objet `RoutePayload` qui suit le format AWS HTTP API v2. Importez le type depuis `twenty-sdk` : - -```typescript -import { defineFunction, type RoutePayload } from 'twenty-sdk'; - -const handler = async (event: RoutePayload) => { - // Access request data - const { headers, queryStringParameters, pathParameters, body } = event; - - // HTTP method and path are available in requestContext - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -Le type `RoutePayload` a la structure suivante : - -| Nom de la propriété | Type | Description | -| ---------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------- | -| `headers` | `Record` | En-têtes HTTP (uniquement ceux répertoriés dans `forwardedRequestHeaders`) | -| `queryStringParameters` | `Record` | Paramètres de la chaîne de requête (plusieurs valeurs séparées par des virgules) | -| `pathParameters` | `Record` | Paramètres de chemin extraits du modèle de route (p. ex., `/users/:id` → `{ id: '123' }`) | -| `corps du message` | `object \| null` | Corps de la requête analysé (JSON) | -| `isBase64Encoded` | `booléen` | Indique si le corps est encodé en base64 | -| `requestContext.http.method` | `string` | Méthode HTTP (GET, POST, PUT, PATCH, DELETE) | -| `requestContext.http.path` | `string` | Chemin de la requête brut | - -### Transfert des en-têtes HTTP - -Par défaut, les en-têtes HTTP des requêtes entrantes ne sont pas transmis à votre fonction logique pour des raisons de sécurité. Pour accéder à des en-têtes spécifiques, listez-les explicitement dans le tableau `forwardedRequestHeaders` : - -```typescript -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'webhook-handler', - handler, - triggers: [ - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/webhook', - httpMethod: 'POST', - isAuthRequired: false, - forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], - }, - ], -}); -``` - -Dans votre gestionnaire, vous pouvez ensuite accéder à ces en-têtes : - -```typescript -const handler = async (event: RoutePayload) => { - const signature = event.headers['x-webhook-signature']; - const contentType = event.headers['content-type']; - - // Validate webhook signature... - return { received: true }; -}; -``` - - - Les noms d'en-têtes sont normalisés en minuscules. Accédez-y en utilisant des clés en minuscules (par exemple, `event.headers['content-type']`). - - -Vous pouvez créer de nouvelles fonctions de deux façons : - -* **Générée** : Exécutez `yarn entity:add` et choisissez l’option pour ajouter une nouvelle fonction. Cela génère un fichier de démarrage avec un gestionnaire et une configuration. -* **Manuelle** : Créez un nouveau fichier `*.function.ts` et utilisez `defineFunction()`, en suivant le même modèle. - -### Client typé généré - -`yarn app:dev` génère automatiquement le client Twenty typé dans `node_modules/twenty-sdk/generated`. Utilisez-le dans vos fonctions : - -```typescript -import Twenty from '~/generated'; - -const client = new Twenty(); -const { me } = await client.query({ me: { id: true, displayName: true } }); -``` - -Le client est régénéré automatiquement pendant l'exécution de `app:dev`. Redémarrez `app:dev` après avoir modifié vos objets ou lors de l’intégration à un nouvel espace de travail. - -#### Identifiants d’exécution dans les fonctions logiques - -Lorsque votre fonction s’exécute sur Twenty, la plateforme injecte des identifiants sous forme de variables d’environnement avant l’exécution de votre code : - -* `TWENTY_API_URL` : URL de base de l’API Twenty ciblée par votre application. -* `TWENTY_API_KEY` : Clé de courte durée limitée au rôle de fonction par défaut de votre application. - -Notes: - -* Vous n’avez pas besoin de passer l’URL ou la clé API au client généré. Il lit `TWENTY_API_URL` et `TWENTY_API_KEY` depuis process.env à l’exécution. -* Les autorisations de la clé API sont déterminées par le rôle référencé dans votre `application.config.ts` via `defaultRoleUniversalIdentifier`. Il s’agit du rôle par défaut utilisé par les fonctions logiques de votre application. -* Les applications peuvent définir des rôles pour appliquer le principe du moindre privilège. N’accordez que les autorisations dont vos fonctions ont besoin, puis faites pointer `defaultRoleUniversalIdentifier` vers l’identifiant universel de ce rôle. - -### Exemple Hello World - -Découvrez un exemple minimal de bout en bout qui démontre des objets, des fonctions et plusieurs déclencheurs [ici](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world) : - -## Configuration manuelle (sans l’outil de scaffolding) - -Même si nous recommandons d’utiliser `create-twenty-app` pour une expérience de démarrage optimale, vous pouvez également configurer un projet manuellement. N’installez pas la CLI globalement. Ajoutez plutôt `twenty-sdk` comme dépendance locale et reliez des scripts dans votre package.json : - -```bash filename="Terminal" -yarn add -D twenty-sdk -``` - -Ajoutez ensuite des scripts comme ceux-ci : - -```json filename="package.json" -{ - "scripts": { - "auth:login": "twenty auth:login", - "auth:logout": "twenty auth:logout", - "auth:status": "twenty auth:status", - "auth:switch": "twenty auth:switch", - "auth:list": "twenty auth:list", - "app:dev": "twenty app:dev", - "app:uninstall": "twenty app:uninstall", - "entity:add": "twenty entity:add", - "function:logs": "twenty function:logs", - "function:execute": "twenty function:execute", - "help": "twenty help" - } -} -``` - -Vous pouvez désormais exécuter les mêmes commandes via Yarn, par exemple `yarn app:dev`, etc. - -## Résolution des problèmes - -* Erreurs d’authentification : exécutez `yarn auth:login` et assurez-vous que votre clé API dispose des autorisations requises. -* Impossible de se connecter au serveur : vérifiez l’URL de l’API et que le serveur Twenty est accessible. -* Types ou client manquants/obsolètes : redémarrez `yarn app:dev`. -* Le mode dev ne se synchronise pas : assurez-vous que `yarn app:dev` est en cours d’exécution et que les modifications ne sont pas ignorées par votre environnement. - -Canal d’aide Discord : https://discord.com/channels/1130383047699738754/1130386664812982322 diff --git a/packages/twenty-docs/l/fr/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/fr/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4180b67161..0000000000 --- a/packages/twenty-docs/l/fr/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Features - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/it/developers/extend/apps/building.mdx b/packages/twenty-docs/l/it/developers/extend/apps/building.mdx deleted file mode 100644 index 9b11b396ee..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Architettura -description: Come funzionano le app di Twenty — sandboxing, ciclo di vita e componenti di base. -icon: sitemap ---- - -Le app di Twenty sono pacchetti TypeScript che estendono il tuo spazio di lavoro con oggetti personalizzati, logica, componenti dell'interfaccia utente (UI) e funzionalità di IA. Vengono eseguite sulla piattaforma Twenty con sandboxing completo e controlli delle autorizzazioni. - -## Come funzionano le app - -Un'app è una raccolta di **entità** dichiarate utilizzando le funzioni `defineEntity()` del pacchetto `twenty-sdk`. L'SDK rileva queste dichiarazioni tramite analisi dell'AST in fase di build e produce un **manifest** — una descrizione completa di ciò che la tua app aggiunge a uno spazio di lavoro. - -``` -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 -``` - - - **L'organizzazione dei file dipende da te.** Il rilevamento delle entità è basato sull'AST — l'SDK trova le chiamate a `export default defineEntity(...)` indipendentemente da dove si trova il file. La struttura delle cartelle sopra è una convenzione, non un requisito. - - -## Tipi di entità - -| Entità | Scopo | Documentazione | -| -------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------- | -| **Applicazione** | Identità dell'app, autorizzazioni, variabili | [Modello dati](/l/it/developers/extend/apps/data-model) | -| **Ruolo** | Set di autorizzazioni per oggetti e campi | [Modello dati](/l/it/developers/extend/apps/data-model) | -| **Oggetto** | Tabelle di dati personalizzate con campi | [Modello dati](/l/it/developers/extend/apps/data-model) | -| **Campo** | Estendi gli oggetti esistenti, definisci le relazioni | [Modello dati](/l/it/developers/extend/apps/data-model) | -| **Funzione logica** | TypeScript lato server con trigger | [Funzioni logiche](/l/it/developers/extend/apps/logic-functions) | -| **Componente front-end** | UI React in sandbox nella pagina di Twenty | [Componenti front-end](/l/it/developers/extend/apps/front-components) | -| **Abilità** | Istruzioni riutilizzabili per agenti IA | [Abilità e agenti](/l/it/developers/extend/apps/skills-and-agents) | -| **Agente** | Assistenti IA con prompt personalizzati | [Abilità e agenti](/l/it/developers/extend/apps/skills-and-agents) | -| **Vista** | Viste di elenco dei record preconfigurate | [Layout](/l/it/developers/extend/apps/layout) | -| **Voce del menu di navigazione** | Voci della barra laterale personalizzate | [Layout](/l/it/developers/extend/apps/layout) | -| **Layout di pagina** | Schede e widget personalizzati nelle pagine dei record | [Layout](/l/it/developers/extend/apps/layout) | - -## Sandboxing - -* **Le funzioni logiche** vengono eseguite in processi Node.js isolati sul server. Accedono ai dati solo tramite il client API tipizzato, con ambito limitato alle autorizzazioni del ruolo dell'app. -* **I componenti front-end** vengono eseguiti in Web Workers utilizzando il Remote DOM — isolati dalla pagina principale ma renderizzando elementi DOM nativi (non iframe). Comunicano con Twenty tramite un'API host basata sul passaggio di messaggi. -* **Le autorizzazioni** vengono applicate a livello di API. Il token di runtime (`TWENTY_APP_ACCESS_TOKEN`) è derivato dal ruolo definito in `defineApplication()`. - -## Ciclo di vita dell'app - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ Install flow │ -│ upload → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -├─────────────────────────────────────────────────────────┤ -│ Publish │ -│ npm publish → appears in Twenty marketplace │ -└─────────────────────────────────────────────────────────┘ -``` - -* **`yarn twenty dev`** — monitora i file sorgente e sincronizza in tempo reale le modifiche su un server Twenty connesso. Il client API tipizzato viene rigenerato automaticamente quando lo schema cambia. -* **`yarn twenty build`** — compila TypeScript, crea i bundle delle funzioni logiche e dei componenti front-end con esbuild e produce un manifest. -* **Hook di pre/post-installazione** — funzioni logiche opzionali che vengono eseguite durante l'installazione. Consulta [Funzioni logiche](/l/it/developers/extend/apps/logic-functions) per i dettagli. - -## Prossimi passaggi - - - - Definisci oggetti, campi, ruoli e relazioni. - - - Funzioni lato server con trigger HTTP, cron ed eventi. - - - Componenti React in sandbox nell'UI di Twenty. - - - Viste, voci di navigazione e layout delle pagine dei record. - - - Abilità e agenti IA con prompt personalizzati. - - - Comandi CLI, test, asset, remoti e CI. - - - Distribuisci su un server o pubblica sul marketplace. - - diff --git a/packages/twenty-docs/l/it/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/it/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index fece9c0ddd..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI e test -description: Comandi CLI, configurazione dei test, asset pubblici, pacchetti npm, remoti e configurazione CI. -icon: terminal ---- - -## Asset pubblici (cartella `public/`) - -La cartella `public/` alla radice della tua app contiene file statici — immagini, icone, font o qualsiasi altro asset di cui la tua app ha bisogno a runtime. Questi file sono inclusi automaticamente nelle build, sincronizzati durante la modalità di sviluppo e caricati sul server. - -I file posizionati in `public/` sono: - -* **Pubblicamente accessibili** — una volta sincronizzati sul server, gli asset sono serviti a un URL pubblico. Non è necessaria alcuna autenticazione per accedervi. -* **Disponibili nei componenti front-end** — usa gli URL degli asset per visualizzare immagini, icone o qualsiasi media all'interno dei tuoi componenti React. -* **Disponibili nelle funzioni logiche** — fai riferimento agli URL degli asset nelle email, nelle risposte API o in qualsiasi logica lato server. -* **Usati per i metadati del marketplace** — i campi `logoUrl` e `screenshots` in `defineApplication()` fanno riferimento a file di questa cartella (ad es., `public/logo.png`). Questi vengono visualizzati nel marketplace quando la tua app viene pubblicata. -* **Sincronizzati automaticamente in modalità dev** — quando aggiungi, aggiorni o elimini un file in `public/`, viene sincronizzato automaticamente con il server. Nessun riavvio necessario. -* **Inclusi nelle build** — `yarn twenty build` raggruppa tutti gli asset pubblici nell'output di distribuzione. - -### Accedere agli asset pubblici con `getPublicAssetUrl` - -Usa l'helper `getPublicAssetUrl` da `twenty-sdk` per ottenere l'URL completo di un file nella tua directory `public/`. Funziona sia nelle funzioni logiche che nei componenti front-end. - -**In una funzione logica:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, -}); -``` - -**In un componente front-end:** - -```tsx src/front-components/company-card.tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -L'argomento `path` è relativo alla cartella `public/` della tua app. Sia `getPublicAssetUrl('logo.png')` sia `getPublicAssetUrl('public/logo.png')` risolvono allo stesso URL — il prefisso `public/` viene rimosso automaticamente se presente. - -## Uso dei pacchetti npm - -Puoi installare e usare qualsiasi pacchetto npm nella tua app. Sia le funzioni logiche sia i componenti front-end vengono impacchettati con [esbuild](https://esbuild.github.io/), che incorpora tutte le dipendenze nell'output — non sono necessari i `node_modules` a runtime. - -### Installazione di un pacchetto - -```bash filename="Terminal" -yarn add axios -``` - -Quindi importalo nel tuo codice: - -```ts src/logic-functions/fetch-data.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import axios from 'axios'; - -const handler = async (): Promise => { - 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, -}); -``` - -Lo stesso vale per i componenti front-end: - -```tsx src/front-components/chart.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { format } from 'date-fns'; - -const DateWidget = () => { - return

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### Come funziona il bundling - -La fase di build usa esbuild per produrre un singolo file autonomo per ogni funzione logica e per ogni componente front-end. Tutti i pacchetti importati sono incorporati nel bundle. - -**Le funzioni logiche** vengono eseguite in un ambiente Node.js. I moduli integrati di Node (`fs`, `path`, `crypto`, `http`, ecc.) sono disponibili e non necessitano di essere installati. - -**I componenti front-end** vengono eseguiti in un Web Worker. I moduli integrati di Node non sono disponibili — solo le API del browser e i pacchetti npm che funzionano in un ambiente browser. - -Entrambi gli ambienti hanno `twenty-client-sdk/core` e `twenty-client-sdk/metadata` disponibili come moduli preforniti — questi non vengono inclusi nel bundle ma vengono risolti a runtime dal server. - -## Testare la tua app - -L'SDK fornisce API programmatiche che ti consentono di compilare, distribuire, installare e disinstallare la tua app dal codice di test. In combinazione con [Vitest](https://vitest.dev/) e i client API tipizzati, puoi scrivere test di integrazione che verificano che la tua app funzioni end-to-end contro un server Twenty reale. - -### Impostazione - -L'app generata tramite scaffolding include già Vitest. Se la configuri manualmente, installa le dipendenze: - -```bash filename="Terminal" -yarn add -D vitest vite-tsconfig-paths -``` - -Crea un `vitest.config.ts` alla radice della tua app: - -```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', - }, - }, -}); -``` - -Crea un file di setup che verifichi che il server sia raggiungibile prima dell'esecuzione dei test: - -```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), - ); -}); -``` - -### API programmatiche dell'SDK - -Il sottopercorso `twenty-sdk/cli` esporta funzioni che puoi chiamare direttamente dal codice di test: - -| Funzione | Descrizione | -| -------------- | ----------------------------------------------- | -| `appBuild` | Compila l'app e, opzionalmente, crea un tarball | -| `appDeploy` | Carica un tarball sul server | -| `appInstall` | Installa l'app nello spazio di lavoro attivo | -| `appUninstall` | Disinstalla l'app dallo spazio di lavoro attivo | - -Ogni funzione restituisce un oggetto risultato con `success: boolean` e `data` oppure `error`. - -### Scrivere un test di integrazione - -Ecco un esempio completo che compila, distribuisce e installa l'app, quindi verifica che compaia nello spazio di lavoro: - -```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(); - }); -}); -``` - -### Esecuzione dei test - -Assicurati che il tuo server Twenty locale sia in esecuzione, quindi: - -```bash filename="Terminal" -yarn test -``` - -Oppure in modalità watch durante lo sviluppo: - -```bash filename="Terminal" -yarn test:watch -``` - -### Controllo dei tipi - -Puoi anche eseguire il controllo dei tipi sulla tua app senza eseguire i test: - -```bash filename="Terminal" -yarn twenty typecheck -``` - -Questo esegue `tsc --noEmit` e riporta eventuali errori di tipo. - -## Riferimento CLI - -Oltre a `dev`, `build`, `add` e `typecheck`, la CLI fornisce comandi per eseguire funzioni, visualizzare i log e gestire le installazioni delle app. - -### Esecuzione delle funzioni (`yarn twenty exec`) - -Esegui manualmente una funzione logica senza attivarla tramite HTTP, cron o evento del database: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Visualizzazione dei log delle funzioni (`yarn twenty logs`) - -Esegui lo streaming dei log di esecuzione per le funzioni logiche della tua app: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -Questo è diverso da `yarn twenty server logs`, che mostra i log del container Docker. `yarn twenty logs` mostra i log di esecuzione delle funzioni della tua app dal server Twenty. - - -### Disinstallazione di un'app (`yarn twenty uninstall`) - -Rimuovi la tua app dallo spazio di lavoro attivo: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## Gestione dei remoti - -Un **remoto** è un server Twenty a cui la tua app si connette. Durante la configurazione, lo strumento di scaffolding ne crea uno automaticamente per te. Puoi aggiungere altri remoti o passare da uno all'altro in qualsiasi momento. - -```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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Le tue credenziali sono archiviate in `~/.twenty/config.json`. - -## CI con GitHub Actions - -Lo strumento di scaffolding genera un workflow GitHub Actions pronto all'uso in `.github/workflows/ci.yml`. Esegue automaticamente i test di integrazione a ogni push su `main` e sulle pull request. - -Il workflow: - -1. Esegue il checkout del tuo codice -2. Avvia un server Twenty temporaneo utilizzando l'azione `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Installa le dipendenze con `yarn install --immutable` -4. Esegue `yarn test` con `TWENTY_API_URL` e `TWENTY_API_KEY` iniettati dagli output dell'azione - -```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 }} -``` - -Non è necessario configurare alcun secret — l'azione `spawn-twenty-docker-image` avvia un server Twenty effimero direttamente nel runner e fornisce i dettagli di connessione. Il secret `GITHUB_TOKEN` è fornito automaticamente da GitHub. - -Per fissare una versione specifica di Twenty invece di `latest`, modifica la variabile d'ambiente `TWENTY_VERSION` all'inizio del workflow. diff --git a/packages/twenty-docs/l/it/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/it/developers/extend/apps/data-model.mdx deleted file mode 100644 index c7d2491cd4..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,494 +0,0 @@ ---- -title: Modello dati -description: Definisci oggetti, campi, ruoli e metadati dell'applicazione con Twenty SDK. -icon: database ---- - -Il pacchetto `twenty-sdk` fornisce le funzioni `defineEntity` per dichiarare il modello di dati della tua applicazione. Devi usare `export default defineEntity({...})` affinché l'SDK rilevi le tue entità. Queste funzioni convalidano la configurazione in fase di build e offrono il completamento automatico nell'IDE e la sicurezza dei tipi. - - - **L'organizzazione dei file dipende da te.** - Il rilevamento delle entità è basato sull'AST — l'SDK trova le chiamate a `export default defineEntity(...)` indipendentemente da dove si trova il file. Raggruppare i file per tipo (ad es., `logic-functions/`, `roles/`) è solo una convenzione, non un requisito. - - - - - -I ruoli incapsulano i permessi sugli oggetti e sulle azioni del tuo spazio di lavoro. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -Ogni app deve avere esattamente una chiamata a `defineApplication` che descrive: - -* **Identità**: identificatori, nome visualizzato e descrizione. -* **Autorizzazioni**: quale ruolo usano le sue funzioni e i componenti front-end. -* **Variabili (opzionali)**: coppie chiave–valore esposte alle funzioni come variabili d'ambiente. -* **(Opzionali) Funzioni di pre-installazione/post-installazione**: funzioni logiche che vengono eseguite prima o dopo l'installazione. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -export default defineApplication({ - universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Note: -* I campi `universalIdentifier` sono ID deterministici che possiedi. Generali una volta e mantienili stabili tra una sincronizzazione e l'altra. -* `applicationVariables` diventano variabili d'ambiente per le tue funzioni e i componenti front-end (ad esempio, `DEFAULT_RECIPIENT_NAME` è disponibile come `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` deve fare riferimento a un ruolo definito con `defineRole()` (vedi sopra). -* Le funzioni di pre-installazione e post-installazione vengono rilevate automaticamente durante il build del manifesto — non è necessario farvi riferimento in `defineApplication()`. - -#### Metadati del marketplace - -Se prevedi di [pubblicare la tua app](/l/it/developers/extend/apps/publishing), questi campi opzionali controllano come appare nel marketplace: - -| Campo | Descrizione | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -| `author` | Nome dell'autore o dell'azienda | -| `category` | Categoria dell'app per il filtraggio nel marketplace | -| `logoUrl` | Percorso al logo della tua app (ad es., `public/logo.png`) | -| `screenshots` | Array di percorsi degli screenshot (ad es., `public/screenshot-1.png`) | -| `aboutDescription` | Descrizione markdown più lunga per la scheda "Informazioni". Se omesso, il marketplace utilizza il `README.md` del pacchetto da npm | -| `websiteUrl` | Link al tuo sito web | -| `termsUrl` | Link ai Termini di servizio | -| `emailSupport` | Indirizzo email di supporto | -| `issueReportUrl` | Link al sistema di tracciamento dei problemi | - -#### Ruoli e permessi - -Il `defaultRoleUniversalIdentifier` in `application-config.ts` indica il ruolo predefinito utilizzato dalle funzioni logiche e dai componenti front-end della tua app. Vedi `defineRole` sopra per i dettagli. - -* Il token di runtime iniettato come `TWENTY_APP_ACCESS_TOKEN` è derivato da questo ruolo. -* Il client tipizzato è limitato ai permessi concessi a quel ruolo. -* Segui il principio del privilegio minimo: crea un ruolo dedicato con solo i permessi necessari alle tue funzioni. - -##### Ruolo funzione predefinito - -Quando esegui lo scaffolding di una nuova app, la CLI crea un file di ruolo predefinito: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -L'`universalIdentifier` di questo ruolo viene referenziato in `application-config.ts` come `defaultRoleUniversalIdentifier`: - -* **\*.role.ts** definisce ciò che il ruolo può fare. -* **application-config.ts** punta a quel ruolo in modo che le tue funzioni ne ereditino i permessi. - -Note: -* Parti dal ruolo generato dallo scaffolder, quindi restringilo progressivamente seguendo il principio del privilegio minimo. -* Sostituisci `objectPermissions` e `fieldPermissions` con gli oggetti e i campi di cui le tue funzioni hanno realmente bisogno. -* `permissionFlags` controllano l'accesso alle funzionalità a livello di piattaforma. Mantienili al minimo. -* Vedi un esempio funzionante: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Gli oggetti personalizzati descrivono sia lo schema sia il comportamento per i record nel tuo spazio di lavoro. Usa `defineObject()` per definire oggetti con convalida integrata: - -```ts postCard.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, - }, - ], -}); -``` - -Punti chiave: - -* Usa `defineObject()` per una convalida integrata e un migliore supporto IDE. -* Il `universalIdentifier` deve essere univoco e stabile tra i deployment. -* Ogni campo richiede un `name`, `type`, `label` e il proprio `universalIdentifier` stabile. -* L'array `fields` è facoltativo: puoi definire oggetti senza campi personalizzati. -* Puoi generare nuovi oggetti con `yarn twenty add`, che ti guida nella denominazione, nei campi e nelle relazioni. - - -**I campi base vengono creati automaticamente.** Quando definisci un oggetto personalizzato, Twenty aggiunge automaticamente i campi standard -come `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt`. -Non è necessario definirli nel tuo array `fields` — aggiungi solo i tuoi campi personalizzati. -Puoi sovrascrivere i campi predefiniti definendo un campo con lo stesso nome nel tuo array `fields`, -ma non è consigliato. - - - - - -Usa `defineField()` per aggiungere campi a oggetti che non possiedi — come gli oggetti standard di Twenty (Person, Company, ecc.) o oggetti di altre app. A differenza dei campi inline in `defineObject()`, i campi autonomi richiedono un `objectUniversalIdentifier` per specificare quale oggetto estendono: - -```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' }, - ], -}); -``` - -Punti chiave: -* `objectUniversalIdentifier` identifica l'oggetto di destinazione. Per gli oggetti standard, usa `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` esportati da `twenty-sdk`. -* Quando definisci campi inline in `defineObject()`, **non** hai bisogno di `objectUniversalIdentifier` — viene ereditato dall'oggetto padre. -* `defineField()` è l'unico modo per aggiungere campi a oggetti che non hai creato con `defineObject()`. - - - - -Le relazioni collegano gli oggetti tra loro. In Twenty, le relazioni sono sempre **bidirezionali** — definisci entrambi i lati e ciascun lato fa riferimento all'altro. - -Esistono due tipi di relazione: - -| Tipo di relazione | Descrizione | Ha una chiave esterna? | -| ----------------- | --------------------------------------------------------------------- | ---------------------- | -| `MANY_TO_ONE` | Molti record di questo oggetto puntano a un record della destinazione | Sì (`joinColumnName`) | -| `ONE_TO_MANY` | Un record di questo oggetto ha molti record della destinazione | No (lato inverso) | - -#### Come funzionano le relazioni - -Ogni relazione richiede **due campi** che fanno riferimento l'uno all'altro: - -1. Il lato **MANY_TO_ONE** — risiede sull'oggetto che detiene la chiave esterna -2. Il lato **ONE_TO_MANY** — risiede sull'oggetto che possiede la collezione - -Entrambi i campi usano `FieldType.RELATION` e si riferiscono reciprocamente tramite `relationTargetFieldMetadataUniversalIdentifier`. - -#### Esempio: Post Card ha molti destinatari - -Supponiamo che un `PostCard` possa essere inviato a molti record `PostCardRecipient`. Ogni destinatario appartiene esattamente a una sola cartolina. - -**Passaggio 1: definisci il lato ONE_TO_MANY su PostCard** (il lato "uno"): - -```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, - }, -}); -``` - -**Passaggio 2: definisci il lato MANY_TO_ONE su PostCardRecipient** (il lato "molti" — contiene la chiave esterna): - -```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', - }, -}); -``` - - -**Importazioni circolari:** Entrambi i campi di relazione fanno riferimento all'`universalIdentifier` dell'altro. Per evitare problemi di importazioni circolari, esporta gli ID dei campi come costanti denominate da ciascun file e importale nell'altro file. Il sistema di build le risolve in fase di compilazione. - - -#### Relazioni con gli oggetti standard - -Per creare una relazione con un oggetto Twenty integrato (Person, Company, ecc.), usa `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', - }, -}); -``` - -#### Proprietà dei campi di relazione - -| Proprietà | Obbligatorio | Descrizione | -| ------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | -| `tipo` | Sì | Deve essere `FieldType.RELATION` | -| `relationTargetObjectMetadataUniversalIdentifier` | Sì | L'`universalIdentifier` dell'oggetto di destinazione | -| `relationTargetFieldMetadataUniversalIdentifier` | Sì | L'`universalIdentifier` del campo corrispondente sull'oggetto di destinazione | -| `universalSettings.relationType` | Sì | `RelationType.MANY_TO_ONE` o `RelationType.ONE_TO_MANY` | -| `universalSettings.onDelete` | Solo MANY_TO_ONE | Cosa accade quando il record referenziato viene eliminato: `CASCADE`, `SET_NULL`, `RESTRICT` o `NO_ACTION` | -| `universalSettings.joinColumnName` | Solo MANY_TO_ONE | Nome della colonna del database per la chiave esterna (ad es., `postCardId`) | - -#### Campi di relazione inline in defineObject - -Puoi anche definire i campi di relazione direttamente all'interno di `defineObject()`. In tal caso, ometti `objectUniversalIdentifier` — viene ereditato dall'oggetto padre: - -```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 - ], -}); -``` - - - -## Creazione di entità con lo scaffolding tramite `yarn twenty add` - -Invece di creare manualmente i file delle entità, puoi usare lo scaffolder interattivo: - -```bash filename="Terminal" -yarn twenty add -``` - -Questo ti chiede di scegliere un tipo di entità e ti guida attraverso i campi richiesti. Genera un file pronto all'uso con un `universalIdentifier` stabile e la corretta chiamata a `defineEntity()`. - -Puoi anche passare direttamente il tipo di entità per saltare il primo prompt: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Tipi di entità disponibili - -| Tipo di entità | Comando | File generato | -| ---------------------------- | ------------------------------------ | ------------------------------------------------------- | -| Oggetto | `yarn twenty add object` | `src/objects/\.ts` | -| Campo | `yarn twenty add field` | `src/fields/\.ts` | -| Funzione logica | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Componente front-end | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Ruolo | `yarn twenty add role` | `src/roles/\.ts` | -| Abilità | `yarn twenty add skill` | `src/skills/\.ts` | -| Agente | `yarn twenty add agent` | `src/agents/\.ts` | -| Vista | `yarn twenty add view` | `src/views/\.ts` | -| Voce del menu di navigazione | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Layout di pagina | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### Cosa genera lo scaffolder - -Ogni tipo di entità ha il proprio template. Ad esempio, `yarn twenty add object` richiede: - -1. **Nome (singolare)** — ad es., `invoice` -2. **Nome (plurale)** — ad es., `invoices` -3. **Etichetta (singolare)** — compilata automaticamente dal nome (ad es., `Invoice`) -4. **Etichetta (plurale)** — compilata automaticamente (ad es., `Invoices`) -5. **Creare una vista e una voce di navigazione?** — se rispondi sì, lo scaffolder genera anche una vista corrispondente e un link nella barra laterale per il nuovo oggetto. - -Gli altri tipi di entità hanno prompt più semplici — la maggior parte chiede solo un nome. - -Il tipo di entità `field` è più dettagliato: chiede il nome del campo, l'etichetta, il tipo (da un elenco di tutti i tipi di campo disponibili come `TEXT`, `NUMBER`, `SELECT`, `RELATION`, ecc.) e l'`universalIdentifier` dell'oggetto di destinazione. - -### Percorso di output personalizzato - -Usa il flag `--path` per posizionare il file generato in una posizione personalizzata: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/it/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/it/developers/extend/apps/front-components.mdx deleted file mode 100644 index ace72b9528..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,419 +0,0 @@ ---- -title: Componenti front-end -description: Crea componenti React che vengono renderizzati all'interno della UI di Twenty con isolamento in sandbox. -icon: window-maximize ---- - -I componenti front-end sono componenti React che vengono renderizzati direttamente all'interno della UI di Twenty. Vengono eseguiti in un **Web Worker** isolato utilizzando Remote DOM — il tuo codice è in sandbox ma viene renderizzato in modo nativo nella pagina, non in un iframe. - -## Dove possono essere utilizzati i componenti front. - -I componenti front possono essere renderizzati in due posizioni all'interno di Twenty: - -* **Pannello laterale** — I componenti front non headless si aprono nel pannello laterale destro. Questo è il comportamento predefinito quando un componente front viene avviato dal menu comandi. -* **Widget (dashboard e pagine dei record)** — I componenti front possono essere incorporati come widget all'interno dei layout di pagina. Quando si configura una dashboard o il layout di una pagina record, gli utenti possono aggiungere un widget del componente front. - -## Esempio di base - -Il modo più rapido per vedere in azione un componente front-end è registrarlo come **comando**. Aggiungere un campo `command` con `isPinned: true` lo fa apparire come pulsante di azione rapida nell'angolo in alto a destra della pagina — nessun layout di pagina necessario: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', - name: 'hello-world', - description: 'A simple front component', - component: HelloWorld, - command: { - universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345', - shortLabel: 'Hello', - label: 'Hello World', - icon: 'IconBolt', - isPinned: true, - availabilityType: 'GLOBAL', - }, -}); -``` - -Dopo la sincronizzazione con `yarn twenty dev` (o eseguendo una volta sola `yarn twenty dev --once`), l'azione rapida appare nell'angolo in alto a destra della pagina: - -
- Pulsante di azione rapida nell'angolo in alto a destra -
- -Fai clic per renderizzare il componente in linea. - -## Campi di configurazione - -| Campo | Obbligatorio | Descrizione | -| --------------------- | ------------ | ---------------------------------------------------------------------------------------- | -| `universalIdentifier` | Sì | ID univoco stabile per questo componente | -| `component` | Sì | Una funzione di componente React | -| `name` | No | Nome visualizzato | -| `description` | No | Descrizione di ciò che fa il componente | -| `isHeadless` | No | Imposta su `true` se il componente non ha una UI visibile (vedi sotto) | -| `command` | No | Registra il componente come comando (vedi [opzioni del comando](#command-options) sotto) | - -## Posizionare un componente front-end su una pagina - -Oltre ai comandi, puoi incorporare un componente front-end direttamente in una pagina di record aggiungendolo come widget in un **layout di pagina**. Vedi la sezione [definePageLayout](/l/it/developers/extend/apps/skills-and-agents#definepagelayout) per i dettagli. - -## Headless vs non headless - -I componenti front prevedono due modalità di rendering controllate dall'opzione `isHeadless`: - -**Non headless (predefinito)** — Il componente renderizza un'interfaccia utente visibile. Quando viene avviato dal menu comandi, si apre nel pannello laterale. Questo è il comportamento predefinito quando `isHeadless` è `false` o omesso. - -**Headless (`isHeadless: true`)** — Il componente viene montato in modo invisibile in background. Non apre il pannello laterale. I componenti headless sono pensati per azioni che eseguono una logica e poi si smontano — ad esempio, eseguire un'attività asincrona, navigare a una pagina o mostrare una finestra modale di conferma. Si abbinano naturalmente ai componenti Command dell'SDK descritti di seguito. - -```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, -}); -``` - -Poiché il componente restituisce `null`, Twenty evita di renderizzare un contenitore per esso — non appare alcuno spazio vuoto nel layout. Il componente ha comunque accesso a tutti gli hook e all'API di comunicazione con l'host. - -## Componenti Command dell'SDK - -Il pacchetto `twenty-sdk` fornisce quattro componenti di supporto Command progettati per i componenti front headless. Ogni componente esegue un'azione al montaggio, gestisce gli errori mostrando una notifica snackbar e smonta automaticamente il componente front al termine. - -Importali da `twenty-sdk/command`: - -* **`Command`** — Esegue una callback asincrona tramite la prop `execute`. -* **`CommandLink`** — Naviga verso un percorso dell'app. Props: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — Apre una finestra modale di conferma. Se l'utente conferma, esegue la callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — Apre una specifica pagina del pannello laterale. Props: `page`, `pageTitle`, `pageIcon`. - -Ecco un esempio completo di componente front headless che usa `Command` per eseguire un'azione dal menu comandi: - -```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 ; -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', - name: 'run-action', - description: 'Creates a task from the command menu', - component: RunAction, - isHeadless: true, - command: { - universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', - label: 'Run my action', - icon: 'IconPlayerPlay', - }, -}); -``` - -E un esempio che usa `CommandModal` per chiedere conferma prima di eseguire: - -```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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, - command: { - universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567', - label: 'Delete draft', - icon: 'IconTrash', - }, -}); -``` - -## Accesso al contesto di runtime - -All'interno del tuo componente, usa gli hook dell'SDK per accedere all'utente corrente, al record e all'istanza del componente: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Hook disponibili: - -| Hook | Restituisce | Descrizione | -| --------------------------------------------- | ----------------- | --------------------------------------------------------------------- | -| `useUserId()` | `string` o `null` | L'ID dell'utente corrente | -| `useRecordId()` | `string` o `null` | L'ID del record corrente (quando posizionato su una pagina di record) | -| `useFrontComponentId()` | `string` | L'ID di questa istanza di componente | -| `useFrontComponentExecutionContext(selector)` | varia | Accedi all'intero contesto di esecuzione con una funzione selettore | - -## API di comunicazione con l'host - -I componenti front-end possono attivare navigazione, modali e notifiche utilizzando funzioni da `twenty-sdk`: - -| Funzione | Descrizione | -| ----------------------------------------------- | ------------------------------------- | -| `navigate(to, params?, queryParams?, options?)` | Naviga a una pagina dell'app | -| `openSidePanelPage(params)` | Apri un pannello laterale | -| `closeSidePanel()` | Chiudi il pannello laterale | -| `openCommandConfirmationModal(params)` | Mostra una finestra di conferma | -| `enqueueSnackbar(params)` | Mostra una notifica toast | -| `unmountFrontComponent()` | Smonta il componente | -| `updateProgress(progress)` | Aggiorna un indicatore di avanzamento | - -Ecco un esempio che usa l'API host per mostrare una snackbar e chiudere il pannello laterale dopo il completamento di un'azione: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -## Opzioni del comando - -Aggiungere un campo `command` a `defineFrontComponent` registra il componente nel menu comandi (Cmd+K). Se `isPinned` è `true`, compare anche come pulsante di azione rapida nell'angolo in alto a destra della pagina. - -| Campo | Obbligatorio | Descrizione | -| --------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Sì | ID univoco stabile per il comando | -| `label` | Sì | Etichetta completa mostrata nel menu comandi (Cmd+K) | -| `shortLabel` | No | Etichetta breve visualizzata sul pulsante di azione rapida fissato | -| `icon` | No | Nome dell'icona visualizzato accanto all'etichetta (ad es. `'IconBolt'`, `'IconSend'`) | -| `isPinned` | No | Quando `true`, mostra il comando come pulsante di azione rapida nell'angolo in alto a destra della pagina | -| `availabilityType` | No | Controlla dove compare il comando: `'GLOBAL'` (sempre disponibile), `'RECORD_SELECTION'` (solo quando sono selezionati dei record) o `'FALLBACK'` (mostrato quando nessun altro comando corrisponde) | -| `availabilityObjectUniversalIdentifier` | No | Limita il comando alle pagine di uno specifico tipo di oggetto (ad es. solo sui record Company) | -| `conditionalAvailabilityExpression` | No | Un'espressione booleana per controllare dinamicamente se il comando è visibile (vedi sotto) | - -## Espressioni di disponibilità condizionale - -Il campo `conditionalAvailabilityExpression` consente di controllare quando un comando è visibile in base al contesto della pagina corrente. Importa variabili tipizzate e operatori da `twenty-sdk` per costruire espressioni: - -```tsx -import { - defineFrontComponent, - pageType, - numberOfSelectedRecords, - objectPermissions, - everyEquals, - isDefined, -} from 'twenty-sdk/define'; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'bulk-action', - component: BulkAction, - command: { - universalIdentifier: '...', - label: 'Bulk Update', - availabilityType: 'RECORD_SELECTION', - conditionalAvailabilityExpression: everyEquals( - objectPermissions, - 'canUpdateObjectRecords', - true, - ), - }, -}); -``` - -**Variabili di contesto** — rappresentano lo stato corrente della pagina: - -| Variabile | Tipo | Descrizione | -| ------------------------------ | --------- | ------------------------------------------------------------------------ | -| `pageType` | `string` | Tipo di pagina corrente (ad es. `'RecordIndexPage'`, `'RecordShowPage'`) | -| `isInSidePanel` | `boolean` | Indica se il componente è renderizzato in un pannello laterale | -| `numberOfSelectedRecords` | `number` | Numero di record attualmente selezionati | -| `isSelectAll` | `boolean` | Indica se "seleziona tutto" è attivo | -| `selectedRecords` | `array` | Gli oggetti dei record selezionati | -| `favoriteRecordIds` | `array` | ID dei record aggiunti ai preferiti | -| `objectPermissions` | `object` | Autorizzazioni per il tipo di oggetto corrente | -| `targetObjectReadPermissions` | `object` | Autorizzazioni di lettura per l'oggetto di destinazione | -| `targetObjectWritePermissions` | `object` | Autorizzazioni di scrittura per l'oggetto di destinazione | -| `featureFlags` | `object` | Flag delle funzionalità attivi | -| `objectMetadataItem` | `object` | Metadati del tipo di oggetto corrente | -| `hasAnySoftDeleteFilterOnView` | `boolean` | Indica se la vista corrente ha un filtro di soft-delete | - -**Operatori** — combinano variabili in espressioni booleane: - -| Operatore | Descrizione | -| ----------------------------------- | -------------------------------------------------------------------- | -| `isDefined(value)` | `true` se il valore non è null/undefined | -| `isNonEmptyString(value)` | `true` se il valore è una stringa non vuota | -| `includes(array, value)` | `true` se l'array contiene il valore | -| `includesEvery(array, prop, value)` | `true` se la proprietà di ogni elemento include il valore | -| `every(array, prop)` | `true` se la proprietà è truthy su ogni elemento | -| `everyDefined(array, prop)` | `true` se la proprietà è definita su ogni elemento | -| `everyEquals(array, prop, value)` | `true` se la proprietà è uguale al valore su ogni elemento | -| `some(array, prop)` | `true` se la proprietà è truthy su almeno un elemento | -| `someDefined(array, prop)` | `true` se la proprietà è definita su almeno un elemento | -| `someEquals(array, prop, value)` | `true` se la proprietà è uguale al valore su almeno un elemento | -| `someNonEmptyString(array, prop)` | `true` se la proprietà è una stringa non vuota su almeno un elemento | -| `none(array, prop)` | `true` se la proprietà è falsy su ogni elemento | -| `noneDefined(array, prop)` | `true` se la proprietà è undefined su ogni elemento | -| `noneEquals(array, prop, value)` | `true` se la proprietà non è uguale al valore su alcun elemento | - -## Asset pubblici - -I componenti front-end possono accedere ai file dalla directory `public/` dell'app utilizzando `getPublicAssetUrl`: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -Vedi la [sezione sugli asset pubblici](/l/it/developers/extend/apps/cli-and-testing#public-assets-public-folder) per i dettagli. - -## Stile - -I componenti front-end supportano diversi approcci di styling. Puoi usare: - -* **Stili inline** — `style={{ color: 'red' }}` -* **Componenti Twenty UI** — importali da `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar e altro) -* **Emotion** — CSS-in-JS con `@emotion/react` -* **Styled-components** — pattern `styled.div` -* **Tailwind CSS** — classi di utilità -* **Qualsiasi libreria CSS-in-JS** compatibile con React - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/it/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/it/developers/extend/apps/getting-started.mdx deleted file mode 100644 index 6a18d413cf..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,282 +0,0 @@ ---- -title: Per iniziare -icon: rocket -description: Crea la tua prima app Twenty in pochi minuti. ---- - -## Cosa sono le app? - -Le app ti consentono di estendere Twenty con oggetti, campi, funzioni logiche, componenti front-end, competenze IA e altro ancora — il tutto gestito come codice. Invece di configurare tutto tramite l'interfaccia utente, definisci in TypeScript il modello dati e la logica e li distribuisci in uno o più spazi di lavoro. - -## Prerequisiti - -Prima di iniziare, assicurati che quanto segue sia installato sul tuo computer: - -* **Node.js 24+** — [Scarica qui](https://nodejs.org/) -* **Yarn 4** — Incluso con Node.js tramite Corepack. Abilitalo eseguendo `corepack enable` -* **Docker** — [Scarica qui](https://www.docker.com/products/docker-desktop/). Necessario per eseguire un'istanza locale di Twenty. Non necessario se hai già un server Twenty in esecuzione. - -## Crea la tua prima app - -### Crea lo scheletro della tua app - -Apri un terminale ed esegui: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Ti verrà chiesto di inserire un nome e una descrizione per la tua app. Premi **Invio** per accettare i valori predefiniti. - -Questo crea una nuova cartella chiamata `my-twenty-app` con tutto il necessario. - -### Configura un'istanza locale di Twenty - -Lo strumento di scaffolding chiederà: - -> **Vuoi configurare un'istanza locale di Twenty?** - -* **Digita `yes`** (consigliato) — Questo scarica l'immagine Docker `twenty-app-dev` e avvia un server Twenty locale sulla porta `2020`. Assicurati che Docker sia in esecuzione prima di continuare. -* **Digita `no`** — Sceglilo se hai già un server Twenty in esecuzione in locale. - -
- Avviare l'istanza locale? -
- -### Accedi al tuo spazio di lavoro - -Successivamente si aprirà una finestra del browser con la pagina di accesso di Twenty. Accedi con l'account demo preconfigurato: - -* **Email:** `tim@apple.dev` -* **Password:** `tim@apple.dev` - -
- Schermata di accesso di Twenty -
- -### Autorizza l'app - -Dopo l'accesso, vedrai una schermata di autorizzazione. Questo consente alla tua app di interagire con il tuo spazio di lavoro. - -Fai clic su **Authorize** per continuare. - -
- Schermata di autorizzazione della CLI di Twenty -
- -Una volta autorizzato, il terminale confermerà che tutto è configurato. - -
- App creata con successo -
- -### Inizia a sviluppare - -Entra nella nuova cartella della tua app e avvia il server di sviluppo: - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Questo controlla i file sorgente, ricompila a ogni modifica e sincronizza automaticamente la tua app con il server Twenty locale. Dovresti vedere un pannello di stato in tempo reale nel terminale. - -Per un output più dettagliato (log di build, richieste di sincronizzazione, tracce di errore), usa il flag `--verbose`: - -```bash filename="Terminal" -yarn twenty dev --verbose -``` - - -La modalità di sviluppo è disponibile solo sulle istanze di Twenty in esecuzione in modalità sviluppo (`NODE_ENV=development`). Le istanze di produzione rifiutano le richieste di sincronizzazione in modalità sviluppo. Usa `yarn twenty deploy` per distribuire sui server di produzione — vedi [Pubblicazione delle app](/l/it/developers/extend/apps/publishing) per i dettagli. - - -
- Output del terminale in modalità sviluppo -
- -#### Sincronizzazione una tantum con `yarn twenty dev --once` - -Se non vuoi un watcher in esecuzione in background (ad esempio in una pipeline CI, un hook di Git o un flusso di lavoro scriptato), passa il flag `--once`. Esegue la stessa pipeline di `yarn twenty dev` — genera il manifest, effettua il bundling dei file, carica, sincronizza, rigenera il client API tipizzato — ma **termina non appena la sincronizzazione è completata**: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Comando | Comportamento | Quando usarlo | -| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| `yarn twenty dev` | Monitora i file sorgente e risincronizza a ogni modifica. Rimane in esecuzione finché non lo interrompi. | Sviluppo locale interattivo — vuoi il pannello di stato in tempo reale e un ciclo di feedback immediato. | -| `yarn twenty dev --once` | Esegue una singola build + sincronizzazione, quindi termina con codice `0` in caso di successo o `1` in caso di errore. | Script, CI, hook pre-commit, agenti IA e qualsiasi flusso di lavoro non interattivo. | - -Entrambe le modalità richiedono un server Twenty in esecuzione in modalità di sviluppo e un remote autenticato — si applicano gli stessi prerequisiti. - -### Visualizza la tua app in Twenty - -Apri [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer) nel browser. Vai su **Settings > Apps** e seleziona la scheda **Developer**. Dovresti vedere la tua app elencata in **Your Apps**: - -
- Elenco Your Apps che mostra My twenty app -
- -Fai clic su **My twenty app** per aprire la sua **registrazione dell'applicazione**. Una registrazione è un record a livello di server che descrive la tua app — il suo nome, identificatore univoco, credenziali OAuth e origine (locale, npm o tarball). Risiede sul server, non all'interno di uno spazio di lavoro specifico. Quando installi un'app in uno spazio di lavoro, Twenty crea un'**applicazione** con ambito dello spazio di lavoro che rimanda a questa registrazione. Una registrazione può essere installata in più spazi di lavoro sullo stesso server. - -
- Dettagli della registrazione dell'applicazione -
- -Fai clic su **View installed app** per vedere l'app installata. La scheda **About** mostra la versione corrente e le opzioni di gestione: - -
- App installata — scheda About -
- -Passa alla scheda **Content** per vedere tutto ciò che la tua app fornisce — oggetti, campi, funzioni logiche e agenti: - -
- App installata — scheda Content -
- -È tutto pronto! Modifica qualsiasi file in `src/` e le modifiche verranno rilevate automaticamente. - ---- - -## Cosa puoi creare - -Le app sono composte da **entità** — ciascuna definita come un file TypeScript con un singolo `export default`: - -| Entità | Cosa fa | -| ------------------------ | ----------------------------------------------------------------------------------------------------------------- | -| **Oggetti e campi** | Definisci modelli di dati personalizzati (come Post Card, Invoice) con campi tipizzati | -| **Funzioni logiche** | Funzioni TypeScript lato server attivate da route HTTP, pianificazioni cron o eventi del database | -| **Componenti front-end** | Componenti React che vengono renderizzati all'interno dell'UI di Twenty (pannello laterale, widget, menu comandi) | -| **Skill e agenti** | Funzionalità di IA — istruzioni riutilizzabili e assistenti autonomi | -| **Viste e navigazione** | Viste elenco preconfigurate e voci di menu della barra laterale per i tuoi oggetti | -| **Layout di pagina** | Pagine di dettaglio dei record personalizzate con schede e widget | - -Vai a [Creare app](/l/it/developers/extend/apps/building) per una guida dettagliata su ogni tipo di entità. - ---- - -## Struttura del progetto - -Lo scaffolder genera la seguente struttura dei file: - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .oxlintrc.json - tsconfig.json - tsconfig.spec.json # TypeScript config for tests - vitest.config.ts # Vitest test runner configuration - LLMS.md - README.md - .github/ - └── workflows/ - └── ci.yml # GitHub Actions CI workflow - public/ # Public assets (images, fonts, etc.) - src/ - ├── application-config.ts # Required — main application configuration - ├── default-role.ts # Default role for logic functions - ├── constants/ - │ └── universal-identifiers.ts # Auto-generated UUIDs and app metadata - └── __tests__/ - ├── setup-test.ts # Test setup (server health check, config) - └── app-install.integration-test.ts # Integration test -``` - -### Partire da un esempio - -Per iniziare da un esempio più completo con oggetti personalizzati, campi, funzioni logiche, componenti front-end e altro, usa il flag `--example`: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Gli esempi provengono dalla directory [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) su GitHub. Puoi anche aggiungere singole entità a un progetto esistente con `yarn twenty add` (vedi [Creare app](/l/it/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add)). - -### File principali - -| File / Cartella | Scopo | -| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `package.json` | Dichiara il nome, la versione e le dipendenze della tua app. Include uno script `twenty` così puoi eseguire `yarn twenty help` per vedere tutti i comandi. | -| `src/application-config.ts` | **Obbligatorio.** Il file di configurazione principale della tua app. | -| `src/default-role.ts` | Ruolo predefinito che controlla a cosa possono accedere le tue funzioni logiche. | -| `src/constants/universal-identifiers.ts` | UUID generati automaticamente e metadati dell'app (nome visualizzato, descrizione). | -| `src/__tests__/` | Test di integrazione (setup + test di esempio). | -| `public/` | Asset statici (immagini, font) serviti insieme alla tua app. | - -## Server di sviluppo locale - -Lo scaffolder ha già avviato per te un server Twenty locale. Per gestirlo in seguito, usa `yarn twenty server`: - -| Comando | Descrizione | -| -------------------------------------- | --------------------------------------------------------- | -| `yarn twenty server start` | Avvia il server locale (scarica l'immagine se necessario) | -| `yarn twenty server start --port 3030` | Avvia su una porta personalizzata | -| `yarn twenty server start --test` | Avvia un'istanza di test separata sulla porta 2021 | -| `yarn twenty server stop` | Arresta il server (conserva i dati) | -| `yarn twenty server status` | Mostra stato del server, URL e credenziali | -| `yarn twenty server logs` | Trasmetti in streaming i log del server | -| `yarn twenty server logs --lines 100` | Mostra le ultime 100 righe di log | -| `yarn twenty server reset` | Elimina tutti i dati e riparti da zero | - -I dati vengono mantenuti tra i riavvii in due volumi Docker (`twenty-app-dev-data` per PostgreSQL, `twenty-app-dev-storage` per i file). Usa `reset` per cancellare tutto e ripartire da zero. - -### Esecuzione di un'istanza di test - -Passa `--test` a qualsiasi comando `server` per gestire una seconda istanza completamente isolata — utile per eseguire test di integrazione o per sperimentare senza toccare i tuoi dati di sviluppo principali. - -| Comando | Descrizione | -| ---------------------------------- | ------------------------------------------------------------------------ | -| `yarn twenty server start --test` | Avvia l'istanza di test (per impostazione predefinita usa la porta 2021) | -| `yarn twenty server stop --test` | Arresta l'istanza di test | -| `yarn twenty server status --test` | Mostra stato, URL e credenziali dell'istanza di test | -| `yarn twenty server logs --test` | Trasmetti in streaming i log dell'istanza di test | -| `yarn twenty server reset --test` | Cancella i dati di test e riparti da zero | - -L'istanza di test viene eseguita nel proprio container Docker (`twenty-app-dev-test`) con volumi dedicati (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) e configurazione dedicata, così può essere eseguita in parallelo con la tua istanza principale senza conflitti. Combina `--test` con `--port` per sovrascrivere il valore predefinito 2021. - - - Il server richiede che **Docker** sia in esecuzione. Se vedi l'errore "Docker not running", assicurati che Docker Desktop (o il demone Docker) sia avviato. - - -## Configurazione manuale (senza lo scaffolder) - -Se preferisci configurare tutto manualmente invece di usare `create-twenty-app`, puoi farlo in due passaggi. - -**1. Aggiungi `twenty-sdk` e `twenty-client-sdk` come dipendenze:** - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -**2. Aggiungi uno script `twenty` al tuo `package.json`:** - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -Ora puoi eseguire `yarn twenty dev`, `yarn twenty help` e tutti gli altri comandi. - - -Non installare `twenty-sdk` globalmente. Usalo sempre come dipendenza locale del progetto, in modo che ogni progetto possa fissare la propria versione. - - -## Risoluzione dei problemi - -Se riscontri problemi: - -* Assicurati che **Docker sia in esecuzione** prima di avviare lo strumento di scaffolding con un'istanza locale. -* Assicurati di usare **Node.js 24+** (`node -v` per verificare). -* Assicurati che **Corepack sia abilitato** (`corepack enable`) in modo che Yarn 4 sia disponibile. -* Prova a eliminare `node_modules` ed eseguire di nuovo `yarn install` se le dipendenze sembrano danneggiate. - -Ancora bloccato? Chiedi aiuto su [Discord di Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/it/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/it/developers/extend/apps/layout.mdx deleted file mode 100644 index 70aa0e54b0..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Layout -description: Definisci viste, voci del menu di navigazione e layout di pagina per determinare come la tua app appare in Twenty. -icon: table-columns ---- - -Le entità di layout controllano come la tua app si presenta all'interno dell'interfaccia utente di Twenty — ciò che è presente nella barra laterale, quali viste salvate sono incluse con l'app e come è organizzata la pagina dei dettagli di un record. - -## Concetti di layout - -| Concetto | Cosa controlla | Entità | -| -------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------- | -| **Vista** | Una configurazione di elenco salvata per un oggetto — campi visibili, ordine, filtri, gruppi | `defineView` | -| **Voce del menu di navigazione** | Una voce nella barra laterale sinistra che collega a una vista o a un URL esterno | `defineNavigationMenuItem` | -| **Layout di pagina** | Le schede e i widget che compongono la pagina dei dettagli di un record | `definePageLayout` | -| **Scheda layout di pagina** | Una scheda indipendente associata a un layout di pagina esistente (standard o della tua app) | `definePageLayoutTab` | - -Le viste, le voci del menu di navigazione e i layout di pagina fanno riferimento tra loro tramite `universalIdentifier`: - -* Una **voce del menu di navigazione** di tipo `VIEW` punta a un identificatore `defineView`, quindi il link nella barra laterale apre quella vista salvata. -* Un **layout di pagina** di tipo `RECORD_PAGE` si applica a un oggetto e può incorporare [front components](/l/it/developers/extend/apps/front-components) all'interno delle sue schede come widget. - - - - -Le viste sono configurazioni salvate di come vengono visualizzati i record di un oggetto — inclusi quali campi sono visibili, il loro ordine e gli eventuali filtri o raggruppamenti applicati. Usa `defineView()` per fornire viste preconfigurate con la tua app: - -```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, - }, - ], -}); -``` - -Punti chiave: -* `objectUniversalIdentifier` specifica a quale oggetto si applica questa vista. -* `key` determina il tipo di vista (ad es., `ViewKey.INDEX` per la vista elenco principale). -* `fields` controlla quali colonne compaiono e il loro ordine. Ogni campo fa riferimento a un `fieldMetadataUniversalIdentifier`. -* Puoi anche definire `filters`, `filterGroups`, `groups` e `fieldGroups` per configurazioni più avanzate. -* `position` controlla l'ordinamento quando esistono più viste per lo stesso oggetto. - - - - -Le voci del menu di navigazione aggiungono elementi personalizzati alla barra laterale dello spazio di lavoro. Usa `defineNavigationMenuItem()` per collegarti a viste, URL esterni o oggetti: - -```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, -}); -``` - -Punti chiave: -* `type` determina a cosa rimanda la voce di menu: `NavigationMenuItemType.VIEW` per una vista salvata o `NavigationMenuItemType.LINK` per un URL esterno. -* Per i link a viste, imposta `viewUniversalIdentifier`. Per i link esterni, imposta `link`. -* `position` controlla l'ordinamento nella barra laterale. -* `icon` e `color` (opzionali) personalizzano l'aspetto. - - - - -I layout di pagina ti consentono di personalizzare l'aspetto di una pagina dei dettagli di un record — quali schede compaiono, quali widget sono all'interno di ciascuna scheda e come sono disposti. Usa `definePageLayout()` per fornire layout personalizzati con la tua app: - -```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, - }, - }, - ], - }, - ], -}); -``` - -Punti chiave: -* `type` è in genere `'RECORD_PAGE'` per personalizzare la vista dei dettagli di un oggetto specifico. -* `objectUniversalIdentifier` specifica a quale oggetto si applica questo layout. -* Ogni `tab` definisce una sezione della pagina con un `title`, `position` e `layoutMode` (`CANVAS` per il layout libero). -* Ogni `widget` all'interno di una scheda può renderizzare un componente front-end, un elenco di relazioni o altri tipi di widget integrati. -* `position` sulle schede controlla il loro ordine. Usa valori più alti (ad es., 50) per posizionare le schede personalizzate dopo quelle integrate. - - - - -`definePageLayoutTab` consente alla tua app di aggiungere una singola scheda — con widget opzionali — a un layout di pagina **esistente**. Il caso d'uso più comune è aggiungere una scheda personalizzata (ad esempio, una scheda di analisi o di riepilogo IA) a una delle pagine record integrate di Twenty, oppure a un layout di pagina che la tua app fornisce già. - -Il layout di pagina di destinazione deve essere o un layout di pagina Twenty **standard** oppure uno definito dalla **tua app**; i riferimenti tra app a layout di pagina di proprietà di un'altra app installata non sono attualmente supportati. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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, - }, - }, - ], -}); -``` - -Punti chiave: -* `pageLayoutUniversalIdentifier` è **obbligatorio** quando si utilizza `definePageLayoutTab` e deve puntare a un layout di pagina già esistente al momento dell'installazione (standard o della tua app). Quando il layout di pagina padre manca, l'installazione non va a buon fine e restituisce un chiaro errore di validazione. -* `widgets` sono limitati solo a questa scheda — fanno riferimento a componenti front-end, viste, ecc. esattamente come i widget definiti inline in `definePageLayout`. -* `position` controlla l'ordinamento rispetto alle schede esistenti nel layout di destinazione. Scegli un valore che collochi la tua scheda dove desideri rispetto alle schede integrate. -* Usa questo invece di `definePageLayout` quando vuoi solo **aggiungere** a un layout esistente. Usa `definePageLayout` quando possiedi l'intero layout (in genere una `RECORD_PAGE` per un oggetto che distribuisci nella tua app, oppure una `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/it/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/it/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index 4d0545a6b8..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,560 +0,0 @@ ---- -title: Funzioni logiche -description: Definisci funzioni TypeScript lato server con trigger HTTP, cron e trigger di eventi del database. -icon: bolt ---- - -Le funzioni logiche sono funzioni TypeScript lato server che vengono eseguite sulla piattaforma Twenty. Possono essere attivate da richieste HTTP, pianificazioni cron o eventi del database — e possono anche essere esposte come strumenti per agenti di IA. - - - - -Ogni file di funzione usa `defineLogicFunction()` per esportare una configurazione con un handler e trigger opzionali. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Tipi di trigger disponibili: -* **httpRoute**: Espone la tua funzione su un percorso e metodo HTTP **sotto l'endpoint `/s/`**: -> ad es. `path: '/post-card/create'` è invocabile su `https://your-twenty-server.com/s/post-card/create` -* **cron**: Esegue la tua funzione secondo una pianificazione utilizzando un'espressione CRON. -* **databaseEvent**: Viene eseguito sugli eventi del ciclo di vita degli oggetti dello spazio di lavoro. Quando l'operazione dell'evento è `updated`, è possibile specificare campi specifici da monitorare nell'array `updatedFields`. Se lasciato non definito o vuoto, qualsiasi aggiornamento attiverà la funzione. -> ad es. `person.updated`, `*.created`, `company.*` - - -Puoi anche eseguire manualmente una funzione utilizzando la CLI: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Puoi osservare i log con: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Payload del trigger di route - -Quando un trigger di tipo route invoca la tua funzione logica, questa riceve un oggetto `RoutePayload` che segue il [formato AWS HTTP API v2](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). -Importa il tipo `RoutePayload` da `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -Il tipo `RoutePayload` ha la seguente struttura: - - | Proprietà | Tipo | Descrizione | Esempio | - | ---------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | Intestazioni HTTP (solo quelle elencate in `forwardedRequestHeaders`) | vedi la sezione sotto | - | `queryStringParameters` | `Record\` | Parametri della query string (valori multipli uniti da virgole) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Parametri di percorso estratti dal pattern della route | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Corpo della richiesta analizzato (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Original UTF-8 request body, before JSON parsing. Useful for verifying HMAC-style webhook signatures (e.g. GitHub's `X-Hub-Signature-256`, Stripe). `undefined` when the runtime did not preserve it. | | - | `isBase64Encoded` | `boolean` | Indica se il corpo è codificato in base64 | | - | `requestContext.http.method` | `string` | Metodo HTTP (GET, POST, PUT, PATCH, DELETE) | | - | `requestContext.http.path` | `string` | Percorso della richiesta non elaborato | | - - -#### forwardedRequestHeaders - -Per impostazione predefinita, le intestazioni HTTP delle richieste in ingresso **non** vengono passate alla tua funzione logica per motivi di sicurezza. -Per accedere a intestazioni specifiche, elencale nell'array `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'], - }, -}); -``` - -Nel tuo handler, accedi alle intestazioni inoltrate in questo modo: - -```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 }; -}; -``` - - -I nomi delle intestazioni vengono normalizzati in minuscolo. Accedile usando chiavi in minuscolo (ad es., `event.headers['content-type']`). - - -#### Esporre una funzione come strumento - -Le funzioni logiche possono essere esposte come **strumenti** per gli agenti di IA e i flussi di lavoro. Quando una funzione è contrassegnata come strumento, diventa individuabile dalle funzionalità di IA di Twenty e può essere utilizzata nelle automazioni dei flussi di lavoro. - -Per contrassegnare una funzione logica come strumento, imposta `isTool: true`: - -```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, - isTool: true, -}); -``` - -Punti chiave: - -* Puoi combinare `isTool` con i trigger — una funzione può essere sia uno strumento (invocabile dagli agenti IA) sia attivata da eventi allo stesso tempo. -* **`toolInputSchema`** (opzionale): un oggetto JSON Schema che descrive i parametri accettati dalla funzione. Lo schema viene calcolato automaticamente dall'analisi statica del codice sorgente, ma puoi impostarlo esplicitamente: - -```ts -export default defineLogicFunction({ - ..., - toolInputSchema: { - 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'], - }, -}); -``` - - -**Scrivi una buona `description`.** Gli agenti IA fanno affidamento sul campo `description` della funzione per decidere quando usare lo strumento. Sii specifico su cosa fa lo strumento e quando dovrebbe essere invocato. - - - - - -Una funzione post-installazione è una funzione logica che viene eseguita automaticamente dopo che la tua app è stata installata in uno spazio di lavoro. Il server la esegue **dopo** che i metadati dell'app sono stati sincronizzati e il client SDK è stato generato, così lo spazio di lavoro è completamente pronto per l'uso e il nuovo schema è attivo. I casi d'uso tipici includono il popolamento di dati predefiniti, la creazione di record iniziali, la configurazione delle impostazioni dello spazio di lavoro o il provisioning di risorse su servizi di terze parti. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Puoi anche eseguire manualmente la funzione di post-installazione in qualsiasi momento utilizzando la CLI: - -```bash filename="Terminal" -yarn twenty exec --postInstall -``` - -Punti chiave: -* Le funzioni di post-installazione utilizzano `definePostInstallLogicFunction()` — una variante specializzata che omette le impostazioni dei trigger (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`). -* L'handler riceve un `InstallPayload` con `{ previousVersion?: string; newVersion: string }` — `newVersion` è la versione in fase di installazione e `previousVersion` è la versione installata in precedenza (oppure `undefined` in caso di nuova installazione). Usa questi valori per distinguere le nuove installazioni dagli aggiornamenti e per eseguire logiche di migrazione specifiche per versione. -* **Quando viene eseguito l'hook**: solo sulle nuove installazioni, per impostazione predefinita. Passa `shouldRunOnVersionUpgrade: true` se vuoi che venga eseguito anche quando l'app viene aggiornata da una versione precedente. Se omesso, il flag è `false` per impostazione predefinita e gli aggiornamenti saltano l'hook. -* **Modello di esecuzione — asincrono per impostazione predefinita, sincrono su richiesta**: il flag `shouldRunSynchronously` controlla *come* viene eseguito il post-install. - * `shouldRunSynchronously: false` *(default)* — l'hook viene **messo in coda nella coda dei messaggi** con `retryLimit: 3` ed eseguito in modo asincrono in un worker. La risposta di installazione viene restituita non appena il job è messo in coda, quindi un handler lento o in errore non blocca il chiamante. Il worker riproverà fino a tre volte. **Usalo per job di lunga durata** — popolamento di dataset di grandi dimensioni, chiamate a API di terze parti lente, provisioning di risorse esterne, qualsiasi cosa che possa superare una finestra di risposta HTTP ragionevole. - * `shouldRunSynchronously: true` — l'hook viene eseguito **inline durante il flusso di installazione** (stesso executor del pre-install). La richiesta di installazione rimane bloccata finché l'handler non termina e, se genera un'eccezione, il chiamante dell'installazione riceve un `POST_INSTALL_ERROR`. Nessun tentativo automatico. **Usalo per attività rapide che devono completarsi prima della risposta** — ad esempio, emettere un errore di validazione all'utente, oppure un setup rapido di cui il client avrà bisogno immediatamente dopo il ritorno della chiamata di installazione. Tieni presente che la migrazione dei metadati è già stata applicata quando viene eseguito il post-install, quindi un errore in modalità sincrona **non** annulla le modifiche allo schema — si limita a far emergere l'errore. -* Assicurati che il tuo handler sia idempotente. In modalità asincrona la coda può riprovare fino a tre volte; in entrambe le modalità l'hook può essere eseguito di nuovo durante gli aggiornamenti quando `shouldRunOnVersionUpgrade: true`. -* Le variabili d'ambiente `APPLICATION_ID`, `APP_ACCESS_TOKEN` e `API_URL` sono disponibili all'interno dell'handler (come in qualsiasi altra funzione logica), quindi puoi chiamare le API di Twenty con un token di accesso applicativo con ambito sulla tua app. -* È consentita una sola funzione di post-installazione per applicazione. La build del manifesto genererà un errore se ne viene rilevata più di una. -* I campi `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` della funzione vengono associati automaticamente al manifest dell'applicazione nel campo `postInstallLogicFunction` durante la build — non è necessario referenziarli in `defineApplication()`. -* Il timeout predefinito è impostato a 300 secondi (5 minuti) per consentire attività di configurazione più lunghe, come il popolamento dei dati. -* **Non eseguito in modalità dev**: quando un'app è registrata in locale (tramite `yarn twenty dev`), il server salta completamente il flusso di installazione e sincronizza i file direttamente tramite il watcher della CLI — quindi il post-install non viene mai eseguito in modalità dev, indipendentemente da `shouldRunSynchronously`. Usa `yarn twenty exec --postInstall` per attivarlo manualmente su un workspace in esecuzione. - - - - -Una funzione di pre-install è una funzione logica che viene eseguita automaticamente durante l'installazione, **prima che venga applicata la migrazione dei metadati del workspace**. Condivide la stessa struttura di payload del post-install (`InstallPayload`), ma è posizionata prima nel flusso di installazione così da poter preparare lo stato da cui dipenderà la migrazione imminente — usi tipici includono il backup dei dati, la validazione della compatibilità con il nuovo schema o l'archiviazione di record che stanno per essere ristrutturati o eliminati. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Puoi anche eseguire manualmente la funzione di pre-installazione in qualsiasi momento utilizzando la CLI: - -```bash filename="Terminal" -yarn twenty exec --preInstall -``` - -Punti chiave: -* Le funzioni di pre-install usano `definePreInstallLogicFunction()` — stessa configurazione specialistica del post-install, solo agganciata a uno slot di ciclo di vita diverso. -* Sia gli handler di pre- sia quelli di post-install ricevono lo stesso tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importalo una volta e riutilizzalo per entrambi gli hook. -* **Quando viene eseguito l'hook**: posizionato appena prima della migrazione dei metadati del workspace (`synchronizeFromManifest`). Prima dell'esecuzione, il server esegue una "sincronizzazione ridotta" puramente additiva che registra nei metadati del workspace la funzione di pre-install della versione **nuova** — nient'altro viene toccato — e poi la esegue. Poiché questa sincronizzazione è solo additiva, gli oggetti, i campi e i dati della versione precedente restano intatti quando il tuo handler viene eseguito: puoi leggere ed eseguire in sicurezza il backup dello stato pre-migrazione. -* **Modello di esecuzione**: il pre-install è eseguito **in modo sincrono** e **blocca l'installazione**. Se l'handler genera un'eccezione, l'installazione viene interrotta prima che vengano applicate modifiche allo schema — il workspace rimane sulla versione precedente in uno stato coerente. Questo è intenzionale: il pre-install è la tua ultima possibilità per rifiutare un aggiornamento rischioso. -* Come per il post-install, è consentita una sola funzione di pre-installazione per applicazione. Viene collegata automaticamente al manifest dell'applicazione nel campo `preInstallLogicFunction` durante la build. -* **Non eseguito in modalità dev**: come per il post-install — il flusso di installazione viene completamente saltato per le app registrate localmente, quindi il pre-install non viene mai eseguito con `yarn twenty dev`. Usa `yarn twenty exec --preInstall` per attivarlo manualmente. - - - - -Entrambi gli hook fanno parte dello stesso flusso di installazione e ricevono lo stesso `InstallPayload`. La differenza è **quando** vengono eseguiti rispetto alla migrazione dei metadati del workspace, e questo modifica quali dati possono gestire in sicurezza. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -Il pre-install è sempre **sincrono** (blocca l'installazione e può interromperla). Il post-install è **asincrono per impostazione predefinita** — messo in coda su un worker con retry automatici — ma può optare per l'esecuzione sincrona con `shouldRunSynchronously: true`. Vedi l'accordion `definePostInstallLogicFunction` sopra per quando usare ciascuna modalità. - -**Usa `post-install` per tutto ciò che richiede l'esistenza del nuovo schema.** Questo è il caso più comune: - -* Popolamento di dati predefiniti (creazione di record iniziali, viste predefinite, contenuti demo) su oggetti e campi appena aggiunti. -* Registrazione di webhook con servizi di terze parti ora che l'app ha le proprie credenziali. -* Chiamare la tua API per completare il setup che dipende dai metadati sincronizzati. -* Logica idempotente di "ensure this exists" che dovrebbe riconciliare lo stato a ogni aggiornamento — da combinare con `shouldRunOnVersionUpgrade: true`. - -Esempio — eseguire il seeding di un record `PostCard` predefinito dopo l'installazione: - -```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 => { - 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, -}); -``` - -**Usa `pre-install` quando una migrazione altrimenti distruggerebbe o corromperebbe i dati esistenti.** Poiché il pre-install viene eseguito contro lo schema *precedente* e un suo fallimento annulla l'aggiornamento, è il posto giusto per qualsiasi operazione rischiosa: - -* **Eseguire il backup dei dati che stanno per essere eliminati o ristrutturati** — ad esempio, stai rimuovendo un campo nella v2 e devi copiarne i valori in un altro campo o esportarli su uno storage prima che venga eseguita la migrazione. -* **Archiviare i record che un nuovo vincolo renderebbe non validi** — ad esempio, un campo sta diventando `NOT NULL` e devi prima eliminare o correggere le righe con valori nulli. -* **Validare la compatibilità e rifiutare l'aggiornamento se i dati attuali non possono essere migrati correttamente** — genera un'eccezione dall'handler e l'installazione si interrompe senza applicare modifiche. Questo è più sicuro che scoprire l'incompatibilità a migrazione in corso. -* **Rinominare o rigenerare le chiavi dei dati** prima di una modifica dello schema che farebbe perdere l'associazione. - -Esempio — archiviare i record prima di una migrazione distruttiva: - -```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 => { - // 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, -}); -``` - -**Regola generale:** - -| Vuoi... | Usa | -| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| Popolare dati predefiniti, configurare il workspace, registrare risorse esterne | `post-install` | -| Eseguire seeding di lunga durata o chiamate a terze parti che non dovrebbero bloccare la risposta dell'installazione | `post-install` (predefinito — `shouldRunSynchronously: false`, con retry del worker) | -| Eseguire un setup rapido di cui il chiamante farà affidamento immediatamente dopo il ritorno della chiamata di installazione | `post-install` con `shouldRunSynchronously: true` | -| Leggere o eseguire il backup dei dati che la prossima migrazione perderebbe | `pre-install` | -| Rifiutare un aggiornamento che corromperebbe i dati esistenti | `pre-install` (genera un'eccezione dall'handler) | -| Eseguire la riconciliazione a ogni aggiornamento | `post-install` con `shouldRunOnVersionUpgrade: true` | -| Eseguire un setup una tantum solo alla prima installazione | `post-install` con `shouldRunOnVersionUpgrade: false` (predefinito) | - - -In caso di dubbio, usa **post-install**. Ricorri al pre-install solo quando la migrazione stessa è distruttiva e devi intercettare lo stato precedente prima che vada perso. - - - - - -## Client API tipizzati (twenty-client-sdk) - -Il pacchetto `twenty-client-sdk` fornisce due client GraphQL tipizzati per interagire con l'API di Twenty dalle tue funzioni logiche e dai componenti front-end. - -| Client | Importa | Endpoint | Generato? | -| ------------------- | ---------------------------- | ------------------------------------------------------------------------ | -------------------------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — dati dello spazio di lavoro (record, oggetti) | Sì, in fase di dev/build | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — configurazione dello spazio di lavoro, caricamenti di file | No, fornito pronto all'uso | - - - - -`CoreApiClient` è il client principale per interrogare e modificare i dati dello spazio di lavoro. Viene **generato dallo schema del tuo spazio di lavoro** durante `yarn twenty dev` o `yarn twenty build`, quindi è completamente tipizzato per corrispondere ai tuoi oggetti e campi. - -```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, - }, -}); -``` - -Il client utilizza una sintassi a selection-set: passa `true` per includere un campo, usa `__args` per gli argomenti e annida oggetti per le relazioni. Ottieni completamento automatico e controllo dei tipi completi basati sullo schema del tuo spazio di lavoro. - - -**CoreApiClient viene generato in fase di dev/build.** Se lo usi senza eseguire prima `yarn twenty dev` o `yarn twenty build`, genera un errore. La generazione avviene automaticamente — la CLI esegue l'introspezione dello schema GraphQL del tuo spazio di lavoro e genera un client tipizzato usando `@genql/cli`. - - -#### Utilizzo di CoreSchema per le annotazioni di tipo - -`CoreSchema` fornisce tipi TypeScript corrispondenti agli oggetti del tuo spazio di lavoro — utile per tipizzare lo stato dei componenti o i parametri delle funzioni: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`MetadataApiClient` è fornito pronto all'uso con l'SDK (nessuna generazione richiesta). Interroga l'endpoint `/metadata` per la configurazione dello spazio di lavoro, le applicazioni e i caricamenti di file. - -```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 }, - }, - }, -}); -``` - -#### Caricamento dei file - -`MetadataApiClient` include un metodo `uploadFile` per allegare file ai campi di tipo file: - -```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://...' } -``` - -| Parametro | Tipo | Descrizione | -| ---------------------------------- | -------- | ---------------------------------------------------------------------- | -| `fileBuffer` | `Buffer` | Il contenuto grezzo del file | -| `filename` | `string` | Il nome del file (utilizzato per l'archiviazione e la visualizzazione) | -| `contentType` | `string` | Tipo MIME (predefinito su `application/octet-stream` se omesso) | -| `fieldMetadataUniversalIdentifier` | `string` | L'`universalIdentifier` del campo di tipo file nel tuo oggetto | - -Punti chiave: -* Usa l'`universalIdentifier` del campo (non il suo ID specifico dello spazio di lavoro), quindi il tuo codice di upload funziona in qualsiasi spazio di lavoro in cui la tua app è installata. -* L'`url` restituito è un URL firmato che puoi usare per accedere al file caricato. - - - - - - Quando il tuo codice viene eseguito su Twenty (funzioni logiche o componenti front-end), la piattaforma inietta le credenziali come variabili d'ambiente: - - * `TWENTY_API_URL` — URL di base dell'API di Twenty - * `TWENTY_APP_ACCESS_TOKEN` — Chiave a breve durata con ambito al ruolo funzione predefinito della tua applicazione - - Non è **necessario** passarle ai client — vengono lette automaticamente da `process.env`. I permessi della chiave API sono determinati dal ruolo referenziato in `defaultRoleUniversalIdentifier` nel tuo `application-config.ts`. - diff --git a/packages/twenty-docs/l/it/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/it/developers/extend/apps/publishing.mdx deleted file mode 100644 index edbb932459..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,254 +0,0 @@ ---- -title: Pubblicazione -icon: carica -description: Distribuisci la tua app Twenty nel marketplace oppure distribuiscila internamente. ---- - -## Panoramica - -Una volta che la tua app è stata [compilata e testata localmente](/l/it/developers/extend/apps/building), hai due modalità per distribuirla: - -* **Distribuisci un tarball** — carica la tua app direttamente su un server Twenty specifico per uso interno o privato. -* **Pubblica su npm** — elenca la tua app nel marketplace di Twenty affinché qualsiasi spazio di lavoro possa scoprirla e installarla. - -Entrambi i percorsi partono dalla stessa fase di **build**. - -## Compilazione della tua app - -Esegui il comando di build per compilare la tua app e generare un `manifest.json` pronto per la distribuzione: - -```bash filename="Terminal" -yarn twenty build -``` - -Questo compila i sorgenti TypeScript, transpila le funzioni di logica e i componenti front-end e scrive tutto in `.twenty/output/`. Aggiungi `--tarball` per produrre anche un pacchetto `.tgz` per la distribuzione manuale o il comando di deploy. - -## Distribuzione su un server (tarball) - -Per le app che non vuoi rendere pubbliche — strumenti proprietari, integrazioni solo aziendali o build sperimentali — puoi distribuire un tarball direttamente su un server Twenty. - -### Prerequisiti - -Prima della distribuzione, ti serve un remote configurato che punti al server di destinazione. I remote memorizzano localmente l'URL del server e le credenziali di autenticazione in `~/.twenty/config.json`. - -Aggiungi un remote: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Distribuzione - -Compila e carica la tua app sul server in un solo passaggio: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Condivisione di un'app distribuita - - -La condivisione di app private (tarball) tra spazi di lavoro è una funzionalità **Enterprise**. La scheda **Distribution** mostrerà un invito all'aggiornamento al posto dei controlli di condivisione finché il tuo spazio di lavoro non dispone di una chiave Enterprise valida. Vedi [Impostazioni > Pannello di amministrazione > Enterprise](/settings/admin-panel#enterprise) per attivarla. - - -Le app in formato tarball non sono elencate nel marketplace pubblico, quindi altri spazi di lavoro sullo stesso server non le troveranno navigando. Una volta che il tuo spazio di lavoro è sul piano Enterprise, puoi condividere un'app distribuita in questo modo: - -1. Vai su **Impostazioni > Applicazioni > Registrazioni** e apri la tua app -2. Nella scheda **Distribuzione**, fai clic su **Copia link di condivisione** -3. Condividi questo link con utenti su altri spazi di lavoro — li porterà direttamente alla pagina di installazione dell'app - -Il link di condivisione utilizza l'URL di base del server (senza alcun sottodominio dello spazio di lavoro) così funziona per qualsiasi spazio di lavoro sul server. - -### Gestione delle versioni - -Quando si aggiorna un'app in formato tarball già distribuita, il server richiede che la `version` in `package.json` sia **strettamente superiore** (per l'ordinamento [semver](https://semver.org)) rispetto alla versione attualmente distribuita. Eseguire nuovamente il deploy della stessa versione, o pubblicarne una inferiore, viene rifiutato prima che il tarball venga archiviato — vedrai un errore `VERSION_ALREADY_EXISTS` nella CLI. - -Per rilasciare un aggiornamento: - -1. Incrementa il campo `version` nel tuo `package.json` (ad es. `1.2.3` → `1.2.4`, `1.3.0` o `2.0.0`). -2. Esegui `yarn twenty deploy` (oppure `yarn twenty deploy --remote production`) -3. Gli spazi di lavoro che hanno l'app installata vedranno l'aggiornamento disponibile nelle proprie impostazioni - - -I tag di pre-release funzionano come previsto: incrementare `1.0.0-rc.1` → `1.0.0-rc.2` è consentito e una release finale come `1.0.0` viene correttamente riconosciuta come superiore a `1.0.0-rc.5`. La versione in `package.json` deve essere essa stessa una stringa semver valida. - - -{/* TODO: add screenshot of the Upgrade button */} - -## CI/CD automatizzati (workflow preconfigurati) - -Le app generate con `create-twenty-app` includono due workflow di GitHub Actions pronti all'uso, nella cartella `.github/workflows/`. Sono pronti all'esecuzione non appena esegui il push del repository su GitHub — non è necessaria alcuna configurazione aggiuntiva per la CI e la CD richiede solo un singolo secret. - -### CI — `ci.yml` - -Esegue automaticamente i test di integrazione a ogni push su `main` e sulle pull request. - -**Cosa fa:** - -1. Esegue il checkout del codice sorgente della tua app. -2. Avvia un'istanza di test isolata di Twenty utilizzando l'azione composita `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (l'equivalente per la CI di `yarn twenty server start --test`). -3. Abilita Corepack, configura Node.js dal tuo `.nvmrc` e installa le dipendenze con `yarn install --immutable`. -4. Esegue `yarn test`, passando `TWENTY_API_URL` e `TWENTY_API_KEY` dall'istanza avviata affinché i tuoi test possano comunicare con un server reale. - -**Opzioni di configurazione:** - -* `TWENTY_VERSION` (variabile di ambiente, predefinito `latest`) — fissa la versione del server Twenty usata nella CI modificando questo valore in `ci.yml`. -* La concorrenza è raggruppata per `github.ref` e annulla le esecuzioni in corso in caso di nuovi push. - -Non sono necessari Secrets — l'istanza di test è effimera ed esiste solo per la durata del job. - -### CD — `cd.yml` - -Esegue il deploy della tua app su un server Twenty configurato a ogni push su `main` e, facoltativamente, da una pull request quando viene applicata l'etichetta `deploy`. - -**Cosa fa:** - -1. Esegue il checkout della testa della PR (per le PR etichettate) oppure del commit inviato. -2. Esegue `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — l'equivalente per la CI di `yarn twenty deploy`. -3. Esegue `twentyhq/twenty/.github/actions/install-twenty-app@main` in modo che la versione appena distribuita venga installata nello spazio di lavoro di destinazione. - -**Configurazione richiesta:** - -| Impostazione | Dove | Scopo | -| ----------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `env` in `cd.yml` (predefinito `http://localhost:3000`) | Il server Twenty su cui effettuare il deploy. Modificalo con l'URL reale del tuo server prima del primo utilizzo. | -| `TWENTY_DEPLOY_API_KEY` | Repository GitHub **Settings → Secrets and variables → Actions** | Chiave API con autorizzazione di deploy sul server di destinazione. | - - -Il valore predefinito di `TWENTY_DEPLOY_URL`, `http://localhost:3000`, è un segnaposto — non raggiungerà alcuna risorsa da un runner ospitato su GitHub. Aggiornalo all'URL pubblico del tuo server (oppure usa un runner self-hosted con accesso di rete) prima di abilitare il CD. - - -**Attivare un deploy di anteprima da una PR:** - -Aggiungi l'etichetta `deploy` a una pull request. La condizione `if:` in `cd.yml` eseguirà il job per quella PR utilizzando il commit di testa della PR, permettendoti di convalidare una modifica sul server di destinazione prima del merge. - -### Bloccare le azioni riutilizzabili - -Entrambi i workflow fanno riferimento ad azioni riutilizzabili a `@main`, quindi gli aggiornamenti delle azioni nel repository `twentyhq/twenty` vengono recepiti automaticamente. Se desideri build deterministiche, sostituisci `@main` con uno SHA di commit o un tag di release in ciascuna riga `uses:`. - -## Pubblicazione su npm - -La pubblicazione su npm rende la tua app scopribile nel marketplace di Twenty. Qualsiasi spazio di lavoro Twenty può sfogliare, installare e aggiornare le app del marketplace direttamente dall'interfaccia utente. - -### Requisiti - -* Un account [npm](https://www.npmjs.com) -* La parola chiave `twenty-app` nell'array `keywords` del tuo `package.json` (aggiungila manualmente — non è inclusa per impostazione predefinita nel template `create-twenty-app`) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Metadati del marketplace - -La configurazione `defineApplication()` supporta campi opzionali che controllano come la tua app appare nel marketplace. Usa `logoUrl` e `screenshots` per fare riferimento alle immagini nella cartella `public/`: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -Vedi l'[accordion defineApplication](/l/it/developers/extend/apps/building#defineentity-functions) nella pagina Building Apps per l'elenco completo dei campi del marketplace (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, ecc.). - -### Pubblica - -```bash filename="Terminal" -yarn twenty publish -``` - -Per pubblicare con un dist-tag specifico (ad es. `beta` o `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### Come funziona l'individuazione nel marketplace - -Il server Twenty sincronizza il proprio catalogo del marketplace dal registro npm **ogni ora**. - -Puoi attivare la sincronizzazione immediatamente invece di aspettare: - -```bash filename="Terminal" -yarn twenty catalog-sync -# To target a specific remote: -# yarn twenty catalog-sync --remote production -``` - -I metadati visualizzati nel marketplace provengono dalla configurazione `defineApplication()` — campi come `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`. - - -Se la tua app non definisce un `aboutDescription` in `defineApplication()`, il marketplace userà automaticamente il `README.md` del tuo pacchetto su npm come contenuto della pagina Informazioni. Questo significa che puoi mantenere un unico README sia per npm sia per il marketplace di Twenty. Se desideri una descrizione diversa nel marketplace, imposta esplicitamente `aboutDescription`. - - -### Pubblicazione con CI - -Usa questo workflow di GitHub Actions per pubblicare automaticamente a ogni release (usa [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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Per altri sistemi CI (GitLab CI, CircleCI, ecc.), si applicano gli stessi tre comandi: `yarn install`, `yarn twenty build`, quindi `npm publish` da `.twenty/output`. - - -**npm provenance** è opzionale ma consigliata. La pubblicazione con `--provenance` aggiunge un badge di attendibilità alla tua scheda npm, consentendo agli utenti di verificare che il pacchetto sia stato creato a partire da uno specifico commit in una pipeline CI pubblica. Consulta la [documentazione su npm provenance](https://docs.npmjs.com/generating-provenance-statements) per le istruzioni di configurazione. - - -## Installazione delle app - -Una volta che un'app è stata pubblicata (npm) o distribuita (tarball), gli spazi di lavoro possono installarla tramite l'interfaccia utente. - -Vai alla pagina **Impostazioni > Applicazioni** in Twenty, dove è possibile sfogliare e installare sia le app del marketplace sia quelle distribuite tramite tarball. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Puoi anche installare le app dalla riga di comando: - -```bash filename="Terminal" -yarn twenty install -``` - - -Il server applica il versioning semver durante l’installazione, rispecchiando le regole del deploy: - -* L’installazione della stessa versione già installata nel tuo spazio di lavoro viene rifiutata con un errore `APP_ALREADY_INSTALLED`. -* L’installazione di una versione inferiore rispetto a quella attualmente installata viene rifiutata con un errore `CANNOT_DOWNGRADE_APPLICATION`. - -Per installare una versione più recente, effettua prima il deploy o la pubblicazione, quindi riesegui `yarn twenty install`. - diff --git a/packages/twenty-docs/l/it/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/it/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index 6f321793dd..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Skill e agenti -description: Definisci skill e agenti di IA per la tua app. -icon: robot ---- - - - Skills and agents are currently in alpha. La funzionalità funziona ma è ancora in evoluzione. - - -Le app possono definire capacità di IA che risiedono all'interno dello spazio di lavoro — istruzioni di skill riutilizzabili e agenti con prompt di sistema personalizzati. - - - - -Le skill definiscono istruzioni e capacità riutilizzabili che gli agenti IA possono utilizzare all'interno del tuo spazio di lavoro. Usa `defineSkill()` per definire skill con convalida integrata: - -```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`, -}); -``` - -Punti chiave: -* `name` è una stringa identificativa univoca per la skill (kebab-case consigliato). -* `label` è il nome di visualizzazione leggibile mostrato nell'UI. -* `content` contiene le istruzioni della skill — questo è il testo che l'agente IA utilizza. -* `icon` (opzionale) imposta l'icona visualizzata nell'UI. -* `description` (opzionale) fornisce contesto aggiuntivo sullo scopo della skill. - - - - -Gli agenti sono assistenti IA che vivono all'interno del tuo spazio di lavoro. Usa `defineAgent()` per creare agenti con un prompt di sistema personalizzato: - -```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.', -}); -``` - -Punti chiave: -* `name` è la stringa identificativa univoca dell'agente (kebab-case consigliato). -* `label` è il nome visualizzato nell'UI. -* `prompt` è il prompt di sistema che definisce il comportamento dell'agente. -* `description` (opzionale) fornisce contesto su ciò che fa l'agente. -* `icon` (opzionale) imposta l'icona visualizzata nell'UI. -* `modelId` (opzionale) sostituisce il modello di IA predefinito utilizzato dall'agente. - - - diff --git a/packages/twenty-docs/l/it/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/it/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 6920254718..0000000000 --- a/packages/twenty-docs/l/it/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: App di Twenty -description: Crea e gestisci le personalizzazioni di Twenty come codice. ---- - - -Le app sono attualmente in fase alfa. La funzionalità funziona ma è ancora in evoluzione. - - -## Cosa sono le app? - -Le app ti consentono di estendere Twenty con oggetti, campi, funzioni logiche, componenti front-end, competenze IA e altro ancora — il tutto gestito come codice. Invece di configurare tutto tramite l'interfaccia utente, definisci in TypeScript il modello dati e la logica e li distribuisci in uno o più spazi di lavoro. - -**Cosa puoi creare:** - -* **Oggetti e campi personalizzati** — estendi il tuo modello dati con nuove entità o aggiungi campi a oggetti esistenti come Company o Person -* **Funzioni logiche** — funzioni lato server attivate da eventi del database, pianificazioni cron o route HTTP -* **Componenti front-end** — componenti React che vengono renderizzati nell'interfaccia utente di Twenty (pagine dei record, menu dei comandi, pannelli laterali) -* **Competenze e agenti IA** — estendi l'IA di Twenty con funzionalità personalizzate -* **Viste e navigazione** — viste salvate preconfigurate e link nella barra laterale - -## Avvio rapido - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Questo genera lo scheletro di una nuova app, avvia facoltativamente un server Twenty locale e inizia a monitorare i tuoi file per le modifiche. Consulta la [Guida introduttiva](/l/it/developers/extend/apps/getting-started) per l'intera procedura. - -## Guide dettagliate - -| Guide | Descrizione | -| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -| [Guida introduttiva](/l/it/developers/extend/apps/getting-started) | Crea lo scheletro di un'app, configura un server locale, struttura del progetto, CI | -| [Creare app](/l/it/developers/extend/apps/building) | Definizioni di entità (`defineObject`, `defineLogicFunction`, `defineFrontComponent`, ecc.), client API, pacchetti npm, asset pubblici, test | -| [Pubblicazione](/l/it/developers/extend/apps/publishing) | Distribuisci su un server, pubblica su npm, marketplace | - -## Concetti chiave - -### Rilevamento delle entità - -L'SDK rileva le entità scansionando i tuoi file TypeScript alla ricerca di chiamate a `export default define({...})`. La denominazione dei file e la struttura delle cartelle sono flessibili — il rilevamento è basato sull'AST, non sui percorsi. - -### Tipi di entità disponibili - -| Funzione | Scopo | -| ---------------------------------- | ------------------------------------------------------ | -| `defineApplication()` | Metadati dell'applicazione (obbligatorio, uno per app) | -| `defineObject()` | Oggetti personalizzati con campi | -| `defineField()` | Campi su oggetti esistenti | -| `defineLogicFunction()` | Logica lato server con trigger | -| `defineFrontComponent()` | Componenti React nell'interfaccia utente di Twenty | -| `defineRole()` | Ruoli di autorizzazione | -| `defineView()` | Configurazioni di viste salvate | -| `defineNavigationMenuItem()` | Link di navigazione della barra laterale | -| `defineSkill()` | Competenze dell'agente IA | -| `defineAgent()` | Agenti IA con prompt | -| `definePageLayout()` | Layout personalizzati delle pagine dei record | -| `definePreInstallLogicFunction()` | Viene eseguito prima dell'installazione dell'app | -| `definePostInstallLogicFunction()` | Viene eseguito dopo l'installazione dell'app | - -### Flusso di lavoro di sviluppo - -1. **`yarn twenty dev`** — osserva i file sorgente, ricompila alle modifiche, sincronizza con il server, genera client API tipizzati -2. **`yarn twenty build`** — produce una build distribuibile -3. **`yarn twenty deploy`** — distribuisce su un server Twenty remoto -4. **`yarn twenty add`** — crea lo scheletro di una nuova entità in modo interattivo - -### Riferimento CLI - -```bash filename="Terminal" -yarn twenty help # List all commands -yarn twenty server start # Start local dev server -yarn twenty remote add # Connect to a Twenty server -yarn twenty exec -n fn # Execute a logic function -yarn twenty logs -n fn # Stream function logs -``` - -Consulta la [Guida introduttiva](/l/it/developers/extend/apps/getting-started) per il riferimento completo della CLI. diff --git a/packages/twenty-docs/l/it/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/it/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 6fc8457589..0000000000 --- a/packages/twenty-docs/l/it/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Impostazioni delle versioni -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Funzionalità del Lab - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Vai su **Impostazioni → Versioni** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/ja/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/ja/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 80708bf1fc..0000000000 --- a/packages/twenty-docs/l/ja/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,648 +0,0 @@ ---- -title: Twenty アプリ -description: Twenty のカスタマイズをコードとして構築・管理します。 ---- - - - アプリは現在アルファテスト中です。 この機能は動作しますが、まだ進化の途上です。 - - -## Apps とは? - -Apps を使うと、Twenty のカスタマイズを**コードとして**構築・管理できます。 Instead of configuring everything through the UI, you define your data model and logic functions in code — making it faster to build, maintain, and roll out to multiple workspaces. - -**現在できること:** - -* カスタムオブジェクトとフィールドをコードとして定義(管理されたデータモデル) -* Build logic functions with custom triggers -* 同じアプリを複数のワークスペースにデプロイ - -**近日公開:** - -* カスタム UI レイアウトとコンポーネント - -## 前提条件 - -* Node.js 24+ と Yarn 4 -* Twenty のワークスペースと API キー(https://app.twenty.com/settings/api-webhooks で作成) - -## 始めに - -公式スキャフォルダーで新しいアプリを作成し、認証して開発を開始します: - -```bash filename="Terminal" -# 新しいアプリのひな型を作成 -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app - -# yarn@4 を使用していない場合 -corepack enable -yarn install - -# API キーで認証(プロンプトが表示されます) -yarn auth:login - -# 開発モードを開始:ローカルの変更がワークスペースに自動同期されます -yarn app:dev -``` - -そこで次のことができます: - -```bash filename="Terminal" -# アプリケーションに新しいエンティティを追加(ガイド付き) -yarn entity:add - -# アプリケーションの関数のログを監視 -yarn function:logs - -# 名前で関数を実行 -yarn function:execute -n my-function -p '{"name": "test"}' - -# 現在のワークスペースからアプリケーションをアンインストール -yarn app:uninstall - -# コマンドのヘルプを表示 -yarn help},{ -``` - -参考: [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) および [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk) の CLI リファレンスページをご覧ください。 - -## プロジェクト構成(スキャフォルド作成) - -`npx create-twenty-app@latest my-twenty-app` を実行すると、スキャフォルダーは次を行います: - -* 最小限のベースアプリケーションを `my-twenty-app/` にコピーします -* ローカルの `twenty-sdk` 依存関係と Yarn 4 の設定を追加します -* `twenty` CLI と連携する設定ファイルとスクリプトを作成します -* デフォルトのアプリケーション設定とデフォルトの関数ロールを生成します - -スキャフォルド直後のアプリは次のようになります: - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .yarn/ - install-state.gz - .oxlintrc.json - tsconfig.json - README.md - src/ - application.config.ts # Required - main application configuration - default-function.role.ts # Default role for serverless functions - hello-world.function.ts # Example serverless function - hello-world.front-component.tsx # Example front component - // your entities (*.object.ts, *.function.ts, *.front-component.tsx, *.role.ts) -``` - -### コンベンション優先 - -アプリケーションは **コンベンション優先(設定より規約)** のアプローチを採用し、エンティティはファイルのサフィックスで検出されます。 これにより、`src/app/` フォルダー内を柔軟に構成できます: - -| ファイルサフィックス | エンティティタイプ | -| ----------------------- | ----------------- | -| `*.object.ts` | カスタムオブジェクトの定義 | -| `*.function.ts` | サーバーレス関数の定義 | -| `*.front-component.tsx` | フロントエンドコンポーネントの定義 | -| `*.role.ts` | ロールの定義 | - -### サポートされるフォルダー構成 - -エンティティは次のいずれのパターンでも構成できます: - -**従来型(タイプ別):** - -```text -src/ -├── application.config.ts -├── objects/ -│ └── postCard.object.ts -├── functions/ -│ └── createPostCard.function.ts -├── components/ -│ └── card.front-component.tsx -└── roles/ - └── admin.role.ts -``` - -**機能単位:** - -```text -src/ -├── application.config.ts -└── post-card/ - ├── postCard.object.ts - ├── createPostCard.function.ts - ├── card.front-component.tsx - └── postCardAdmin.role.ts -``` - -**フラット:** - -```text -src/ -├── application.config.ts -├── postCard.object.ts -├── createPostCard.function.ts -├── card.front-component.tsx -└── admin.role.ts -``` - -概要: - -* **package.json**: Declares the app name, version, engines (Node 24+, Yarn 4), and adds `twenty-sdk` plus scripts like `app:dev`, `entity:add`, `function:logs`, `function:execute`, `app:uninstall`, and `auth:login` that delegate to the local `twenty` CLI. -* **.gitignore**: `node_modules`、`.yarn`、`generated/`(型付きクライアント)、`dist/`、`build/`、カバレッジ用フォルダー、ログファイル、`.env*` ファイルなどの一般的な生成物を無視します。 -* **yarn.lock**、**.yarnrc.yml**、**.yarn/**: プロジェクトで使用する Yarn 4 ツールチェーンをロックおよび構成します。 -* **.nvmrc**: プロジェクトで想定する Node.js バージョンを固定します。 -* **.oxlintrc.json** と **tsconfig.json**: アプリの TypeScript ソース向けの Lint と TypeScript 設定を提供します。 -* **README.md**: アプリのルートにある、基本的な手順を記した短い README。 -* **src/**: The main place where you define your application-as-code: - * `application.config.ts`: アプリのグローバル設定(メタデータとランタイムの接続)。 「アプリケーション設定」を参照してください。 - * `*.role.ts`: Role definitions used by your logic functions. 「デフォルトの関数ロール」を参照してください。 - * `*.object.ts`: カスタムオブジェクトの定義。 - * `*.function.ts`: Logic function definitions. - * `*.front-component.tsx`: Front component definitions. - -後続のコマンドにより、さらにファイルやフォルダーが追加されます: - -* `yarn app:dev` は `node_modules/twenty-sdk/generated` に型付き Twenty クライアントを自動生成します。 -* `yarn entity:add` will add entity definition files under `src/` for your custom objects, functions, front components, or roles. - -## 認証 - -初めて `yarn auth:login` を実行すると、次が求められます: - -* API URL(デフォルトは http://localhost:3000 または現在のワークスペースプロファイル) -* API キー - -認証情報はユーザーごとに `~/.twenty/config.json` に保存されます。 複数のプロファイルを管理し、相互に切り替えることができます。 - -### ワークスペースの管理 - -```bash filename="Terminal" -# 対話的にログイン(推奨) -yarn auth:login - -# 特定のワークスペースプロファイルにログイン -yarn auth:login --workspace my-custom-workspace - -# 設定済みのワークスペースをすべて一覧表示 -yarn auth:list - -# デフォルトのワークスペースを切り替え(対話的) -yarn auth:switch - -# 特定のワークスペースに切り替え -yarn auth:switch production - -# 現在の認証状態を確認 -yarn auth:status -``` - -一度 `auth:switch` でワークスペースを切り替えると、その後のすべてのコマンドはデフォルトでそのワークスペースを使用します。 一時的に `--workspace ` で上書きできます。 - -## SDK リソース(型と設定)を使う - -twenty-sdk は、アプリ内で使用する型付きのビルディングブロックとヘルパー関数を提供します。 以下は、最も頻繁に扱う主要な構成要素です。 - -### ヘルパー関数 - -この SDK は、アプリのエンティティを定義するための組み込み検証付きヘルパー関数を 4 つ提供します: - -| 関数 | 目的 | -| ------------------ | ------------------------------------ | -| `defineApplication()` | アプリケーションのメタデータを構成 | -| `defineObject()` | フィールド付きのカスタムオブジェクトを定義 | -| `defineFunction()` | Define logic functions with handlers | -| `defineRole()` | ロールの権限とオブジェクトアクセスを構成 | - -これらの関数は実行時に設定を検証し、IDE の補完と型安全性を向上させます。 - -### オブジェクトの定義 - -カスタムオブジェクトは、ワークスペース内のレコードのスキーマと挙動の両方を表します。 組み込み検証付きでオブジェクトを定義するには `defineObject()` を使用します: - -```typescript -// src/app/postCard.object.ts -import { defineObject, FieldType } from 'twenty-sdk'; - -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, - }, - ], -}); -``` - -主要ポイント: - -* 組み込み検証と優れた IDE サポートのために `defineObject()` を使用します。 -* `universalIdentifier` は、デプロイをまたいで一意かつ安定している必要があります。 -* 各フィールドには、`name`、`type`、`label`、および自身の安定した `universalIdentifier` が必要です。 -* `fields` 配列は任意です。カスタムフィールドなしでオブジェクトを定義できます。 -* You can scaffold new objects using `yarn entity:add`, which guides you through naming, fields, and relationships. - - - **ベースフィールドは自動作成されます。** カスタムオブジェクトを定義すると、Twenty は `name`、`createdAt`、`updatedAt`、`createdBy`、`position`、`deletedAt` などの標準フィールドを自動的に追加します。 これらを `fields` 配列で定義する必要はありません。カスタムフィールドのみを追加してください。 - - -### アプリケーション設定(application.config.ts) - -すべてのアプリには、次の内容を記述する単一の `application.config.ts` ファイルがあります: - -* **アプリの概要**: 識別子、表示名、説明。 -* **関数の実行方法**: 権限に使用するロール。 -* **(任意)変数**: 関数に環境変数として公開されるキーと値のペア。 - -アプリケーション設定を定義するには `defineApplication()` を使用します: - -```typescript -// src/app/application.config.ts -import { defineApplication } from 'twenty-sdk'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role'; - -export default defineApplication({ - universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -注記: - -* `universalIdentifier` フィールドは、あなたが管理する決定的な ID です。一度生成し、同期をまたいで安定したままにしてください。 -* `applicationVariables` は関数の環境変数になります(例:`DEFAULT_RECIPIENT_NAME` は `process.env.DEFAULT_RECIPIENT_NAME` として利用可能)。 -* `defaultRoleUniversalIdentifier` は、`*.role.ts` ファイルで定義するロールと一致している必要があります(下記参照)。 - -#### ロールと権限 - -アプリケーションは、ワークスペース内のオブジェクトやアクションに対する権限をカプセル化するロールを定義できます。 The field `defaultRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's logic functions. - -* `TWENTY_API_KEY` として注入される実行時の API キーは、このデフォルトの関数ロールから派生します。 -* 型付きクライアントの権限は、そのロールに付与された権限に制限されます。 -* 最小権限の原則に従い、関数に必要な権限のみに限定した専用ロールを作成し、そのユニバーサル識別子を参照してください。 - -##### デフォルトの関数ロール(\*.role.ts) - -新しいアプリをスキャフォルドすると、CLI はデフォルトのロールファイルも作成します。 組み込み検証付きでロールを定義するには `defineRole()` を使用します: - -```typescript -// src/app/default-function.role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - label: 'Default function role', - description: 'Default role for function Twenty client', - canReadAllObjectRecords: false, - canUpdateAllObjectRecords: false, - canSoftDeleteAllObjectRecords: false, - canDestroyAllObjectRecords: false, - canUpdateAllSettings: false, - canBeAssignedToAgents: false, - canBeAssignedToUsers: false, - canBeAssignedToApiKeys: false, - objectPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - canReadObjectRecords: true, - canUpdateObjectRecords: true, - canSoftDeleteObjectRecords: false, - canDestroyObjectRecords: false, - }, - ], - fieldPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff', - canReadFieldValue: false, - canUpdateFieldValue: false, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - -このロールの `universalIdentifier` は、`application.config.ts` で `defaultRoleUniversalIdentifier` として参照されます。 言い換えると: - -* **\*.role.ts** は、デフォルトの関数ロールで可能な操作を定義します。 -* **application.config.ts** でそのロールを指定することで、関数はその権限を継承します。 - -注記: - -* スキャフォルドされたロールから開始し、最小権限の原則に従って段階的に制限してください。 -* `objectPermissions` と `fieldPermissions` を、関数に必要なオブジェクト/フィールドに置き換えてください。 -* `permissionFlags` はプラットフォームレベルの機能へのアクセスを制御します。 最小限に保ち、必要なものだけを追加してください。 -* 動作例は Hello World アプリにあります: [packages/twenty-apps/hello-world/src/roles/function-role.ts](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts)。 - -### Logic function config and entrypoint - -各関数ファイルは、ハンドラーと任意のトリガーを含む設定を `defineFunction()` でエクスポートします。 自動検出のために `*.function.ts` のファイルサフィックスを使用します。 - -```typescript -// src/app/createPostCard.function.ts -import { defineFunction } from 'twenty-sdk'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk'; -import Twenty, { type Person } from '~/generated'; - -const handler = async (params: RoutePayload) => { - const client = new Twenty(); // generated typed client - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : 'Hello world'; - - const result = await client.mutation({ - createPostCard: { - __args: { data: { name } }, - id: true, - name: true, - }, - }); - return result; -}; - -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'create-new-post-card', - timeoutSeconds: 2, - handler, - triggers: [ - // Public HTTP route trigger '/s/post-card/create' - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/post-card/create', - httpMethod: 'GET', - isAuthRequired: false, - }, - // Cron trigger (CRON pattern) - // { - // universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2', - // type: 'cron', - // pattern: '0 0 1 1 *', - // }, - // Database event trigger - // { - // universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156', - // type: 'databaseEvent', - // eventName: 'person.updated', - // updatedFields: ['name'], - // }, - ], -}); -``` - -一般的なトリガーの種類: - -* **route**: `/s/` エンドポイント配下で、HTTP パスとメソッドで関数を公開します: - -> 例: `path: '/post-card/create',` -> `/s/post-card/create` で呼び出し - -* **cron**: CRON 式を使用してスケジュールで関数を実行します。 -* **databaseEvent**: ワークスペースのオブジェクトのライフサイクルイベントで実行されます。 イベント操作が `updated` の場合、監視する特定のフィールドを `updatedFields` 配列で指定できます。 未定義または空のままにすると、任意の更新でも関数がトリガーされます。 - -> 例: `person.updated` - -注記: - -* `triggers` 配列は任意です。 トリガーのない関数は、他の関数から呼び出されるユーティリティ関数として使用できます。 -* 1 つの関数で複数のトリガータイプを組み合わせることができます。 - -### ルートトリガーのペイロード - - - **破壊的変更(v1.16、2026年1月):** ルートトリガーのペイロード形式が変更されました。 v1.16 以前は、クエリパラメーター、パスパラメーター、および body がペイロードとして直接送信されていました。 v1.16 以降は、それらは構造化された `RoutePayload` オブジェクト内にネストされます。 - - **v1.16 以前:** - - ```typescript - const handler = async (params) => { - const { param1, param2 } = params; // Direct access - }; - ``` - - **v1.16 以降:** - - ```typescript - const handler = async (event: RoutePayload) => { - const { param1, param2 } = event.body; // Access via .body - const { queryParam } = event.queryStringParameters; - const { id } = event.pathParameters; - }; - ``` - - **既存の関数を移行するには:** ハンドラーで、params オブジェクトから直接ではなく、`event.body`、`event.queryStringParameters`、または `event.pathParameters` から分割代入するように更新してください。 - - -When a route trigger invokes your logic function, it receives a `RoutePayload` object that follows the AWS HTTP API v2 format. 型を `twenty-sdk` からインポートします: - -```typescript -import { defineFunction, type RoutePayload } from 'twenty-sdk'; - -const handler = async (event: RoutePayload) => { - // Access request data - const { headers, queryStringParameters, pathParameters, body } = event; - - // HTTP method and path are available in requestContext - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -`RoutePayload` 型は次の構造になっています: - -| プロパティ | タイプ | 説明 | -| ---------------------------- | ------------------------------------- | ---------------------------------------------------------- | -| `headers` | `Record` | HTTP ヘッダー (`forwardedRequestHeaders` に列挙されたもののみ) | -| `queryStringParameters` | `Record` | クエリ文字列パラメーター (複数の値はカンマで連結) | -| `pathParameters` | `Record` | ルートパターンから抽出されたパスパラメーター (例: `/users/:id` → `{ id: '123' }`) | -| `本文` | `object \| null` | 解析済みのリクエストボディ (JSON) | -| `isBase64Encoded` | `ブール型` | body が base64 エンコードされているかどうか | -| `requestContext.http.method` | `string` | HTTP メソッド (GET, POST, PUT, PATCH, DELETE) | -| `requestContext.http.path` | `string` | 生のリクエストパス | - -### HTTP ヘッダーの転送 - -By default, HTTP headers from incoming requests are **not** passed to your logic function for security reasons. 特定のヘッダーにアクセスするには、`forwardedRequestHeaders` 配列に明示的に列挙してください: - -```typescript -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'webhook-handler', - handler, - triggers: [ - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/webhook', - httpMethod: 'POST', - isAuthRequired: false, - forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], - }, - ], -}); -``` - -ハンドラー内で、これらのヘッダーにアクセスできます: - -```typescript -const handler = async (event: RoutePayload) => { - const signature = event.headers['x-webhook-signature']; - const contentType = event.headers['content-type']; - - // Validate webhook signature... - return { received: true }; -}; -``` - - - ヘッダー名は小文字に正規化されます。 小文字のキーを使用してアクセスしてください (例: `event.headers['content-type']`)。 - - -新しい関数は次の 2 通りで作成できます: - -* **Scaffolded**: Run `yarn entity:add` and choose the option to add a new function. これにより、ハンドラーと設定を備えたスターターファイルが生成されます。 -* **手動**: 新しい `*.function.ts` ファイルを作成し、同じパターンで `defineFunction()` を使用します。 - -### 生成された型付きクライアント - -`yarn app:dev` は `node_modules/twenty-sdk/generated` に型付き Twenty クライアントを自動生成します。 関数内で使用します: - -```typescript -import Twenty from '~/generated'; - -const client = new Twenty(); -const { me } = await client.query({ me: { id: true, displayName: true } }); -``` - -このクライアントは `app:dev` 実行中に自動的に再生成されます。 オブジェクトを変更した後、または新しいワークスペースにオンボーディングする際は、`app:dev` を再起動してください。 - -#### Runtime credentials in logic functions - -関数が Twenty 上で実行されると、コードが実行される前に、プラットフォームが認証情報を環境変数として注入します: - -* `TWENTY_API_URL`: アプリが対象とする Twenty API のベース URL。 -* `TWENTY_API_KEY`: アプリケーションのデフォルト関数ロールにスコープされた短命のキー。 - -ノート: - -* 生成されたクライアントに URL や API キーを渡す必要はありません。 実行時に process.env から `TWENTY_API_URL` と `TWENTY_API_KEY` を読み取ります。 -* API キーの権限は、`application.config.ts` で `defaultRoleUniversalIdentifier` によって参照されるロールによって決まります。 This is the default role used by logic functions of your application. -* アプリケーションは、最小権限の原則に従うロールを定義できます。 関数に必要な権限のみを付与し、`defaultRoleUniversalIdentifier` をそのロールのユニバーサル識別子に指定してください。 - -### Hello World の例 - -オブジェクト、関数、複数のトリガーを示す最小のエンドツーエンド例は[こちら](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world)をご覧ください。 - -## 手動セットアップ(スキャフォルダーなし) - -最適な導入体験のために `create-twenty-app` の使用を推奨しますが、手動でプロジェクトをセットアップすることもできます。 CLI をグローバルにインストールしないでください。 代わりに、`twenty-sdk` をローカル依存関係として追加し、package.json にスクリプトを設定します: - -```bash filename="Terminal" -yarn add -D twenty-sdk -``` - -次のようなスクリプトを追加します: - -```json filename="package.json" -{ - "scripts": { - "auth:login": "twenty auth:login", - "auth:logout": "twenty auth:logout", - "auth:status": "twenty auth:status", - "auth:switch": "twenty auth:switch", - "auth:list": "twenty auth:list", - "app:dev": "twenty app:dev", - "app:uninstall": "twenty app:uninstall", - "entity:add": "twenty entity:add", - "function:logs": "twenty function:logs", - "function:execute": "twenty function:execute", - "help": "twenty help" - } -} -``` - -Now you can run the same commands via Yarn, e.g. `yarn app:dev`, etc. - -## トラブルシューティング - -* 認証エラー: `yarn auth:login` を実行し、API キーに必要な権限があることを確認してください。 -* サーバーに接続できません: API URL と、Twenty サーバーに到達可能であることを確認してください。 -* Types or client missing/outdated: restart `yarn app:dev`. -* 開発モードで同期されない: `yarn app:dev` が実行中であり、環境によって変更が無視されていないことを確認してください。 - -Discord ヘルプチャンネル: https://discord.com/channels/1130383047699738754/1130386664812982322 diff --git a/packages/twenty-docs/l/ja/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/ja/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4180b67161..0000000000 --- a/packages/twenty-docs/l/ja/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Features - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/ko/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/ko/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 3f70201065..0000000000 --- a/packages/twenty-docs/l/ko/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,650 +0,0 @@ ---- -title: Twenty 앱 -description: Twenty 맞춤설정을 코드로 구축하고 관리하세요. ---- - - - 앱은 현재 알파 테스트 중입니다. 해당 기능은 작동하지만 아직 발전 중입니다. - - -## 앱이란 무엇인가요? - -앱을 사용하면 Twenty 맞춤설정을 **코드로** 구축하고 관리할 수 있습니다. 모든 것을 UI에서 구성하는 대신, 데이터 모델과 로직 함수를 코드로 정의합니다 — 이를 통해 더 빠르게 구축·유지 관리하고 여러 워크스페이스에 배포할 수 있습니다. - -**현재 가능한 작업:** - -* 사용자 정의 객체와 필드를 코드로 정의하기(관리형 데이터 모델) -* 사용자 정의 트리거로 로직 함수 구축 -* 동일한 앱을 여러 워크스페이스에 배포 - -**곧 제공 예정:** - -* 사용자 정의 UI 레이아웃 및 컴포넌트 - -## 사전 준비 - -* Node.js 24+ 및 Yarn 4 -* Twenty 워크스페이스와 API 키(https://app.twenty.com/settings/api-webhooks에서 생성) - -## 시작하기 - -공식 스캐폴더를 사용해 새 앱을 만든 다음, 인증하고 개발을 시작하세요: - -```bash filename="Terminal" -# 새 앱 초기 구조 생성 -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app - -# yarn@4를 사용하지 않는 경우 -corepack enable -yarn install - -# API 키로 인증합니다(입력하라는 메시지가 표시됩니다) -yarn auth:login - -# 개발 모드 시작: 로컬 변경 사항이 워크스페이스와 자동으로 동기화됩니다 -yarn app:dev -``` - -여기에서 다음 작업을 수행할 수 있습니다: - -```bash filename="Terminal" -# Add a new entity to your application (guided) -yarn entity:add - -# Watch your application's function logs -yarn function:logs - -# Execute a function by name -yarn function:execute -n my-function -p '{"name": "test"}' - -# Uninstall the application from the current workspace -yarn app:uninstall - -# Display commands' help -yarn help -``` - -참고: [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) 및 [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk)의 CLI 참고 페이지도 확인하세요. - -## 프로젝트 구조(스캐폴딩됨) - -`npx create-twenty-app@latest my-twenty-app`를 실행하면, 스캐폴더가 다음을 수행합니다: - -* `my-twenty-app/`에 최소한의 기본 애플리케이션을 복사합니다 -* 로컬 `twenty-sdk` 종속성과 Yarn 4 구성을 추가합니다 -* `twenty` CLI와 연결된 설정 파일과 스크립트를 생성합니다 -* 기본 애플리케이션 구성과 기본 함수 역할을 생성합니다 - -새로 스캐폴딩된 앱은 다음과 같습니다: - -```text filename="my-twenty-app/" -my-twenty-app/ - package.json - yarn.lock - .gitignore - .nvmrc - .yarnrc.yml - .yarn/ - install-state.gz - .oxlintrc.json - tsconfig.json - README.md - public/ # 공개 자산 폴더(이미지, 폰트 등) - src/ - application.config.ts # 필수 - 기본 애플리케이션 구성 - default-function.role.ts # 서버리스 함수의 기본 역할 - hello-world.function.ts # 서버리스 함수 예제 - hello-world.front-component.tsx # 프런트 컴포넌트 예제 - // 사용자 엔터티 (*.object.ts, *.function.ts, *.front-component.tsx, *.role.ts) -``` - -### 설정보다 관례 - -애플리케이션은 파일 접미사로 엔티티를 감지하는 **관례 우선** 접근 방식을 사용합니다. 이를 통해 `src/app/` 폴더 내에서 유연하게 구성할 수 있습니다: - -| 파일 접미사 | 엔티티 유형 | -| ----------------------- | ------------ | -| `*.object.ts` | 사용자 정의 객체 정의 | -| `*.function.ts` | 서버리스 함수 정의 | -| `*.front-component.tsx` | 프런트 컴포넌트 정의 | -| `*.role.ts` | 역할 정의 | - -### 지원되는 폴더 구성 방식 - -엔티티를 다음 패턴 중 어느 것으로든 구성할 수 있습니다: - -**전통적(유형별):** - -```text -src/ -├── application.config.ts -├── objects/ -│ └── postCard.object.ts -├── functions/ -│ └── createPostCard.function.ts -├── components/ -│ └── card.front-component.tsx -└── roles/ - └── admin.role.ts -``` - -**기능 기반:** - -```text -src/ -├── application.config.ts -└── post-card/ - ├── postCard.object.ts - ├── createPostCard.function.ts - ├── card.front-component.tsx - └── postCardAdmin.role.ts -``` - -**플랫:** - -```text -src/ -├── application.config.ts -├── postCard.object.ts -├── createPostCard.function.ts -├── card.front-component.tsx -└── admin.role.ts -``` - -개요: - -* **package.json**: 앱 이름, 버전, 엔진(Node 24+, Yarn 4)을 선언하고, `twenty-sdk`와 함께 `app:dev`, `entity:add`, `function:logs`, `function:execute`, `app:uninstall`, `auth:login` 같은 스크립트를 추가합니다. 이 스크립트들은 로컬 `twenty` CLI에 위임됩니다. -* **.gitignore**: `node_modules`, `.yarn`, `generated/`(타입드 클라이언트), `dist/`, `build/`, 커버리지 폴더, 로그 파일, `.env*` 파일 등의 일반 산출물을 무시합니다. -* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: 프로젝트에서 사용하는 Yarn 4 툴체인을 고정하고 구성합니다. -* **.nvmrc**: 프로젝트에서 예상하는 Node.js 버전을 고정합니다. -* **.oxlintrc.json** 및 **tsconfig.json**: 앱의 TypeScript 소스에 대한 린팅 및 TypeScript 구성을 제공합니다. -* **README.md**: 앱 루트에 기본 안내를 담은 간단한 README입니다. -* **public/**: 애플리케이션과 함께 제공되는 공개 자산(이미지, 폰트, 정적 파일)을 저장하는 폴더입니다. 여기에 배치된 파일은 동기화 시 업로드되며 런타임에 액세스할 수 있습니다. -* **src/**: 애플리케이션을 코드로 정의하는 주요 위치: - * `application.config.ts`: 앱의 전역 구성(메타데이터 및 런타임 연결)입니다. 아래의 "Application config"를 참조하세요. - * `*.role.ts`: 로직 함수에서 사용하는 역할 정의. 아래의 "Default function role"을 참조하세요. - * `*.object.ts`: 사용자 정의 객체 정의. - * `*.function.ts`: 로직 함수 정의. - * `*.front-component.tsx`: 프런트 컴포넌트 정의. - -이후 명령을 실행하면 더 많은 파일과 폴더가 추가됩니다: - -* `yarn app:dev`는 `node_modules/twenty-sdk/generated`에 타입드 Twenty 클라이언트를 자동으로 생성합니다. -* `yarn entity:add`는 사용자 정의 객체, 함수, 프런트 컴포넌트 또는 역할에 대한 엔티티 정의 파일을 `src/` 아래에 추가합니다. - -## 인증 - -처음 `yarn auth:login`을 실행하면 다음을 입력하라는 프롬프트가 표시됩니다: - -* API URL(기본값은 http://localhost:3000 또는 현재 워크스페이스 프로필) -* API 키 - -자격 증명은 사용자별로 `~/.twenty/config.json`에 저장됩니다. 여러 프로필을 유지하고 프로필 간에 전환할 수 있습니다. - -### 작업 공간 관리 - -```bash filename="Terminal" -# Login interactively (recommended) -yarn auth:login - -# Login to a specific workspace profile -yarn auth:login --workspace my-custom-workspace - -# List all configured workspaces -yarn auth:list - -# Switch the default workspace (interactive) -yarn auth:switch - -# Switch to a specific workspace -yarn auth:switch production - -# Check current authentication status -yarn auth:status -``` - -한 번 `auth:switch`로 작업 공간을 전환하면, 이후의 모든 명령은 기본적으로 해당 작업 공간을 사용합니다. 여전히 `--workspace `로 일시적으로 재정의할 수 있습니다. - -## SDK 리소스(타입 및 구성) 사용 - -twenty-sdk는 앱 내부에서 사용하는 타입드 빌딩 블록과 헬퍼 함수를 제공합니다. 가장 자주 사용하게 될 핵심 요소는 다음과 같습니다. - -### 헬퍼 함수 - -SDK는 앱 엔티티를 정의할 때 사용할 수 있는, 내장 검증이 포함된 네 가지 헬퍼 함수를 제공합니다: - -| 함수 | 목적 | -| --------------------- | ------------------- | -| `defineApplication()` | 애플리케이션 메타데이터 구성 | -| `defineObject()` | 필드가 있는 사용자 정의 객체 정의 | -| `defineFunction()` | 핸들러가 있는 로직 함수 정의 | -| `defineRole()` | 역할 권한과 객체 접근 구성 | - -이 함수들은 런타임에 구성을 검증하고, 더 나은 IDE 자동 완성과 타입 안정성을 제공합니다. - -### 객체 정의하기 - -사용자 정의 객체는 워크스페이스의 레코드에 대한 스키마와 동작을 모두 정의합니다. `defineObject()`를 사용해 내장 검증과 함께 객체를 정의하세요: - -```typescript -// src/app/postCard.object.ts -import { defineObject, FieldType } from 'twenty-sdk'; - -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, - }, - ], -}); -``` - -핵심 요점: - -* 내장 검증과 더 나은 IDE 지원을 위해 `defineObject()`를 사용하세요. -* `universalIdentifier`는 배포 전반에서 고유하고 안정적이어야 합니다. -* 각 필드는 `name`, `type`, `label` 및 고유하고 안정적인 `universalIdentifier`가 필요합니다. -* `fields` 배열은 선택 사항입니다. 사용자 정의 필드 없이도 객체를 정의할 수 있습니다. -* `yarn entity:add`를 사용하여 새 객체를 스캐폴딩할 수 있으며, 이름, 필드, 관계 설정 과정을 안내합니다. - - - **기본 필드는 자동으로 생성됩니다.** 사용자 정의 객체를 정의하면 Twenty가 `name`, `createdAt`, `updatedAt`, `createdBy`, `position`, `deletedAt` 등의 표준 필드를 자동으로 추가합니다. 이 필드들은 `fields` 배열에 정의할 필요가 없습니다. 사용자 정의 필드만 추가하세요. - - -### 애플리케이션 구성(application.config.ts) - -모든 앱에는 다음을 설명하는 단일 `application.config.ts` 파일이 있습니다: - -* **앱에 대한 정보**: 식별자, 표시 이름, 설명. -* **함수가 실행되는 방식**: 권한을 위해 사용하는 역할. -* **(선택 사항) 변수**: 함수에 환경 변수로 노출되는 키–값 쌍. - -Use `defineApplication()` to define your application configuration: - -```typescript -// src/app/application.config.ts -import { defineApplication } from 'twenty-sdk'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role'; - -export default defineApplication({ - universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7', - displayName: 'My Twenty App', - description: 'My first Twenty app', - icon: 'IconWorld', - applicationVariables: { - DEFAULT_RECIPIENT_NAME: { - universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', - description: 'Default recipient name for postcards', - value: 'Jane Doe', - isSecret: false, - }, - }, - roleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -노트: - -* `universalIdentifier` 필드는 고유하고 결정적인 ID입니다. 한 번 생성한 후 동기화 전반에 걸쳐 안정적으로 유지하세요. -* `applicationVariables`는 함수의 환경 변수가 됩니다(예: `DEFAULT_RECIPIENT_NAME`는 `process.env.DEFAULT_RECIPIENT_NAME`로 사용 가능). -* `roleUniversalIdentifier` must match the role you define in your `*.role.ts` file (see below). - -#### 역할 및 권한 - -애플리케이션은 워크스페이스의 객체와 작업에 대한 권한을 캡슐화하는 역할을 정의할 수 있습니다. The field `roleUniversalIdentifier` in `application.config.ts` designates the default role used by your app's logic functions. - -* `TWENTY_API_KEY`로 주입되는 런타임 API 키는 이 기본 함수 역할에서 파생됩니다. -* 타입드 클라이언트는 해당 역할에 부여된 권한으로 제한됩니다. -* 최소 권한 원칙을 따르세요. 함수에 필요한 권한만 가진 전용 역할을 만들고, 해당 역할의 universal identifier를 참조하세요. - -##### 기본 함수 역할(\*.role.ts) - -새 앱을 스캐폴딩하면, CLI가 기본 역할 파일도 생성합니다. `defineRole()`을 사용해 내장 검증과 함께 역할을 정의하세요: - -```typescript -// src/app/default-function.role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - label: 'Default function role', - description: 'Default role for function Twenty client', - canReadAllObjectRecords: false, - canUpdateAllObjectRecords: false, - canSoftDeleteAllObjectRecords: false, - canDestroyAllObjectRecords: false, - canUpdateAllSettings: false, - canBeAssignedToAgents: false, - canBeAssignedToUsers: false, - canBeAssignedToApiKeys: false, - objectPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - canReadObjectRecords: true, - canUpdateObjectRecords: true, - canSoftDeleteObjectRecords: false, - canDestroyObjectRecords: false, - }, - ], - fieldPermissions: [ - { - objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050', - fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff', - canReadFieldValue: false, - canUpdateFieldValue: false, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - -The `universalIdentifier` of this role is then referenced in `application.config.ts` as `roleUniversalIdentifier`. 다시 말해: - -* **\*.role.ts**는 기본 함수 역할이 수행할 수 있는 작업을 정의합니다. -* **application.config.ts**는 해당 역할을 가리키므로, 함수는 그 권한을 상속받습니다. - -노트: - -* 스캐폴딩된 역할에서 시작하여, 최소 권한 원칙에 따라 점진적으로 제한하세요. -* `objectPermissions`와 `fieldPermissions`를 함수에 필요한 객체/필드로 교체하세요. -* `permissionFlags`는 플랫폼 수준 기능에 대한 액세스를 제어합니다. 최소한으로 유지하고, 필요한 것만 추가하세요. -* Hello World 앱의 동작 예제를 참조하세요: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - -### 로직 함수 구성과 엔트리포인트 - -각 함수 파일은 `defineFunction()`을 사용해 핸들러와 선택적 트리거가 포함된 구성을 내보냅니다. 자동 감지를 위해 `*.function.ts` 파일 접미사를 사용하세요. - -```typescript -// src/app/createPostCard.function.ts -import { defineFunction } from 'twenty-sdk'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk'; -import Twenty, { type Person } from '~/generated'; - -const handler = async (params: RoutePayload) => { - const client = new Twenty(); // generated typed client - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : 'Hello world'; - - const result = await client.mutation({ - createPostCard: { - __args: { data: { name } }, - id: true, - name: true, - }, - }); - return result; -}; - -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'create-new-post-card', - timeoutSeconds: 2, - handler, - triggers: [ - // Public HTTP route trigger '/s/post-card/create' - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/post-card/create', - httpMethod: 'GET', - isAuthRequired: false, - }, - // Cron trigger (CRON pattern) - // { - // universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2', - // type: 'cron', - // pattern: '0 0 1 1 *', - // }, - // Database event trigger - // { - // universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156', - // type: 'databaseEvent', - // eventName: 'person.updated', - // updatedFields: ['name'], - // }, - ], -}); -``` - -일반적인 트리거 유형: - -* **route**: **`/s/` 엔드포인트** 아래에서 HTTP 경로와 메서드로 함수를 노출합니다: - -> 예: `path: '/post-card/create',` -> `/s/post-card/create`에서 호출 - -* **cron**: CRON 식을 사용하여 예약된 일정으로 함수를 실행합니다. -* **databaseEvent**: 워크스페이스 객체 라이프사이클 이벤트에서 실행됩니다. 이벤트 작업이 `updated`인 경우, 수신할 특정 필드를 `updatedFields` 배열에 지정할 수 있습니다. 정의하지 않거나 비워두면, 어떤 업데이트든 함수가 트리거됩니다. - -> 예: `person.updated` - -노트: - -* `triggers` 배열은 선택 사항입니다. 트리거가 없는 함수는 다른 함수에서 호출되는 유틸리티 함수로 사용할 수 있습니다. -* 하나의 함수에서 여러 트리거 유형을 혼합할 수 있습니다. - -### 라우트 트리거 페이로드 - - - **호환성 파괴적 변경(v1.16, 2026년 1월):** 라우트 트리거 페이로드 형식이 변경되었습니다. v1.16 이전에는 쿼리 매개변수, 경로 매개변수, 그리고 본문이 페이로드로 직접 전송되었습니다. v1.16부터는 이들이 구조화된 `RoutePayload` 객체 내부에 중첩됩니다. - - **v1.16 이전:** - - ```typescript - const handler = async (params) => { - const { param1, param2 } = params; // Direct access - }; - ``` - - **v1.16 이후:** - - ```typescript - const handler = async (event: RoutePayload) => { - const { param1, param2 } = event.body; // Access via .body - const { queryParam } = event.queryStringParameters; - const { id } = event.pathParameters; - }; - ``` - - **기존 함수 마이그레이션 방법:** 핸들러에서 params 객체에서 직접 구조 분해하는 대신 `event.body`, `event.queryStringParameters`, 또는 `event.pathParameters`에서 구조 분해하도록 업데이트하세요. - - -라우트 트리거가 로직 함수를 호출하면, AWS HTTP API v2 형식을 따르는 `RoutePayload` 객체를 받습니다. `twenty-sdk`에서 해당 타입을 임포트하세요: - -```typescript -import { defineFunction, type RoutePayload } from 'twenty-sdk'; - -const handler = async (event: RoutePayload) => { - // Access request data - const { headers, queryStringParameters, pathParameters, body } = event; - - // HTTP method and path are available in requestContext - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -`RoutePayload` 타입은 다음과 같은 구조입니다: - -| 속성 | 유형 | 설명 | -| ---------------------------- | ------------------------------------- | ------------------------------------------------------- | -| `headers` | `Record` | HTTP 헤더(`forwardedRequestHeaders`에 나열된 항목만) | -| `queryStringParameters` | `Record` | 쿼리 문자열 매개변수(여러 값은 쉼표로 연결됨) | -| `pathParameters` | `Record` | 라우트 패턴에서 추출된 경로 매개변수(예: `/users/:id` → `{ id: '123' }`) | -| `본문` | `object \| null` | 파싱된 요청 본문(JSON) | -| `isBase64Encoded` | `부울` | 본문이 base64로 인코딩되었는지 여부 | -| `requestContext.http.method` | `string` | HTTP 메서드(GET, POST, PUT, PATCH, DELETE) | -| `requestContext.http.path` | `string` | 원시 요청 경로 | - -### HTTP 헤더 전달 - -기본적으로 보안상의 이유로 들어오는 요청의 HTTP 헤더는 로직 함수로 **전달되지 않습니다**. 특정 헤더에 접근하려면 `forwardedRequestHeaders` 배열에 명시적으로 나열하세요: - -```typescript -export default defineFunction({ - universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', - name: 'webhook-handler', - handler, - triggers: [ - { - universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6', - type: 'route', - path: '/webhook', - httpMethod: 'POST', - isAuthRequired: false, - forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], - }, - ], -}); -``` - -핸들러에서 다음과 같이 해당 헤더에 접근할 수 있습니다: - -```typescript -const handler = async (event: RoutePayload) => { - const signature = event.headers['x-webhook-signature']; - const contentType = event.headers['content-type']; - - // Validate webhook signature... - return { received: true }; -}; -``` - - - 헤더 이름은 소문자로 정규화됩니다. 소문자 키를 사용해 접근하세요(예: `event.headers['content-type']`). - - -새 함수를 만드는 방법은 두 가지입니다: - -* **스캐폴딩**: `yarn entity:add`를 실행하고 새 함수를 추가하는 옵션을 선택하세요. 이렇게 하면 핸들러와 구성이 포함된 시작 파일이 생성됩니다. -* **수동**: 새 `*.function.ts` 파일을 만들고 동일한 패턴에 따라 `defineFunction()`을 사용하세요. - -### 생성된 타입드 클라이언트 - -`yarn app:dev`는 `node_modules/twenty-sdk/generated`에 타입드 Twenty 클라이언트를 자동으로 생성합니다. 함수에서 사용하세요: - -```typescript -import Twenty from '~/generated'; - -const client = new Twenty(); -const { me } = await client.query({ me: { id: true, displayName: true } }); -``` - -클라이언트는 `app:dev` 실행 중 자동으로 다시 생성됩니다. 객체를 변경한 후 또는 새 워크스페이스에 온보딩할 때 `app:dev`를 다시 시작하세요. - -#### 로직 함수의 런타임 자격 증명 - -함수가 Twenty에서 실행될 때, 플랫폼은 코드가 실행되기 전에 자격 증명을 환경 변수로 주입합니다: - -* `TWENTY_API_URL`: 앱이 대상으로 하는 Twenty API의 기본 URL. -* `TWENTY_API_KEY`: 애플리케이션의 기본 함수 역할 범위로 제한된 단기 키. - -노트: - -* 생성된 클라이언트에 URL이나 API 키를 전달할 필요가 없습니다. 런타임에 process.env에서 `TWENTY_API_URL`과 `TWENTY_API_KEY`를 읽습니다. -* The API key's permissions are determined by the role referenced in your `application.config.ts` via `roleUniversalIdentifier`. 이는 애플리케이션의 로직 함수에서 사용하는 기본 역할입니다. -* 애플리케이션은 최소 권한 원칙을 따르도록 역할을 정의할 수 있습니다. Grant only the permissions your functions need, then point `roleUniversalIdentifier` to that role's universal identifier. - -### Hello World 예제 - -객체, 함수, 여러 트리거를 보여주는 최소한의 엔드투엔드 예제를 [여기](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world)에서 살펴보세요: - -## 수동 설정(스캐폴더 없이) - -최적의 시작 경험을 위해 `create-twenty-app` 사용을 권장하지만, 프로젝트를 수동으로 설정할 수도 있습니다. CLI를 전역으로 설치하지 마세요. 대신 `twenty-sdk`를 로컬 종속성으로 추가하고 package.json에 스크립트를 연결하세요: - -```bash filename="Terminal" -yarn add -D twenty-sdk -``` - -그런 다음 다음과 같은 스크립트를 추가하세요: - -```json filename="package.json" -{ - "scripts": { - "auth:login": "twenty auth:login", - "auth:logout": "twenty auth:logout", - "auth:status": "twenty auth:status", - "auth:switch": "twenty auth:switch", - "auth:list": "twenty auth:list", - "app:dev": "twenty app:dev", - "app:uninstall": "twenty app:uninstall", - "entity:add": "twenty entity:add", - "function:logs": "twenty function:logs", - "function:execute": "twenty function:execute", - "help": "twenty help" - } -} -``` - -이제 Yarn을 통해 동일한 명령을 실행할 수 있습니다. 예: `yarn app:dev` 등. - -## 문제 해결 - -* 인증 오류: `yarn auth:login`를 실행하고 API 키에 필요한 권한이 있는지 확인하세요. -* 서버에 연결할 수 없음: API URL과 Twenty 서버에 접근 가능한지 확인하세요. -* 타입 또는 클라이언트가 없거나 오래된 경우: `yarn app:dev`를 다시 시작하세요. -* 개발 모드가 동기화되지 않음: `yarn app:dev`가 실행 중인지, 환경에서 변경 사항을 무시하지 않는지 확인하세요. - -Discord 도움말 채널: https://discord.com/channels/1130383047699738754/1130386664812982322 diff --git a/packages/twenty-docs/l/ko/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/ko/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4180b67161..0000000000 --- a/packages/twenty-docs/l/ko/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Features - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/building.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/building.mdx deleted file mode 100644 index 0fb3db35f3..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Arquitetura -description: Como as aplicações Twenty funcionam — sandboxing, ciclo de vida e os blocos de construção. -icon: sitemap ---- - -As aplicações Twenty são pacotes TypeScript que estendem seu espaço de trabalho com objetos personalizados, lógica, componentes de UI e recursos de IA. Elas são executadas na plataforma Twenty com sandboxing completo e controles de permissão. - -## Como as aplicações funcionam - -Uma aplicação é uma coleção de **entidades** declaradas usando funções `defineEntity()` do pacote `twenty-sdk`. O SDK detecta essas declarações via análise de AST no momento da compilação e produz um **manifesto** — uma descrição completa do que seu aplicativo adiciona a um espaço de trabalho. - -``` -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 -``` - - - **A organização de arquivos fica a seu critério.** A detecção de entidades é baseada em AST — o SDK encontra chamadas a `export default defineEntity(...)` independentemente de onde o arquivo esteja. A estrutura de pastas acima é uma convenção, não um requisito. - - -## Tipos de entidade - -| Entidade | Finalidade | Documentação | -| ----------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------- | -| **Aplicação** | Identidade da aplicação, permissões, variáveis | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) | -| **Papel** | Conjuntos de permissões para objetos e campos | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) | -| **Objeto** | Tabelas de dados personalizadas com campos | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) | -| **Campo** | Estender objetos existentes, definir relações | [Modelo de Dados](/l/pt/developers/extend/apps/data-model) | -| **Função lógica** | TypeScript no lado do servidor com gatilhos | [Funções lógicas](/l/pt/developers/extend/apps/logic-functions) | -| **Componente de front-end** | UI React em sandbox na página do Twenty | [Componentes de front-end](/l/pt/developers/extend/apps/front-components) | -| **Habilidade** | Instruções reutilizáveis para agentes de IA | [Habilidades e Agentes](/l/pt/developers/extend/apps/skills-and-agents) | -| **Agente** | Assistentes de IA com prompts personalizados | [Habilidades e Agentes](/l/pt/developers/extend/apps/skills-and-agents) | -| **Vista** | Vistas de lista de registros pré-configuradas | [Layout](/l/pt/developers/extend/apps/layout) | -| **Item do menu de navegação** | Entradas personalizadas na barra lateral | [Layout](/l/pt/developers/extend/apps/layout) | -| **Layout da Página** | Abas e widgets personalizados nas páginas de registro | [Layout](/l/pt/developers/extend/apps/layout) | - -## Sandboxing - -* **Funções lógicas** são executadas em processos Node.js isolados no servidor. Elas acessam dados apenas por meio do cliente de API tipado, restrito às permissões do papel do aplicativo. -* **Componentes de front-end** executam em Web Workers usando Remote DOM — isolados da página principal, mas renderizando elementos DOM nativos (não iframes). Eles se comunicam com o Twenty por meio de uma API de host com passagem de mensagens. -* **Permissões** são aplicadas no nível da API. O token de tempo de execução (`TWENTY_APP_ACCESS_TOKEN`) é derivado do papel definido em `defineApplication()`. - -## Ciclo de vida do aplicativo - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ Install flow │ -│ upload → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -├─────────────────────────────────────────────────────────┤ -│ Publish │ -│ npm publish → appears in Twenty marketplace │ -└─────────────────────────────────────────────────────────┘ -``` - -* **`yarn twenty dev`** — observa seus arquivos-fonte e sincroniza ao vivo as alterações com um servidor Twenty conectado. O cliente de API tipado é regenerado automaticamente quando o esquema muda. -* **`yarn twenty build`** — compila TypeScript, empacota funções de lógica e componentes de front-end com o esbuild e produz um manifesto. -* **Hooks de pré/pós-instalação** — funções de lógica opcionais que são executadas durante a instalação. Veja [Funções de Lógica](/l/pt/developers/extend/apps/logic-functions) para detalhes. - -## Próximos passos - - - - Defina objetos, campos, papéis e relações. - - - Funções no lado do servidor com gatilhos HTTP, cron e de eventos. - - - Componentes React em sandbox dentro da UI do Twenty. - - - Vistas, itens de navegação e layouts de página de registro. - - - Habilidades e agentes de IA com prompts personalizados. - - - Comandos de CLI, testes, assets, remotes e CI. - - - Implante em um servidor ou publique no marketplace. - - diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index e915bf1df6..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI e Testes -description: Comandos de CLI, configuração de testes, assets públicos, pacotes do npm, remotes e configuração de CI. -icon: terminal ---- - -## Recursos públicos (pasta `public/`) - -A pasta `public/` na raiz do seu app contém arquivos estáticos — imagens, ícones, fontes ou quaisquer outros recursos de que seu app precisa em tempo de execução. Esses arquivos são incluídos automaticamente nas compilações, sincronizados durante o modo de desenvolvimento e enviados para o servidor. - -Arquivos colocados em `public/` são: - -* **Publicamente acessíveis** — depois de sincronizados com o servidor, os recursos são servidos em uma URL pública. Não é necessária autenticação para acessá-los. -* **Disponíveis em componentes de front-end** — use URLs de recursos para exibir imagens, ícones ou qualquer mídia dentro de seus componentes React. -* **Disponíveis em funções lógicas** — referencie URLs de recursos em e-mails, respostas de API ou qualquer lógica no lado do servidor. -* **Usados para metadados do marketplace** — os campos `logoUrl` e `screenshots` em `defineApplication()` referenciam arquivos desta pasta (por exemplo, `public/logo.png`). Eles são exibidos no marketplace quando seu app é publicado. -* **Sincronizados automaticamente no modo de desenvolvimento** — quando você adiciona, atualiza ou exclui um arquivo em `public/`, ele é sincronizado automaticamente com o servidor. Não é necessário reiniciar. -* **Incluídos nas compilações** — `yarn twenty build` agrupa todos os recursos públicos na saída de distribuição. - -### Acessando recursos públicos com `getPublicAssetUrl` - -Use o helper `getPublicAssetUrl` de `twenty-sdk` para obter a URL completa de um arquivo no seu diretório `public/`. Funciona tanto em **funções lógicas** quanto em **componentes de front-end**. - -**Em uma função lógica:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, -}); -``` - -**Em um componente de front-end:** - -```tsx src/front-components/company-card.tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -O argumento `path` é relativo à pasta `public/` do seu app. Tanto `getPublicAssetUrl('logo.png')` quanto `getPublicAssetUrl('public/logo.png')` resolvem para a mesma URL — o prefixo `public/` é removido automaticamente, se presente. - -## Usando pacotes npm - -Você pode instalar e usar qualquer pacote npm no seu app. Tanto funções lógicas quanto componentes de front-end são empacotados com [esbuild](https://esbuild.github.io/), que incorpora todas as dependências na saída — nenhum `node_modules` é necessário em tempo de execução. - -### Instalando um pacote - -```bash filename="Terminal" -yarn add axios -``` - -Em seguida, importe-o no seu código: - -```ts src/logic-functions/fetch-data.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import axios from 'axios'; - -const handler = async (): Promise => { - 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, -}); -``` - -O mesmo vale para componentes de front-end: - -```tsx src/front-components/chart.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { format } from 'date-fns'; - -const DateWidget = () => { - return

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### Como o empacotamento funciona - -A etapa de build usa o esbuild para produzir um único arquivo independente por função lógica e por componente de front-end. Todos os pacotes importados são incorporados ao bundle. - -**Funções lógicas** são executadas em um ambiente Node.js. Módulos nativos do Node (`fs`, `path`, `crypto`, `http`, etc.) estão disponíveis e não precisam ser instalados. - -**Componentes de front-end** são executados em um Web Worker. Módulos nativos do Node **não** estão disponíveis — apenas APIs do navegador e pacotes npm que funcionam em um ambiente de navegador. - -Ambos os ambientes têm `twenty-client-sdk/core` e `twenty-client-sdk/metadata` disponíveis como módulos pré-fornecidos — eles não são empacotados, mas resolvidos em tempo de execução pelo servidor. - -## Testando seu aplicativo - -O SDK fornece APIs programáticas que permitem compilar, implantar, instalar e desinstalar seu aplicativo a partir de código de teste. Em conjunto com [Vitest](https://vitest.dev/) e os clientes de API tipados, você pode escrever testes de integração que verificam que seu aplicativo funciona de ponta a ponta em um servidor Twenty real. - -### Configuração - -O aplicativo gerado pelo scaffolder já inclui o Vitest. Se você configurá-lo manualmente, instale as dependências: - -```bash filename="Terminal" -yarn add -D vitest vite-tsconfig-paths -``` - -Crie um `vitest.config.ts` na raiz do seu aplicativo: - -```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', - }, - }, -}); -``` - -Crie um arquivo de configuração que verifique se o servidor está acessível antes da execução dos testes: - -```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), - ); -}); -``` - -### APIs programáticas do SDK - -O subcaminho `twenty-sdk/cli` exporta funções que você pode chamar diretamente a partir do código de teste: - -| Função | Descrição | -| -------------- | ------------------------------------------------------------ | -| `appBuild` | Compilar o aplicativo e, opcionalmente, empacotar um tarball | -| `appDeploy` | Enviar um tarball para o servidor | -| `appInstall` | Instalar o aplicativo no espaço de trabalho ativo | -| `appUninstall` | Desinstalar o aplicativo do espaço de trabalho ativo | - -Cada função retorna um objeto de resultado com `success: boolean` e `data` ou `error`. - -### Escrevendo um teste de integração - -Aqui está um exemplo completo que compila, implanta e instala o aplicativo e, em seguida, verifica se ele aparece no espaço de trabalho: - -```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(); - }); -}); -``` - -### Executando testes - -Certifique-se de que seu servidor Twenty local esteja em execução e, em seguida: - -```bash filename="Terminal" -yarn test -``` - -Ou no modo watch durante o desenvolvimento: - -```bash filename="Terminal" -yarn test:watch -``` - -### Verificação de tipos - -Você também pode executar a verificação de tipos no seu aplicativo sem executar os testes: - -```bash filename="Terminal" -yarn twenty typecheck -``` - -Isso executa `tsc --noEmit` e informa quaisquer erros de tipo. - -## Referência da CLI - -Além de `dev`, `build`, `add` e `typecheck`, a CLI fornece comandos para executar funções, visualizar logs e gerenciar instalações de aplicativos. - -### Executando funções (`yarn twenty exec`) - -Execute manualmente uma função de lógica sem acioná-la via HTTP, cron ou evento de banco de dados: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Visualizando logs de funções (`yarn twenty logs`) - -Transmita os logs de execução das funções de lógica do seu aplicativo: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -Isso é diferente de `yarn twenty server logs`, que mostra os logs do contêiner Docker. `yarn twenty logs` mostra os logs de execução de funções do seu aplicativo a partir do servidor Twenty. - - -### Desinstalando um aplicativo (`yarn twenty uninstall`) - -Remova seu aplicativo do espaço de trabalho ativo: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## Gerenciando remotos - -Um **remoto** é um servidor Twenty ao qual seu aplicativo se conecta. Durante a configuração, o gerador de scaffold cria um para você automaticamente. Você pode adicionar mais remotos ou alternar entre eles a qualquer momento. - -```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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Suas credenciais são armazenadas em `~/.twenty/config.json`. - -## CI com GitHub Actions - -O gerador de scaffold cria um workflow do GitHub Actions pronto para uso em `.github/workflows/ci.yml`. Ele executa seus testes de integração automaticamente a cada push para `main` e em pull requests. - -O workflow: - -1. Faz checkout do seu código -2. Inicializa um servidor Twenty temporário usando a ação `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Instala as dependências com `yarn install --immutable` -4. Executa `yarn test` com `TWENTY_API_URL` e `TWENTY_API_KEY` injetados a partir das saídas da ação - -```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 }} -``` - -Você não precisa configurar nenhum segredo — a ação `spawn-twenty-docker-image` inicia um servidor Twenty efêmero diretamente no runner e fornece os detalhes de conexão. O segredo `GITHUB_TOKEN` é fornecido automaticamente pelo GitHub. - -Para fixar uma versão específica do Twenty em vez de `latest`, altere a variável de ambiente `TWENTY_VERSION` no topo do workflow. diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/connections.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/connections.mdx deleted file mode 100644 index 02112ac98f..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/connections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Conexões -description: Permita que seu aplicativo aja em nome de um usuário em serviços de terceiros via OAuth. -icon: plug ---- - -Conexões são credenciais que um usuário mantém para um serviço externo (Linear, GitHub, Slack, ...). Seu app declara **como** essas credenciais são obtidas — um **provedor de conexão** — e as consome em tempo de execução para fazer chamadas autenticadas à API de terceiros. - -Atualmente, apenas o OAuth 2.0 tem suporte. Tipos de credenciais futuros (tokens de acesso pessoal, chaves de API, autenticação básica) serão conectados à mesma interface — apps que já usam `defineConnectionProvider({ type: 'oauth', ... })` não precisarão migrar. - - - - - -Um provedor de conexão descreve o handshake OAuth de que seu app precisa. O usuário clica em "Adicionar conexão" nas configurações do seu app, conclui a tela de consentimento do provedor e uma linha `ConnectedAccount` é criada no seu workspace. - -Uma configuração funcional precisa de **dois arquivos** — o provedor de conexão e uma declaração correspondente de `serverVariables` em `defineApplication` que contém as credenciais do cliente OAuth. - -```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.', - defaultRoleUniversalIdentifier: '...', - // 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, - }, - }, -}); -``` - -Pontos-chave: - -* `name` é a string de identificador exclusivo usada em `listConnections({ providerName })` (kebab-case, deve corresponder a `^[a-z][a-z0-9-]*$`). -* `displayName` aparece na aba de configurações do app e na lista de ferramentas de IA. -* `clientIdVariable` / `clientSecretVariable` são **nomes**, não valores — devem corresponder às chaves declaradas em `defineApplication.serverVariables`. Os `client_id` e `client_secret` reais são inseridos pelo administrador do servidor por meio da interface de registro do app e nunca são versionados no seu repositório. -* Use `serverVariables` (não `applicationVariables`) — as credenciais OAuth são do servidor como um todo e há um app OAuth por servidor do Twenty. -* Até que ambos os `serverVariables` sejam preenchidos, a aba de configurações do app mostra uma dica "precisa de administrador do servidor" e o botão "Adicionar conexão" fica desativado. -* `type: 'oauth'` é o único valor compatível atualmente. O discriminador é compatível com versões futuras: tipos futuros (`'pat'`, `'api-key'`, ...) adicionarão novos blocos de subconfiguração ao lado de `oauth`. - -O URL de callback do OAuth que seu provedor precisa adicionar à lista de permissões é: - -``` -https:///apps/oauth/callback -``` - - - - - -Dentro de um handler de função de lógica, `listConnections({ providerName })` retorna as linhas `ConnectedAccount` deste app para o provedor fornecido, com tokens de acesso atualizados. - -```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 }; -}; -``` - -Cada conexão tem: - -| Campo | Descrição | -| ----------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `id` | ID de linha exclusivo; passe para `getConnection(id)` para buscar novamente um único registro | -| `visibilidade` | `'user'` (privada para um membro do workspace) ou `'workspace'` (compartilhada com todos os membros) | -| `escopos` | Permissões OAuth concedidas pelo provedor de origem (distintas de `visibility` — não têm relação) | -| `userWorkspaceId` | O id de userWorkspace do proprietário — útil para selecionar "a conexão do usuário da requisição" em gatilhos de rota HTTP | -| `accessToken` | Token de acesso OAuth recente (atualizado automaticamente se estiver expirado) | -| `name` / `handle` | O nome de exibição da conexão (derivado automaticamente no callback do OAuth, renomeável pelo usuário) | -| `authFailedAt` | Definido quando a atualização mais recente falhou; o usuário deve reconectar | - -Pontos-chave: - -* Passe `{ providerName }` para filtrar por provedor; omita para obter todas as conexões que este app possui em todos os provedores. -* O servidor atualiza transparentemente o token de acesso antes de retornar. Seu handler sempre vê um token utilizável (ou `authFailedAt` definido). -* `getConnection(id)` é o equivalente de uma única linha. - - - - - -Quando um usuário clica em "Adicionar conexão", é solicitado que escolha uma visibilidade: - -* **Apenas para mim** — a credencial é privada para o usuário que a conectou. Qualquer função de lógica chamada em seu nome (gatilho de rota HTTP com `isAuthRequired: true`) a vê; gatilhos cron e eventos de banco de dados não. -* **Compartilhada no workspace** — qualquer membro do workspace pode usar a credencial. Gatilhos de cron / banco de dados também a veem, pois não há um usuário da requisição. - -Use a adequada para cada handler: - -```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'); -``` - -Várias conexões por (usuário, provedor) são permitidas, então o mesmo usuário pode manter "Linear pessoal" e "Linear de trabalho" lado a lado. - - - - - -Para cada provedor de conexão, o administrador do servidor precisa primeiro registrar um app OAuth no serviço de terceiros. - -1. Acesse as configurações de desenvolvedor do provedor (por exemplo, https://linear.app/settings/api/applications/new). -2. Defina a **URI de redirecionamento** como `\/apps/oauth/callback`. -3. Copie o **ID do cliente** e o **Segredo do cliente** gerados. -4. Abra o app instalado no Twenty como administrador do servidor → defina os valores nos `serverVariables` correspondentes. -5. Os membros do workspace podem então adicionar conexões na seção **Conexões** de cada app. - - - - diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/data-model.mdx deleted file mode 100644 index 5cf75ff45e..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,493 +0,0 @@ ---- -title: Modelo de dados -description: Defina objetos, campos, papéis e metadados da aplicação com o Twenty SDK. -icon: database ---- - -O pacote `twenty-sdk` fornece funções `defineEntity` para declarar o modelo de dados da sua aplicação. Você deve usar `export default defineEntity({...})` para que o SDK detecte suas entidades. Essas funções validam sua configuração em tempo de compilação e oferecem autocompletar na IDE e segurança de tipos. - - - **A organização de arquivos fica a seu critério.** - A detecção de entidades é baseada em AST — o SDK encontra chamadas a `export default defineEntity(...)` independentemente de onde o arquivo esteja. Agrupar arquivos por tipo (por exemplo, `logic-functions/`, `roles/`) é apenas uma convenção, não um requisito. - - - - - -Papéis encapsulam permissões sobre os objetos e ações do seu espaço de trabalho. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -Todo app deve ter exatamente uma chamada a `defineApplication` que descreve: - -* **Identidade**: identificadores, nome de exibição e descrição. -* **Permissões**: qual papel é usado por suas funções e componentes de front-end. -* **Variáveis (opcional)**: pares chave–valor expostos às suas funções como variáveis de ambiente. -* **(Opcional) Funções de pré-instalação/pós-instalação**: funções de lógica que são executadas antes ou depois da instalação. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -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, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Notas: -* Os campos `universalIdentifier` são IDs determinísticos que você controla. Gere-os uma vez e mantenha-os estáveis entre sincronizações. -* `applicationVariables` tornam-se variáveis de ambiente para suas funções e componentes de front-end (por exemplo, `DEFAULT_RECIPIENT_NAME` fica disponível como `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` deve fazer referência a um papel definido com `defineRole()` (veja acima). -* As funções de pré-instalação e pós-instalação são detectadas automaticamente durante a construção do manifesto — você não precisa referenciá-las em `defineApplication()`. - -#### Metadados do Marketplace - -Se você planeja [publicar seu app](/l/pt/developers/extend/apps/publishing), estes campos opcionais controlam como seu app aparece no marketplace: - -| Campo | Descrição | -| ------------------ | ----------------------------------------------------------------------------------------------------------------- | -| `author` | Nome do autor ou da empresa | -| `category` | Categoria do app para filtragem no marketplace | -| `logoUrl` | Caminho para o logo do seu app (por exemplo, `public/logo.png`) | -| `screenshots` | Array de caminhos de capturas de tela (por exemplo, `public/screenshot-1.png`) | -| `aboutDescription` | Descrição em markdown mais longa para a aba "Sobre". Se omitido, o marketplace usa o `README.md` do pacote no npm | -| `websiteUrl` | Link para seu site | -| `termsUrl` | Link para os Termos de Serviço | -| `emailSupport` | Endereço de e-mail de suporte | -| `issueReportUrl` | Link para o rastreador de problemas | - -#### Papéis e permissões - -O campo `defaultRoleUniversalIdentifier` em `application-config.ts` designa o papel padrão usado pelas funções de lógica e pelos componentes de front-end do seu app. Veja `defineRole` acima para detalhes. - -* O token em tempo de execução injetado como `TWENTY_APP_ACCESS_TOKEN` é derivado desse papel. -* O cliente tipado é restrito às permissões concedidas a esse papel. -* Siga o princípio do menor privilégio: crie um papel dedicado com apenas as permissões de que suas funções precisam. - -##### Papel de função padrão - -Ao criar um novo app com o scaffold, a CLI cria um arquivo de papel padrão: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -O `universalIdentifier` desse papel é referenciado em `application-config.ts` como `defaultRoleUniversalIdentifier`: - -* **\*.role.ts** define o que o papel pode fazer. -* **application-config.ts** aponta para esse papel para que suas funções herdem suas permissões. - -Notas: -* Comece pelo papel gerado pelo scaffold e depois restrinja-o progressivamente seguindo o princípio do menor privilégio. -* Substitua `objectPermissions` e `fieldPermissions` pelos objetos e campos de que suas funções realmente precisam. -* `permissionFlags` controlam o acesso a recursos em nível de plataforma. Mantenha-os no mínimo necessário. -* Veja um exemplo funcional: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Objetos personalizados descrevem tanto o esquema quanto o comportamento de registros no seu espaço de trabalho. Use `defineObject()` para definir objetos com validação integrada: - -```ts postCard.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, - }, - ], -}); -``` - -Pontos-chave: - -* Use `defineObject()` para validação integrada e melhor suporte na IDE. -* O `universalIdentifier` deve ser exclusivo e estável entre implantações. -* Cada campo requer `name`, `type`, `label` e seu próprio `universalIdentifier` estável. -* O array `fields` é opcional — você pode definir objetos sem campos personalizados. -* Você pode criar novos objetos usando `yarn twenty add`, que orienta você sobre nomeação, campos e relacionamentos. - - -**Os campos base são criados automaticamente.** Quando você define um objeto personalizado, o Twenty adiciona automaticamente campos padrão -como `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` e `deletedAt`. -Você não precisa definir esses no seu array `fields` — adicione apenas seus campos personalizados. -Você pode substituir os campos padrão definindo um campo com o mesmo nome no seu array `fields`, -mas isso não é recomendado. - - - - - -Use `defineField()` para adicionar campos a objetos que não são seus — como objetos padrão do Twenty (Person, Company, etc.). ou a objetos de outros apps. Ao contrário dos campos inline em `defineObject()`, os campos independentes exigem um `objectUniversalIdentifier` para especificar qual objeto eles estendem: - -```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' }, - ], -}); -``` - -Pontos-chave: -* `objectUniversalIdentifier` identifica o objeto de destino. Para objetos padrão, use `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` exportado de `twenty-sdk`. -* Ao definir campos inline em `defineObject()`, você não precisa de `objectUniversalIdentifier` — ele é herdado do objeto pai. -* `defineField()` é a única forma de adicionar campos a objetos que você não criou com `defineObject()`. - - - - -As relações conectam objetos entre si. No Twenty, as relações são sempre **bidirecionais** — você define ambos os lados, e cada lado faz referência ao outro. - -Existem dois tipos de relação: - -| Tipo de relação | Descrição | Tem chave estrangeira? | -| --------------- | ----------------------------------------------------------------- | ---------------------- | -| `MANY_TO_ONE` | Muitos registros deste objeto apontam para um registro do destino | Sim (`joinColumnName`) | -| `ONE_TO_MANY` | Um registro deste objeto possui muitos registros do destino | Não (lado inverso) | - -#### Como as relações funcionam - -Toda relação requer **dois campos** que façam referência um ao outro: - -1. O lado **MANY_TO_ONE** — fica no objeto que contém a chave estrangeira -2. O lado **ONE_TO_MANY** — fica no objeto que possui a coleção - -Ambos os campos usam `FieldType.RELATION` e fazem referência cruzada um ao outro via `relationTargetFieldMetadataUniversalIdentifier`. - -#### Exemplo: Um cartão postal tem muitos destinatários - -Suponha que um `PostCard` possa ser enviado para muitos registros `PostCardRecipient`. Cada destinatário pertence a exatamente um cartão postal. - -**Etapa 1: Defina o lado ONE_TO_MANY em PostCard** (o lado "um"): - -```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, - }, -}); -``` - -**Etapa 2: Defina o lado MANY_TO_ONE em PostCardRecipient** (o lado "muitos" — contém a chave estrangeira): - -```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', - }, -}); -``` - - -**Importações circulares:** Ambos os campos de relação referenciam o `universalIdentifier` um do outro. Para evitar problemas de importação circular, exporte os IDs dos seus campos como constantes nomeadas de cada arquivo e importe-os no outro arquivo. O sistema de build resolve isso em tempo de compilação. - - -#### Relacionando a objetos padrão - -Para criar uma relação com um objeto integrado do Twenty (Person, Company, etc.), use `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', - }, -}); -``` - -#### Propriedades de campos de relação - -| Propriedade | Obrigatório | Descrição | -| ------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------- | -| `type` | Sim | Deve ser `FieldType.RELATION` | -| `relationTargetObjectMetadataUniversalIdentifier` | Sim | O `universalIdentifier` do objeto de destino | -| `relationTargetFieldMetadataUniversalIdentifier` | Sim | O `universalIdentifier` do campo correspondente no objeto de destino | -| `universalSettings.relationType` | Sim | `RelationType.MANY_TO_ONE` ou `RelationType.ONE_TO_MANY` | -| `universalSettings.onDelete` | Apenas para MANY_TO_ONE | O que acontece quando o registro referenciado é excluído: `CASCADE`, `SET_NULL`, `RESTRICT` ou `NO_ACTION` | -| `universalSettings.joinColumnName` | Apenas para MANY_TO_ONE | Nome da coluna no banco de dados para a chave estrangeira (por exemplo, `postCardId`) | - -#### Campos de relação inline em defineObject - -Você também pode definir campos de relação diretamente dentro de `defineObject()`. Nesse caso, omita `objectUniversalIdentifier` — ele é herdado do objeto pai: - -```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 - ], -}); -``` - - - -## Gerando entidades com `yarn twenty add` - -Em vez de criar arquivos de entidade manualmente, você pode usar o scaffolder interativo: - -```bash filename="Terminal" -yarn twenty add -``` - -Isso solicita que você escolha um tipo de entidade e orienta você pelos campos obrigatórios. Ele gera um arquivo pronto para uso com um `universalIdentifier` estável e a chamada correta de `defineEntity()`. - -Você também pode passar o tipo de entidade diretamente para pular o primeiro prompt: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Tipos de entidade disponíveis - -| Tipo de entidade | Comando | Arquivo gerado | -| ------------------------- | ------------------------------------ | ------------------------------------------------------- | -| Objeto | `yarn twenty add object` | `src/objects/\.ts` | -| Campo | `yarn twenty add field` | `src/fields/\.ts` | -| Função lógica | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Componente de front-end | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Papel | `yarn twenty add role` | `src/roles/\.ts` | -| Habilidade | `yarn twenty add skill` | `src/skills/\.ts` | -| Agente | `yarn twenty add agent` | `src/agents/\.ts` | -| Vista | `yarn twenty add view` | `src/views/\.ts` | -| Item do menu de navegação | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Layout da página | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### O que o scaffolder gera - -Cada tipo de entidade tem seu próprio modelo. Por exemplo, `yarn twenty add object` solicita: - -1. **Nome (singular)** — por exemplo, `invoice` -2. **Nome (plural)** — por exemplo, `invoices` -3. **Rótulo (singular)** — preenchido automaticamente a partir do nome (por exemplo, `Invoice`) -4. **Rótulo (plural)** — preenchido automaticamente (por exemplo, `Invoices`) -5. **Criar uma view e um item de navegação?** — se você responder sim, o scaffolder também gera uma view correspondente e um link na barra lateral para o novo objeto. - -Outros tipos de entidade têm prompts mais simples — a maioria pede apenas um nome. - -O tipo de entidade `field` é mais detalhado: ele solicita o nome do campo, rótulo, tipo (a partir de uma lista de todos os tipos de campo disponíveis como `TEXT`, `NUMBER`, `SELECT`, `RELATION`, etc.) e o `universalIdentifier` do objeto de destino. - -### Caminho de saída personalizado - -Use a opção `--path` para colocar o arquivo gerado em um local personalizado: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/front-components.mdx deleted file mode 100644 index 595aba0280..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: Componentes de front-end -description: Crie componentes React que renderizam dentro da UI do Twenty com isolamento em sandbox. -icon: window-maximize ---- - -Componentes de front-end são componentes React que renderizam diretamente dentro da UI do Twenty. Eles são executados em um Web Worker isolado usando Remote DOM — seu código é sandboxed, mas renderiza nativamente na página, não em um iframe. - -## Onde os componentes de front-end podem ser usados - -Os componentes de front-end podem ser renderizados em dois locais dentro do Twenty: - -* **Painel lateral** — Componentes de front-end não headless abrem no painel lateral direito. Este é o comportamento padrão quando um componente de front-end é acionado pelo menu de comandos. -* **Widgets (painéis e páginas de registro)** — Componentes de front-end podem ser incorporados como widgets nos layouts de página. Ao configurar um painel ou o layout de uma página de registro, os usuários podem adicionar um widget de componente de front-end. - -## Exemplo básico - -A maneira mais rápida de ver um componente de front-end em ação é registrá-lo como um **item do menu de comando**. Use `defineCommandMenuItem` em um arquivo separado para fazer o componente aparecer como um botão de ação rápida no canto superior direito da página: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -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', -}); -``` - -Após sincronizar com `yarn twenty dev` (ou executando uma única vez o `yarn twenty dev --once`), a ação rápida aparece no canto superior direito da página: - -
- Botão de ação rápida no canto superior direito -
- -Clique nele para renderizar o componente inline. - -## Campos de configuração - -| Campo | Obrigatório | Descrição | -| --------------------- | ----------- | ---------------------------------------------------------------------------- | -| `universalIdentifier` | Sim | ID único e estável para este componente | -| `component` | Sim | Uma função de componente React | -| `name` | Não | Nome de Exibição | -| `description` | Não | Descrição do que o componente faz | -| `isHeadless` | Não | Defina como `true` se o componente não tiver interface visível (veja abaixo) | - -## Colocando um componente de front-end em uma página - -Além de comandos, você pode incorporar um componente de front-end diretamente em uma página de registro adicionando-o como um widget em um **layout de página**. Veja a seção [definePageLayout](/l/pt/developers/extend/apps/skills-and-agents#definepagelayout) para obter detalhes. - -## Headless vs não headless - -Os componentes de front-end têm dois modos de renderização controlados pela opção `isHeadless`: - -**Não headless (padrão)** — O componente renderiza uma interface visível. Quando acionado pelo menu de comandos, ele é aberto no painel lateral. Este é o comportamento padrão quando `isHeadless` é `false` ou omitido. - -**Headless (`isHeadless: true`)** — O componente é montado de forma invisível em segundo plano. Ele não abre o painel lateral. Componentes headless são projetados para ações que executam lógica e, em seguida, se desmontam — por exemplo, executar uma tarefa assíncrona, navegar para uma página ou exibir um modal de confirmação. Eles se combinam naturalmente com os componentes Command do SDK descritos abaixo. - -```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, -}); -``` - -Como o componente retorna `null`, o Twenty ignora renderizar um contêiner para ele — nenhum espaço vazio aparece no layout. O componente ainda tem acesso a todos os hooks e à API de comunicação do host. - -## Componentes Command do SDK - -O pacote `twenty-sdk` fornece quatro componentes auxiliares Command projetados para componentes de front-end headless. Cada componente executa uma ação ao montar, trata erros exibindo uma notificação de snackbar e desmonta automaticamente o componente de front-end ao concluir. - -Importe-os de `twenty-sdk/command`: - -* **`Command`** — Executa um callback assíncrono via a prop `execute`. -* **`CommandLink`** — Navega para um caminho do app. Props: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — Abre um modal de confirmação. Se o usuário confirmar, executa o callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — Abre uma página específica do painel lateral. Props: `page`, `pageTitle`, `pageIcon`. - -Aqui está um exemplo completo de um componente de front-end headless usando `Command` para executar uma ação a partir do menu de comandos: - -```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 ; -}; - -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', -}); -``` - -E um exemplo usando `CommandModal` para solicitar confirmação antes de executar: - -```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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, -}); -``` - -## Acessando o contexto de execução - -Dentro do seu componente, use hooks do SDK para acessar o usuário atual, o registro e a instância do componente: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Hooks disponíveis: - -| Hook | Retorna | Descrição | -| --------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------- | -| `useUserId()` | `string` ou `null` | O ID do usuário atual | -| `useSelectedRecordIds()` | `string[]` | Todos os IDs dos registros selecionados (array vazio se nenhum estiver selecionado) | -| `useRecordId()` | `string` ou `null` | **Obsoleto.** Use `useSelectedRecordIds()` em vez disso | -| `useFrontComponentId()` | `string` | O ID desta instância do componente | -| `useFrontComponentExecutionContext(selector)` | varia | Acesse o contexto de execução completo com uma função seletora | - -## API de comunicação do host - -Componentes de front-end podem acionar navegação, modais e notificações usando funções de `twenty-sdk`: - -| Função | Descrição | -| ----------------------------------------------- | ------------------------------------- | -| `navigate(to, params?, queryParams?, options?)` | Navegar para uma página no app | -| `openSidePanelPage(params)` | Abrir um painel lateral | -| `closeSidePanel()` | Fechar o painel lateral | -| `openCommandConfirmationModal(params)` | Mostrar um diálogo de confirmação | -| `enqueueSnackbar(params)` | Mostrar uma notificação do tipo toast | -| `unmountFrontComponent()` | Desmontar o componente | -| `updateProgress(progress)` | Atualizar um indicador de progresso | - -Aqui está um exemplo que usa a API do host para exibir um snackbar e fechar o painel lateral após a conclusão de uma ação: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -### Trabalhando com vários registros - -Use `useSelectedRecordIds()` para lidar com vários registros selecionados. Isso é útil para operações em lote: - -```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 ( -
-

Export {selectedRecordIds.length} selected record(s)?

- -
- ); -}; - -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, - }, -}); -``` - -## defineCommandMenuItem - -Use `defineCommandMenuItem` para registrar um componente de front-end no menu de comando (Cmd+K). Se `isPinned` for `true`, ele também aparece como um botão de ação rápida no canto superior direito da página. - -```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', -}); -``` - -| Campo | Obrigatório | Descrição | -| --------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Sim | ID exclusivo e estável para o comando | -| `label` | Sim | Rótulo completo exibido no menu de comandos (Cmd+K) | -| `frontComponentUniversalIdentifier` | Sim | O `universalIdentifier` do componente de front-end que este comando abre | -| `shortLabel` | Não | Rótulo mais curto exibido no botão fixado de ação rápida | -| `icon` | Não | Nome do ícone exibido ao lado do rótulo (por exemplo, `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Não | Quando `true`, mostra o comando como um botão de ação rápida no canto superior direito da página | -| `availabilityType` | Não | Controla onde o comando aparece: `'GLOBAL'` (sempre disponível), `'RECORD_SELECTION'` (apenas quando registros estão selecionados) ou `'FALLBACK'` (exibido quando nenhum outro comando corresponde) | -| `availabilityObjectUniversalIdentifier` | Não | Restringe o comando a páginas de um tipo específico de objeto (por exemplo, somente em registros de Company) | -| `conditionalAvailabilityExpression` | Não | Uma expressão booleana para controlar dinamicamente se o comando é visível (veja abaixo) | - -## Expressões de disponibilidade condicional - -O campo `conditionalAvailabilityExpression` permite controlar quando um comando é visível com base no contexto da página atual. Importe variáveis tipadas e operadores de `twenty-sdk` para construir expressões: - -```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, - ), -}); -``` - -**Variáveis de contexto** — representam o estado atual da página: - -| Variável | Tipo | Descrição | -| ------------------------------ | --------- | --------------------------------------------------------------------------- | -| `pageType` | `string` | Tipo de página atual (por exemplo, `'RecordIndexPage'`, `'RecordShowPage'`) | -| `isInSidePanel` | `boolean` | Se o componente é renderizado em um painel lateral | -| `numberOfSelectedRecords` | `number` | Número de registros atualmente selecionados | -| `isSelectAll` | `boolean` | Se "selecionar tudo" está ativo | -| `selectedRecords` | `array` | Os objetos de registro selecionados | -| `favoriteRecordIds` | `array` | IDs dos registros marcados como favoritos | -| `objectPermissions` | `object` | Permissões para o tipo de objeto atual | -| `targetObjectReadPermissions` | `object` | Permissões de leitura para o objeto alvo | -| `targetObjectWritePermissions` | `object` | Permissões de escrita para o objeto alvo | -| `featureFlags` | `object` | Flags de recurso ativas | -| `objectMetadataItem` | `object` | Metadados do tipo de objeto atual | -| `hasAnySoftDeleteFilterOnView` | `boolean` | Se a visualização atual tem um filtro de soft-delete | - -**Operadores** — combine variáveis em expressões booleanas: - -| Operador | Descrição | -| ----------------------------------- | ---------------------------------------------------------------------- | -| `isDefined(value)` | `true` se o valor não for null/undefined | -| `isNonEmptyString(value)` | `true` se o valor for uma string não vazia | -| `includes(array, value)` | `true` se o array contiver o valor | -| `includesEvery(array, prop, value)` | `true` se a propriedade de cada item incluir o valor | -| `every(array, prop)` | `true` se a propriedade for truthy em cada item | -| `everyDefined(array, prop)` | `true` se a propriedade estiver definida em cada item | -| `everyEquals(array, prop, value)` | `true` se a propriedade for igual ao valor em cada item | -| `some(array, prop)` | `true` se a propriedade for truthy em pelo menos um item | -| `someDefined(array, prop)` | `true` se a propriedade estiver definida em pelo menos um item | -| `someEquals(array, prop, value)` | `true` se a propriedade for igual ao valor em pelo menos um item | -| `someNonEmptyString(array, prop)` | `true` se a propriedade for uma string não vazia em pelo menos um item | -| `none(array, prop)` | `true` se a propriedade for falsy em cada item | -| `noneDefined(array, prop)` | `true` se a propriedade for undefined em cada item | -| `noneEquals(array, prop, value)` | `true` se a propriedade não for igual ao valor em nenhum item | - -## Recursos públicos - -Componentes de front-end podem acessar arquivos do diretório `public/` do app usando `getPublicAssetUrl`: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -Veja a [seção de recursos públicos](/l/pt/developers/extend/apps/cli-and-testing#public-assets-public-folder) para obter detalhes. - -## Estilização - -Componentes de front-end suportam várias abordagens de estilização. Você pode usar: - -* **Estilos inline** — `style={{ color: 'red' }}` -* **Componentes de UI do Twenty** — importe de `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar e mais) -* **Emotion** — CSS-in-JS com `@emotion/react` -* **Styled-components** — padrões `styled.div` -* **Tailwind CSS** — classes utilitárias -* **Qualquer biblioteca CSS-in-JS** compatível com React - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/getting-started.mdx deleted file mode 100644 index 7649045ee2..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Primeiros passos -icon: rocket -description: Crie seu primeiro app do Twenty em minutos. ---- - -## Pré-requisitos - -* **Node.js 24+** — [Baixar](https://nodejs.org/) -* **Yarn 4** — Vem com o Node.js via Corepack. Ative-o: `corepack enable` -* **Docker** — [Baixar](https://www.docker.com/products/docker-desktop/). Necessário para executar um servidor Twenty local. Ignore se você já tiver o Twenty em execução em outro lugar. - -A criação de um aplicativo Twenty tem três fases. A ferramenta de scaffolding as reúne em um único comando do fluxo ideal, mas cada fase é um conceito separado — quando algo falha, saber em que fase você está indica o que corrigir. - -| Fase | O que você faz | Ferramenta | Resultado | -| --------------------------- | -------------------------------------------------- | ----------------------------- | ------------------------------------- | -| **1. Criar scaffolding** | Gerar o código-fonte do aplicativo | `npx create-twenty-app` | Um projeto TypeScript em disco | -| **2. Executar um servidor** | Iniciar um servidor Twenty para o qual sincronizar | Docker + `yarn twenty server` | Uma instância Twenty em execução | -| **3. Sincronizar** | Sincronize seu código em tempo real com o servidor | `yarn twenty dev` | Suas alterações aparecem na interface | - ---- - -## Fase 1 — Fazer scaffolding do seu projeto - -Crie um novo aplicativo a partir do modelo: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Você será solicitado a informar um nome e uma descrição — pressione **Enter** para aceitar os valores padrão. Isso gera um projeto TypeScript em `my-twenty-app/` com um `application-config.ts` inicial, um papel padrão, um fluxo de trabalho de CI e um teste de integração. - -**Após esta fase:** você tem o código-fonte de um aplicativo na sua máquina. Ele ainda não está em execução — isso é a Fase 2. - ---- - -## Fase 2 — Executar um servidor Twenty local - -Seu aplicativo precisa de um servidor Twenty para o qual sincronizar. O servidor é uma instância completa do Twenty — interface, API GraphQL, PostgreSQL — executando localmente no Docker. Seu código local envia suas definições para esse servidor, o que faz com que elas apareçam na interface. - -A ferramenta de scaffolding oferece iniciar um para você: - -> **Você gostaria de configurar uma instância local do Twenty?** - -* **Sim (recomendado)** — baixa a imagem Docker `twentycrm/twenty-app-dev` e a inicia na porta `2020`. Certifique-se de que o Docker esteja em execução primeiro. -* **Não** — escolha isto se você já tiver um servidor Twenty ao qual deseja se conectar. Você pode conectá-lo depois com `yarn twenty remote add`. - -
- Deve iniciar instância local? -
- -Quando o servidor estiver ativo, um navegador será aberto para login. Use a conta de demonstração pré-configurada: - -* **E-mail:** `tim@apple.dev` -* **Senha:** `tim@apple.dev` - -
- Tela de login do Twenty -
- -Clique em **Authorize** na próxima tela — isso dá à CLI acesso ao seu espaço de trabalho. - -
- Tela de autorização da CLI do Twenty -
- -Seu terminal confirmará que tudo está configurado. - -
- Scaffold do aplicativo criado com sucesso -
- -**Após esta fase:** você tem um servidor Twenty em execução em [http://localhost:2020](http://localhost:2020) com sua CLI autorizada a sincronizar com ele. - - -Se o Docker não estiver instalado ou em execução, a ferramenta de scaffolding informará o comando de inicialização correto para o seu sistema operacional. Quando o Docker estiver ativo, você pode retomar com `yarn twenty server start` — sem necessidade de recriar o scaffolding. - - ---- - -## Fase 3 — Sincronizar suas alterações - -Este é o ciclo interno no qual você passará a maior parte do tempo. - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Isso monitora `src/`, recompila a cada alteração e sincroniza o resultado com o servidor. Edite um arquivo, salve e, em um segundo, o servidor refletirá a alteração. Você verá um painel de status em tempo real no seu terminal. - -Para uma saída mais detalhada (logs de build, solicitações de sincronização, rastros de erro), adicione `--verbose`. - -
- Saída do terminal no modo de desenvolvimento -
- -Abra [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Você deverá ver seu aplicativo em **Your Apps**. - -
- Lista Your Apps exibindo My twenty app -
- -Clique em **My twenty app** para ver seu **registro do aplicativo** — um registro em nível de servidor que descreve seu aplicativo (nome, identificador, credenciais OAuth, origem). Um registro pode ser instalado em vários espaços de trabalho no mesmo servidor. - -
- Detalhes do registro do aplicativo -
- -Clique em **View installed app** para ver a instalação no espaço de trabalho. A aba **About** mostra a versão e as opções de gerenciamento. - -
- Aplicação instalada -
- -**Após esta fase:** você tem um ciclo de desenvolvimento em tempo real. Edite qualquer arquivo em `src/` e ele aparecerá na interface. - -### Sincronização única para CI e scripts - -Passe `--once` para executar uma única compilação + sincronização e sair — mesmo pipeline, sem watcher: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Comando | Comportamento | Quando usar | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -| `yarn twenty dev` | Monitora e ressincroniza a cada alteração. Fica em execução até você interrompê-lo. | Desenvolvimento local interativo. | -| `yarn twenty dev --once` | Executa uma única compilação + sincronização e, em seguida, encerra com o código `0` em caso de sucesso ou `1` em caso de falha. | Scripts, CI, hooks de pre-commit, agentes de IA e fluxos de trabalho com script. | - -Ambos os modos precisam de um servidor em modo de desenvolvimento e de um remoto autenticado. - - -O modo de desenvolvimento só está disponível em instâncias do Twenty em modo de desenvolvimento (`NODE_ENV=development`). Instâncias de produção rejeitam solicitações de sincronização de desenvolvimento — use `yarn twenty deploy` para implantar em servidores de produção. Veja [Publicação de aplicativos](/l/pt/developers/extend/apps/publishing). - - ---- - -## O que você pode criar - -Os aplicativos são compostos por **entidades** — cada uma definida como um arquivo TypeScript com um único `export default`: - -| Entidade | O que faz | -| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| **Objetos e campos** | Modelos de dados personalizados (Cartão postal, Fatura etc.) com campos tipados | -| **Funções lógicas** | Funções TypeScript do lado do servidor acionadas por rotas HTTP, agendamentos do cron ou eventos de banco de dados | -| **Componentes de front-end** | Componentes React que são renderizados na UI do Twenty (painel lateral, widgets, menu de comandos) | -| **Habilidades e agentes** | Recursos de IA — instruções reutilizáveis e assistentes autônomos | -| **Exibições e navegação** | Exibições de lista pré-configuradas e itens de menu da barra lateral | -| **Layouts de página** | Páginas de detalhes de registros personalizadas com abas e widgets | - -Referência completa: [Criando aplicativos](/l/pt/developers/extend/apps/building). - -## Estrutura do projeto - -```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 -``` - -| Arquivo / Pasta | Finalidade | -| ---------------------------------------- | ------------------------------------------------------------------------ | -| `src/application-config.ts` | **Obrigatório.** O principal arquivo de configuração do seu aplicativo. | -| `src/default-role.ts` | Papel padrão que controla o que suas funções de lógica podem acessar. | -| `src/constants/universal-identifiers.ts` | UUIDs gerados automaticamente e metadados (nome de exibição, descrição). | -| `src/__tests__/` | Testes de integração (configuração + teste de exemplo). | -| `public/` | Recursos estáticos (imagens, fontes) servidos com seu aplicativo. | - -### Começando a partir de um exemplo - -Use `--example` para começar com um projeto mais completo (objetos personalizados, campos, funções de lógica, componentes de front-end): - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Os exemplos estão em [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). Você também pode criar o scaffolding de entidades individuais em um projeto existente com `yarn twenty add` — veja [Criando aplicativos](/l/pt/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add). - ---- - -## Gerenciando o servidor local - -Use `yarn twenty server` para controlar o contêiner Twenty local: - -| Comando | O que faz | -| -------------------------------------- | ------------------------------------------------ | -| `yarn twenty server start` | Inicia o servidor (baixa a imagem se necessário) | -| `yarn twenty server start --port 3030` | Iniciar em uma porta personalizada | -| `yarn twenty server stop` | Interrompe o servidor (preserva os dados) | -| `yarn twenty server status` | Mostra a URL, a versão e as credenciais de login | -| `yarn twenty server logs` | Transmite os logs do servidor | -| `yarn twenty server reset` | Apaga os dados e começa do zero | -| `yarn twenty server upgrade` | Baixa a imagem mais recente `twenty-app-dev` | -| `yarn twenty server upgrade 2.2.0` | Atualizar para uma versão específica | - -Os dados são persistidos entre reinicializações em dois volumes do Docker (`twenty-app-dev-data` para PostgreSQL, `twenty-app-dev-storage` para arquivos). Use `reset` para apagar tudo. - -### Atualizando a imagem do servidor - -`yarn twenty server upgrade` baixa a imagem mais recente, compara os digests e só recria o contêiner se algo realmente tiver mudado. Os volumes são preservados — apenas o contêiner é substituído. Se uma nova imagem foi baixada e o contêiner estava em execução, a atualização inicia automaticamente um novo contêiner; execute `yarn twenty server start` depois para aguardar até que ele fique saudável. - -```bash filename="Terminal" -yarn twenty server upgrade # Latest -yarn twenty server upgrade 2.2.0 # Specific version -``` - -Verifique a versão em execução com `yarn twenty server status` (ele mostra o `APP_VERSION` incorporado ao contêiner). - -### Executando uma instância de teste paralela - -Passe `--test` para qualquer comando de `server` para gerenciar uma segunda instância totalmente isolada — útil para testes de integração ou para experimentar sem tocar nos seus dados principais de desenvolvimento: - -| Comando | O que faz | -| ----------------------------------- | ------------------------------------------------ | -| `yarn twenty server start --test` | Inicia a instância de teste (padrão: porta 2021) | -| `yarn twenty server stop --test` | Parar | -| `yarn twenty server status --test` | Mostrar seu status | -| `yarn twenty server logs --test` | Transmitir seus logs | -| `yarn twenty server reset --test` | Apagar seus dados | -| `yarn twenty server upgrade --test` | Atualizar sua imagem | - -A instância de teste tem seu próprio contêiner (`twenty-app-dev-test`), volumes (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) e configuração — ela é executada junto com sua instância principal sem conflitos. Combine `--test` com `--port` para substituir 2021. - ---- - -## Configuração manual (sem o gerador) - -Ignore a ferramenta de scaffolding se você estiver adicionando o SDK a um projeto existente: - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -Adicione o script ao `package.json`: - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -Agora você pode executar `yarn twenty dev`, `yarn twenty server start` e o restante. - - -Não instale `twenty-sdk` globalmente — fixe-o por projeto, para que cada aplicativo use sua própria versão. - - ---- - -## Resolução de Problemas - -* **Erros do Docker** — Certifique-se de que o Docker Desktop (ou o daemon) esteja em execução antes de `yarn twenty server start`. A mensagem de erro mostrará o comando de inicialização correto para o seu sistema operacional. -* **Versão errada do Node** — É necessário 24 ou superior. Verifique com `node -v`. -* **Falta o Yarn 4** — Execute `corepack enable`. -* **Dependências com problemas** — `rm -rf node_modules && yarn install`. - -Travou? Peça ajuda no [Discord da Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/layout.mdx deleted file mode 100644 index 2aff24dc82..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Layout -description: Defina vistas, itens do menu de navegação e layouts de página para moldar como seu app aparece no Twenty. -icon: table-columns ---- - -As entidades de layout controlam como seu app aparece na UI do Twenty — o que fica na barra lateral, quais vistas salvas acompanham o app e como uma página de detalhes de registro é organizada. - -## Conceitos de layout - -| Conceito | O que controla | Entidade | -| ----------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------- | -| **Vista** | Uma configuração de lista salva para um objeto — campos visíveis, ordem, filtros, grupos | `defineView` | -| **Item do menu de navegação** | Uma entrada na barra lateral esquerda que aponta para uma vista ou uma URL externa | `defineNavigationMenuItem` | -| **Layout da Página** | As abas e widgets que compõem a página de detalhes de um registro | `definePageLayout` | -| **Aba Layout da Página** | Uma aba independente vinculada a um layout de página existente (padrão ou do seu próprio aplicativo) | `definePageLayoutTab` | - -Vistas, itens de navegação e layouts de página referenciam-se mutuamente por `universalIdentifier`: - -* Um **item do menu de navegação** do tipo `VIEW` aponta para um identificador `defineView`, assim o link da barra lateral abre essa vista salva. -* Um **layout de página** do tipo `RECORD_PAGE` destina-se a um objeto e pode incorporar [front components](/l/pt/developers/extend/apps/front-components) em suas abas como widgets. - - - - -As visualizações são configurações salvas de como os registros de um objeto são exibidos — incluindo quais campos são visíveis, sua ordem e quaisquer filtros ou grupos aplicados. Use `defineView()` para enviar visualizações pré-configuradas com seu app: - -```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, - }, - ], -}); -``` - -Pontos-chave: -* `objectUniversalIdentifier` especifica a qual objeto esta visualização se aplica. -* `key` determina o tipo de visualização (por exemplo, `ViewKey.INDEX` para a visualização de lista principal). -* `fields` controla quais colunas aparecem e sua ordem. Cada campo referencia um `fieldMetadataUniversalIdentifier`. -* Você também pode definir `filters`, `filterGroups`, `groups` e `fieldGroups` para configurações mais avançadas. -* `position` controla a ordenação quando existem várias visualizações para o mesmo objeto. - - - - -Os itens do menu de navegação adicionam entradas personalizadas à barra lateral do espaço de trabalho. Use `defineNavigationMenuItem()` para vincular a visualizações, URLs externas ou objetos: - -```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, -}); -``` - -Pontos-chave: -* `type` determina para o que o item de menu aponta: `NavigationMenuItemType.VIEW` para uma visualização salva ou `NavigationMenuItemType.LINK` para uma URL externa. -* Para links de visualização, defina `viewUniversalIdentifier`. Para links externos, defina `link`. -* `position` controla a ordenação na barra lateral. -* `icon` e `color` (opcionais) personalizam a aparência. - - - - -Layouts de página permitem personalizar como uma página de detalhes do registro se parece — quais abas aparecem, quais widgets estão dentro de cada aba e como eles são organizados. Use `definePageLayout()` para enviar layouts personalizados com seu app: - -```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, - }, - }, - ], - }, - ], -}); -``` - -Pontos-chave: -* `type` geralmente é `'RECORD_PAGE'` para personalizar a visualização de detalhes de um objeto específico. -* `objectUniversalIdentifier` especifica a qual objeto este layout se aplica. -* Cada `tab` define uma seção da página com um `title`, `position` e `layoutMode` (`CANVAS` para layout livre). -* Cada `widget` dentro de uma aba pode renderizar um componente de front-end, uma lista de relações ou outros tipos de widget incorporados. -* `position` nas abas controla sua ordem. Use valores mais altos (por exemplo, 50) para colocar abas personalizadas após as nativas. - - - - -`definePageLayoutTab` permite que seu app adicione uma única aba — com widgets opcionais — a um layout de página **existente**. O caso de uso mais comum é adicionar uma aba personalizada (por exemplo, uma aba de análises ou de resumo por IA) a uma das páginas de registro nativas do Twenty, ou a um layout de página que o seu próprio app já fornece. - -O layout de página de destino deve ser um layout de página **padrão** do Twenty ou um definido pelo **seu próprio app**; referências entre apps a layouts de página pertencentes a outro app instalado não são compatíveis no momento. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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, - }, - }, - ], -}); -``` - -Pontos-chave: -* `pageLayoutUniversalIdentifier` é **obrigatório** ao usar `definePageLayoutTab` e deve apontar para um layout de página que já exista no momento da instalação (padrão ou do seu app). Quando o layout de página pai está ausente, a instalação falha com um erro de validação claro. -* `widgets` têm escopo apenas para esta aba — eles referenciam componentes de interface, visualizações etc., exatamente como widgets definidos inline em `definePageLayout`. -* `position` controla a ordenação em relação às abas existentes no layout de destino. Escolha um valor que posicione sua aba onde você deseja em relação às abas nativas. -* Use isto em vez de `definePageLayout` quando você quiser apenas **adicionar** a um layout existente. Use `definePageLayout` quando você for o proprietário de todo o layout (normalmente uma `RECORD_PAGE` para um objeto que você fornece no seu app, ou uma `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index a1f5720c48..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,565 +0,0 @@ ---- -title: Funções lógicas -description: Defina funções TypeScript no lado do servidor com gatilhos HTTP, cron e de eventos de banco de dados. -icon: bolt ---- - -As funções de lógica são funções TypeScript no lado do servidor que são executadas na plataforma Twenty. Elas podem ser acionadas por solicitações HTTP, agendamentos cron ou eventos de banco de dados — e também podem ser expostas como ferramentas para agentes de IA. - - - - -Cada arquivo de função usa `defineLogicFunction()` para exportar uma configuração com um manipulador e gatilhos opcionais. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Tipos de gatilho disponíveis: -* **httpRoute**: Expõe sua função em um caminho e método HTTP **no endpoint `/s/`**: -> por exemplo, `path: '/post-card/create'` é acessível em `https://your-twenty-server.com/s/post-card/create` -* **cron**: Executa sua função em um agendamento usando uma expressão CRON. -* **databaseEvent**: Executa em eventos do ciclo de vida de objetos do espaço de trabalho. Quando a operação do evento é `updated`, campos específicos a serem observados podem ser especificados no array `updatedFields`. Se deixar indefinido ou vazio, qualquer atualização acionará a função. -> por exemplo, `person.updated`, `*.created`, `company.*` - - -Você também pode executar manualmente uma função usando a CLI: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Você pode acompanhar os logs com: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Payload de gatilho de rota - -Quando um gatilho de rota invoca sua função de lógica, ela recebe um objeto `RoutePayload` que segue o [formato HTTP API v2 da AWS](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). -Importe o tipo `RoutePayload` de `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -O tipo `RoutePayload` tem a seguinte estrutura: - - | Propriedade | Tipo | Descrição | Exemplo | - | ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | Cabeçalhos HTTP (apenas aqueles listados em `forwardedRequestHeaders`) | veja a seção abaixo | - | `queryStringParameters` | `Record\` | Parâmetros de query string (valores múltiplos unidos por vírgulas) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Parâmetros de caminho extraídos do padrão de rota | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Corpo da requisição analisado (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Corpo da solicitação UTF-8 original, antes da análise de JSON. Útil para verificar assinaturas de webhook no estilo HMAC (por exemplo, `X-Hub-Signature-256` do GitHub, Stripe). `undefined` quando o ambiente de execução não o preservou. | | - | `isBase64Encoded` | `boolean` | Se o corpo está codificado em base64 | | - | `requestContext.http.method` | `string` | Método HTTP (GET, POST, PUT, PATCH, DELETE) | | - | `requestContext.http.path` | `string` | Caminho bruto da requisição | | - - -#### forwardedRequestHeaders - -Por padrão, os cabeçalhos HTTP das requisições recebidas **não** são repassados para sua função de lógica por motivos de segurança. -Para acessar cabeçalhos específicos, liste-os explicitamente no array `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'], - }, -}); -``` - -No seu manipulador, acesse os cabeçalhos encaminhados assim: - -```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 }; -}; -``` - - -Os nomes dos cabeçalhos são normalizados para minúsculas. Acesse-os usando chaves em minúsculas (por exemplo, `event.headers['content-type']`). - - -#### Expor uma função como ferramenta de IA ou como ação de fluxo de trabalho - -As funções de lógica podem ser expostas em duas superfícies, cada uma com seu próprio gatilho: - -* **`toolTriggerSettings`** — torna a função disponível para os recursos de IA do Twenty (chat, MCP, chamadas de função). Usa o JSON Schema padrão, o formato que os LLMs entendem nativamente. -* **`workflowActionTriggerSettings`** — torna a função visível como uma etapa no construtor visual de fluxos de trabalho. Usa o `InputSchema` avançado do Twenty para que o construtor possa renderizar editores de campo adequados, seletores de variáveis e rótulos. - -Uma função pode optar por uma, pela outra ou por ambas. Ficam ao lado de `cronTriggerSettings`, `databaseEventTriggerSettings` e `httpRouteTriggerSettings` — mesmo padrão, mesmo formato. - -```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: {}, -}); -``` - -Pontos-chave: - -* Uma função pode misturar superfícies — declare tanto `toolTriggerSettings` quanto `workflowActionTriggerSettings` para expô-la no chat E no construtor de fluxos de trabalho. -* `toolTriggerSettings.inputSchema` e `workflowActionTriggerSettings.inputSchema` são opcionais. Quando omitidos, o construtor de manifestos os infere a partir do código-fonte do handler (JSON Schema para a ferramenta de IA, `InputSchema` do Twenty para a ação de fluxo de trabalho). Forneça um explicitamente quando quiser uma tipagem mais rica — por exemplo, com campos compatíveis com `FieldMetadataType`, como `CURRENCY` ou `RELATION` para o construtor de fluxos de trabalho, ou com campos `description` que o agente de IA pode ler: - -```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'], - }, - }, -}); -``` - - -**Escreva uma boa `description`.** Os agentes de IA dependem do campo `description` da função para decidir quando usar a ferramenta. Seja específico sobre o que a ferramenta faz e quando ela deve ser chamada. - - - - - -Uma função de pós-instalação é uma função lógica que é executada automaticamente assim que seu aplicativo terminar de ser instalado em um espaço de trabalho. O servidor a executa **depois** que os metadados do aplicativo forem sincronizados e o cliente do SDK for gerado, para que o espaço de trabalho esteja totalmente pronto para uso e o novo esquema esteja disponível. Casos de uso típicos incluem popular dados padrão, criar registros iniciais, configurar as definições do espaço de trabalho ou provisionar recursos em serviços de terceiros. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Você também pode executar manualmente a função de pós-instalação a qualquer momento usando a CLI: - -```bash filename="Terminal" -yarn twenty exec --postInstall -``` - -Pontos-chave: -* As funções de pós-instalação usam `definePostInstallLogicFunction()` — uma variante especializada que omite as configurações de gatilho (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`). -* O manipulador recebe um `InstallPayload` com `{ previousVersion?: string; newVersion: string }` — `newVersion` é a versão que está sendo instalada, e `previousVersion` é a versão que foi instalada anteriormente (ou `undefined` em uma instalação nova). Use esses valores para distinguir instalações novas de atualizações e para executar lógica de migração específica da versão. -* **Quando o hook é executado**: apenas em instalações novas, por padrão. Passe `shouldRunOnVersionUpgrade: true` se você também quiser que ele seja executado quando o app for atualizado a partir de uma versão anterior. Quando omitida, a flag tem valor padrão `false` e as atualizações ignoram o hook. -* **Modelo de execução — assíncrono por padrão, síncrono opcional**: a flag `shouldRunSynchronously` controla *como* a pós-instalação é executada. - * `shouldRunSynchronously: false` *(padrão)* — o hook é **enfileirado na fila de mensagens** com `retryLimit: 3` e é executado de forma assíncrona em um worker. A resposta da instalação retorna assim que o job é enfileirado, então um manipulador lento ou com falha não bloqueia quem chamou. O worker tentará novamente até três vezes. **Use isto para jobs de longa duração** — popular grandes conjuntos de dados, chamar APIs de terceiros lentas, provisionar recursos externos, qualquer coisa que possa exceder uma janela razoável de resposta HTTP. - * `shouldRunSynchronously: true` — o hook é executado **inline durante o fluxo de instalação** (mesmo executor da pré-instalação). A requisição de instalação bloqueia até o manipulador terminar e, se ele lançar uma exceção, quem chamou a instalação recebe um `POST_INSTALL_ERROR`. Sem novas tentativas automáticas. **Use isto para trabalhos rápidos que precisam ser concluídos antes da resposta** — por exemplo, emitir um erro de validação para o usuário ou fazer uma configuração rápida da qual o cliente dependerá imediatamente após a chamada de instalação retornar. Tenha em mente que a migração de metadados já foi aplicada quando a pós-instalação é executada, então uma falha no modo síncrono **não** reverte as alterações de esquema — ela apenas expõe o erro. -* Garanta que seu manipulador seja idempotente. No modo assíncrono, a fila pode tentar novamente até três vezes; em qualquer modo, o hook pode ser executado novamente em atualizações quando `shouldRunOnVersionUpgrade: true`. -* As variáveis de ambiente `APPLICATION_ID`, `APP_ACCESS_TOKEN` e `API_URL` estão disponíveis dentro do manipulador (assim como em qualquer outra função de lógica), então você pode chamar a API da Twenty com um token de acesso de aplicativo com escopo para o seu app. -* É permitida apenas uma função de pós-instalação por app. A geração do manifesto apresentará erro se mais de uma for detectada. -* O `universalIdentifier`, `shouldRunOnVersionUpgrade` e `shouldRunSynchronously` da função são anexados automaticamente ao manifesto do aplicativo no campo `postInstallLogicFunction` durante o build — você não precisa referenciá-los em `defineApplication()`. -* O tempo limite padrão é definido como 300 segundos (5 minutos) para permitir tarefas de configuração mais longas, como o pré-carregamento de dados. -* **Não executado no modo de desenvolvimento**: quando um app é registrado localmente (via `yarn twenty dev`), o servidor pula completamente o fluxo de instalação e sincroniza arquivos diretamente pelo watcher da CLI — portanto, a pós-instalação nunca é executada no modo de desenvolvimento, independentemente de `shouldRunSynchronously`. Use `yarn twenty exec --postInstall` para acioná-lo manualmente em um workspace em execução. - - - - -Uma função de pré-instalação é uma função de lógica que é executada automaticamente durante a instalação, **antes que a migração de metadados do workspace seja aplicada**. Ela compartilha o mesmo formato de payload que a pós-instalação (`InstallPayload`), mas está posicionada mais cedo no fluxo de instalação para poder preparar o estado do qual a próxima migração depende — usos típicos incluem fazer backup de dados, validar a compatibilidade com o novo esquema ou arquivar registros que estão prestes a ser reestruturados ou removidos. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Você também pode executar manualmente a função de pré-instalação a qualquer momento usando a CLI: - -```bash filename="Terminal" -yarn twenty exec --preInstall -``` - -Pontos-chave: -* Funções de pré-instalação usam `definePreInstallLogicFunction()` — a mesma configuração especializada da pós-instalação, apenas anexada a um ponto diferente do ciclo de vida. -* Os manipuladores de pré e pós-instalação recebem o mesmo tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importe-o uma vez e reutilize-o para ambos os hooks. -* **Quando o hook é executado**: posicionado imediatamente antes da migração de metadados do workspace (`synchronizeFromManifest`). Antes de executar, o servidor realiza uma "sincronização simplificada" puramente aditiva que registra a função de pré-instalação da **nova** versão nos metadados do workspace — nada mais é alterado — e então a executa. Como essa sincronização é apenas aditiva, os objetos, campos e dados da versão anterior ainda estão intactos quando seu manipulador é executado: você pode ler e fazer backup com segurança do estado pré-migração. -* **Modelo de execução**: a pré-instalação é executada **de forma síncrona** e **bloqueia a instalação**. Se o manipulador lançar uma exceção, a instalação é abortada antes que quaisquer alterações de esquema sejam aplicadas — o workspace permanece na versão anterior em um estado consistente. Isto é intencional: a pré-instalação é sua última chance de recusar uma atualização arriscada. -* Assim como na pós-instalação, é permitida apenas uma função de pré-instalação por app. Ela é anexada ao manifesto do aplicativo sob `preInstallLogicFunction` automaticamente durante o build. -* **Não é executada no modo de desenvolvimento**: igual à pós-instalação — o fluxo de instalação é totalmente ignorado para apps registrados localmente, portanto a pré-instalação nunca é executada com `yarn twenty dev`. Use `yarn twenty exec --preInstall` para acioná-lo manualmente. - - - - -Ambos os hooks fazem parte do mesmo fluxo de instalação e recebem o mesmo `InstallPayload`. A diferença é **quando** eles são executados em relação à migração de metadados do workspace, e isso muda quais dados eles podem manipular com segurança. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -A pré-instalação é sempre **síncrona** (ela bloqueia a instalação e pode abortá-la). A pós-instalação é **assíncrona por padrão** — enfileirada em um worker com novas tentativas automáticas — mas pode optar por execução síncrona com `shouldRunSynchronously: true`. Veja o acordeão `definePostInstallLogicFunction` acima para saber quando usar cada modo. - -**Use `post-install` para qualquer coisa que precise que o novo esquema exista.** Este é o caso mais comum: - -* Popular dados padrão (criando registros iniciais, visualizações padrão, conteúdo de demonstração) em objetos e campos recém-adicionados. -* Registrar webhooks com serviços de terceiros agora que o app tem suas credenciais. -* Chamar sua própria API para finalizar a configuração que depende dos metadados sincronizados. -* Lógica idempotente de "garantir que isso exista" que deve reconciliar o estado em cada atualização — combine com `shouldRunOnVersionUpgrade: true`. - -Exemplo — popular um registro `PostCard` padrão após a instalação: - -```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 => { - 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, -}); -``` - -**Use `pre-install` quando uma migração, de outra forma, destruiria ou corromperia dados existentes.** Como a pré-instalação roda contra o esquema *anterior* e sua falha reverte a atualização, é o lugar certo para qualquer coisa arriscada: - -* **Fazer backup de dados que estão prestes a ser removidos ou reestruturados** — por exemplo, você está removendo um campo na v2 e precisa copiar seus valores para outro campo ou exportá-los para um armazenamento antes que a migração seja executada. -* **Arquivar registros que uma nova restrição invalidaria** — por exemplo, um campo está se tornando `NOT NULL` e você precisa excluir ou corrigir linhas com valores nulos primeiro. -* **Validar a compatibilidade e recusar a atualização se os dados atuais não puderem ser migrados de forma limpa** — lance uma exceção no manipulador e a instalação é abortada sem alterações aplicadas. Isto é mais seguro do que descobrir a incompatibilidade no meio da migração. -* **Renomear ou reatribuir chaves de dados** antes de uma alteração de esquema que perderia a associação. - -Exemplo — arquivar registros antes de uma migração destrutiva: - -```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 => { - // 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, -}); -``` - -**Regra geral:** - -| Você quer... | Usar | -| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| Popular dados padrão, configurar o workspace, registrar recursos externos | `post-install` | -| Executar processos longos de popular dados ou chamadas a terceiros que não devem bloquear a resposta da instalação | `post-install` (padrão — `shouldRunSynchronously: false`, com novas tentativas do worker) | -| Executar uma configuração rápida da qual o chamador dependerá imediatamente após o retorno da chamada de instalação | `post-install` com `shouldRunSynchronously: true` | -| Ler ou fazer backup de dados que a próxima migração perderia | `pre-install` | -| Rejeitar uma atualização que corromperia dados existentes | `pre-install` (lançar uma exceção no manipulador) | -| Executar reconciliação em cada atualização | `post-install` com `shouldRunOnVersionUpgrade: true` | -| Fazer uma configuração única apenas na primeira instalação | `post-install` com `shouldRunOnVersionUpgrade: false` (padrão) | - - -Em caso de dúvida, use **post-install** como padrão. Recurra à pré-instalação somente quando a própria migração for destrutiva e você precisar interceptar o estado anterior antes que ele desapareça. - - - - - -## Clientes de API tipados (twenty-client-sdk) - -O pacote `twenty-client-sdk` fornece dois clientes GraphQL tipados para interagir com a API do Twenty a partir das suas funções de lógica e componentes de front-end. - -| Cliente | Importar | Endpoint | Gerado? | -| ------------------- | ---------------------------- | -------------------------------------------------------------------- | -------------------------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — dados do espaço de trabalho (registros, objetos) | Sim, em tempo de dev/build | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — configuração do espaço de trabalho, upload de arquivos | Não, vem pré-compilado | - - - - -`CoreApiClient` é o cliente principal para consultar e mutar dados do espaço de trabalho. Ele é **gerado a partir do schema do seu espaço de trabalho** durante `yarn twenty dev` ou `yarn twenty build`, então é totalmente tipado para corresponder aos seus objetos e campos. - -```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, - }, -}); -``` - -O cliente usa uma sintaxe de selection-set: passe `true` para incluir um campo, use `__args` para argumentos e aninhe objetos para relações. Você tem preenchimento automático e verificação de tipos completos com base no schema do seu espaço de trabalho. - - -**CoreApiClient é gerado em tempo de dev/build.** Se você usá-lo sem executar primeiro `yarn twenty dev` ou `yarn twenty build`, ele lançará um erro. A geração ocorre automaticamente — a CLI analisa o schema GraphQL do seu espaço de trabalho e gera um cliente tipado usando `@genql/cli`. - - -#### Usando CoreSchema para anotações de tipo - -`CoreSchema` fornece tipos TypeScript que correspondem aos objetos do seu espaço de trabalho — útil para tipar o estado de componentes ou parâmetros de função: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`MetadataApiClient` é fornecido pré-compilado com o SDK (não é necessário gerar). Ele consulta o endpoint `/metadata` para configuração do espaço de trabalho, aplicativos e upload de arquivos. - -```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 }, - }, - }, -}); -``` - -#### Carregamento de arquivos - -`MetadataApiClient` inclui um método `uploadFile` para anexar arquivos a campos do tipo arquivo: - -```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://...' } -``` - -| Parâmetro | Tipo | Descrição | -| ---------------------------------- | -------- | ----------------------------------------------------------------- | -| `fileBuffer` | `Buffer` | O conteúdo bruto do arquivo | -| `filename` | `string` | O nome do arquivo (usado para armazenamento e exibição) | -| `contentType` | `string` | Tipo MIME (padrão para `application/octet-stream` se omitido) | -| `fieldMetadataUniversalIdentifier` | `string` | O `universalIdentifier` do campo do tipo de arquivo no seu objeto | - -Pontos-chave: -* Usa o `universalIdentifier` do campo (não o ID específico do espaço de trabalho), de modo que seu código de upload funcione em qualquer espaço de trabalho onde seu app esteja instalado. -* A `url` retornada é uma URL assinada que você pode usar para acessar o arquivo enviado. - - - - - - Quando seu código é executado no Twenty (funções de lógica ou componentes de front-end), a plataforma injeta credenciais como variáveis de ambiente: - - * `TWENTY_API_URL` — URL base da API do Twenty - * `TWENTY_APP_ACCESS_TOKEN` — Chave de curta duração com escopo para o papel de função padrão do seu aplicativo - - Você **não** precisa passá-las para os clientes — eles leem de `process.env` automaticamente. As permissões da chave de API são determinadas pelo papel referenciado em `defaultRoleUniversalIdentifier` no seu `application-config.ts`. - diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/publishing.mdx deleted file mode 100644 index 2c6667fdef..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Publicação -icon: carregar -description: Distribua seu aplicativo Twenty no Marketplace ou implante-o internamente. ---- - -## Visão Geral - -Depois que seu aplicativo estiver [compilado e testado localmente](/l/pt/developers/extend/apps/building), você tem dois caminhos para distribuí-lo: - -* **Implantar um tarball** — envie seu aplicativo diretamente para um servidor Twenty específico para uso interno ou privado. -* **Publicar no npm** — liste seu aplicativo no Marketplace da Twenty para que qualquer espaço de trabalho possa descobrir e instalar. - -Ambos os caminhos começam na mesma etapa de **build**. - -## Compilando seu app - -Execute o comando build para compilar seu app e gerar um `manifest.json` pronto para distribuição: - -```bash filename="Terminal" -yarn twenty build -``` - -Isso compila seu código-fonte em TypeScript, transpila funções de lógica e componentes de front-end e grava tudo em `.twenty/output/`. Adicione `--tarball` para também gerar um pacote `.tgz` para distribuição manual ou para o comando de deploy. - -## Implantando em um servidor (tarball) - -Para aplicativos que você não quer disponibilizar publicamente — ferramentas proprietárias, integrações apenas para empresas ou builds experimentais — você pode implantar um tarball diretamente em um servidor Twenty. - -### Pré-requisitos - -Antes de implantar, você precisa de um remote configurado apontando para o servidor de destino. Os remotes armazenam a URL do servidor e as credenciais de autenticação localmente em `~/.twenty/config.json`. - -Adicionar um remote: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Implantando - -Compile e envie seu aplicativo para o servidor em uma única etapa: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Compartilhando um aplicativo implantado - - -Compartilhar aplicativos privados (tarball) entre espaços de trabalho é um recurso do plano **Enterprise**. A guia **Distribution** exibirá um aviso de atualização em vez dos controles de compartilhamento até que seu espaço de trabalho tenha uma chave Enterprise válida. Vá para [Configurações > Painel de Administração > Enterprise](/settings/admin-panel#enterprise) para ativá-lo. - - -Aplicativos em tarball não são listados no marketplace público, então outros espaços de trabalho no mesmo servidor não os descobrirão ao navegar. Assim que o seu espaço de trabalho estiver no plano Enterprise, você pode compartilhar um app implantado desta forma: - -1. Vá para **Configurações > Aplicações > Registros** e abra seu aplicativo -2. Na guia **Distribuição**, clique em **Copiar link de compartilhamento** -3. Compartilhe esse link com usuários de outros espaços de trabalho — ele os leva diretamente para a página de instalação do aplicativo - -O link de compartilhamento usa a URL base do servidor (sem qualquer subdomínio de espaço de trabalho), para funcionar em qualquer espaço de trabalho no servidor. - -### Gerenciamento de versões - -Ao atualizar um aplicativo empacotado como tarball já implantado, o servidor exige que o `version` no `package.json` seja **estritamente maior** (de acordo com a ordenação do [semver](https://semver.org)) do que a versão atualmente implantada. Reimplantar a mesma versão, ou enviar uma inferior, é rejeitado antes que o tarball seja armazenado — você verá um erro `VERSION_ALREADY_EXISTS` na CLI. - -Para lançar uma atualização: - -1. Atualize o campo `version` no seu `package.json` (por exemplo, `1.2.3` → `1.2.4`, `1.3.0` ou `2.0.0`) -2. Execute `yarn twenty deploy` (ou `yarn twenty deploy --remote production`) -3. Os espaços de trabalho que têm o aplicativo instalado verão a atualização disponível em suas configurações - - -Tags de pré-lançamento funcionam como esperado: incrementar `1.0.0-rc.1` → `1.0.0-rc.2` é permitido, e uma versão final como `1.0.0` é corretamente reconhecida como superior a `1.0.0-rc.5`. A versão em `package.json` deve ser, ela própria, uma string semver válida. - - -{/* TODO: add screenshot of the Upgrade button */} - -### Compatibilidade da versão do servidor - -Se o seu aplicativo usar um recurso introduzido em uma versão específica do servidor Twenty (por exemplo, provedores OAuth adicionados na v2.3.0), você deve declarar a versão mínima do servidor que seu aplicativo requer usando o campo `engines.twenty` em `package.json`: - -```json filename="package.json" -{ - "name": "twenty-my-app", - "version": "1.0.0", - "engines": { - "node": "^24.5.0", - "twenty": ">=2.3.0" - } -} -``` - -O valor é um [intervalo semver](https://github.com/npm/node-semver#ranges) padrão. Padrões comuns: - -| Intervalo | Significado | -| ---------------------------------- | ---------------------------------------------------------- | -| `>=2.3.0` | Qualquer servidor a partir de 2.3.0 | -| `>=2.3.0 \<3.0.0` | 2.3.0 ou posterior, mas abaixo da próxima versão principal | -| `^2.3.0` | O mesmo que `>=2.3.0 \<3.0.0` | - -**O que acontece no momento da implantação e da instalação:** - -* Se `engines.twenty` estiver definido e a versão do servidor de destino não satisfizer o intervalo, a implantação (upload do tarball) ou a instalação será rejeitada com o erro `SERVER_VERSION_INCOMPATIBLE` e uma mensagem indicando tanto o intervalo exigido quanto a versão real do servidor. -* Se `engines.twenty` **não estiver definido**, o aplicativo é aceito em qualquer versão do servidor (retrocompatível com os aplicativos existentes). -* Se o servidor não tiver `APP_VERSION` configurado, a verificação será ignorada. - - -O servidor realiza a verificação definitiva — ele valida `engines.twenty` tanto no upload do tarball quanto na instalação no workspace. Se você implantar um tarball fora de banda ou instalar a partir do marketplace, o servidor ainda impõe a compatibilidade. - - -## CI/CD automatizado (fluxos de trabalho pré-configurados) - -Os apps gerados com `create-twenty-app` já vêm com dois fluxos de trabalho do GitHub Actions prontos, em `.github/workflows/`. Eles estão prontos para executar assim que você fizer push do repositório para o GitHub — nenhuma configuração extra é necessária para CI, e CD requer apenas um único segredo. - -### CI — `ci.yml` - -Executa testes de integração a cada push para `main` e a cada pull request. - -**O que faz:** - -1. Faz checkout do código-fonte do seu app. -2. Inicia uma instância de teste do Twenty isolada usando a ação composta `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (o equivalente em CI de `yarn twenty server start --test`). -3. Habilita o Corepack, configura o Node.js a partir do seu `.nvmrc` e instala as dependências com `yarn install --immutable`. -4. Executa `yarn test`, passando `TWENTY_API_URL` e `TWENTY_API_KEY` da instância iniciada para que seus testes possam se comunicar com um servidor real. - -**Opções de configuração:** - -* `TWENTY_VERSION` (env, padrão `latest`) — fixe a versão do servidor Twenty usada no CI editando isto em `ci.yml`. -* A concorrência é agrupada por `github.ref` e cancela execuções em andamento quando há novos pushes. - -Nenhum segredo é necessário — a instância de teste é efêmera e existe apenas durante a execução do job. - -### CD — `cd.yml` - -Faz o deploy do seu app para um servidor Twenty configurado a cada push para `main` e, opcionalmente, a partir de um pull request quando o rótulo `deploy` é aplicado. - -**O que faz:** - -1. Faz checkout do head do PR (para PRs rotulados) ou do commit enviado. -2. Executa `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — o equivalente em CI de `yarn twenty deploy`. -3. Executa `twentyhq/twenty/.github/actions/install-twenty-app@main` para que a versão recém-implantada seja instalada no workspace de destino. - -**Configuração obrigatória:** - -| Configuração | Onde | Finalidade | -| ----------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `env` em `cd.yml` (padrão `http://localhost:3000`) | O servidor Twenty para o qual fazer o deploy. Altere isto para a URL real do seu servidor antes do primeiro uso. | -| `TWENTY_DEPLOY_API_KEY` | Repositório do GitHub **Settings → Secrets and variables → Actions** | Chave de API com permissão de deploy no servidor de destino. | - - -O `TWENTY_DEPLOY_URL` padrão de `http://localhost:3000` é um placeholder — ele não alcançará nada a partir de um runner hospedado pelo GitHub. Atualize-o para a URL pública do seu servidor (ou use um runner self-hosted com acesso à rede) antes de habilitar o CD. - - -**Acionando um deploy de pré-visualização a partir de um PR:** - -Adicione o rótulo `deploy` a um pull request. A condição `if:` em `cd.yml` executará o job para esse PR usando o commit HEAD do PR, permitindo que você valide uma alteração no servidor de destino antes de fazer o merge. - -### Fixando as ações reutilizáveis - -Ambos os fluxos de trabalho fazem referência a ações reutilizáveis em `@main`, portanto as atualizações de ações no repositório `twentyhq/twenty` são aplicadas automaticamente. Se você quiser builds determinísticos, substitua `@main` por um SHA de commit ou uma tag de release em cada linha `uses:`. - -## Publicação no npm - -Publicar no npm torna seu aplicativo descobrível no Marketplace da Twenty. Qualquer espaço de trabalho da Twenty pode navegar, instalar e atualizar aplicativos do Marketplace diretamente pela UI. - -### Requisitos - -* Uma conta no [npm](https://www.npmjs.com) -* A palavra-chave `twenty-app` no array `keywords` do seu `package.json` (adicione-a manualmente — não é incluída por padrão no template `create-twenty-app`) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Metadados do Marketplace - -A configuração `defineApplication()` oferece suporte a campos opcionais que controlam como seu app aparece no marketplace. Use `logoUrl` e `screenshots` para referenciar imagens da pasta `public/`: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -Veja o [acordeão de defineApplication](/l/pt/developers/extend/apps/building#defineentity-functions) na página Building Apps para a lista completa de campos do marketplace (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.). - -#### Dimensões recomendadas para capturas de tela - -O marketplace renderiza `screenshots` em um contêiner fixo de `8:5` (por exemplo, `1600×1000 px`). - - -Capturas de tela de qualquer proporção são exibidas por completo e nunca são cortadas, mas qualquer coisa significativamente mais alta ou mais estreita que `8:5` exibirá faixas vazias nas laterais. - - -### Publicar - -```bash filename="Terminal" -yarn twenty publish -``` - -Para publicar sob uma dist-tag específica (por exemplo, `beta` ou `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### Como funciona a descoberta no marketplace - -O servidor Twenty sincroniza seu catálogo do marketplace a partir do registro do npm **a cada hora**. - -Você pode acionar a sincronização imediatamente em vez de esperar: - -```bash filename="Terminal" -yarn twenty server catalog-sync -# To target a specific remote: -# yarn twenty server catalog-sync --remote production -``` - -Os metadados exibidos no marketplace vêm da sua configuração `defineApplication()` — campos como `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` e `termsUrl`. - - -Se o seu aplicativo não definir um `aboutDescription` em `defineApplication()`, o marketplace usará automaticamente o `README.md` do seu pacote no npm como conteúdo da página Sobre. Isso significa que você pode manter um único README tanto para o npm quanto para o marketplace da Twenty. Se quiser uma descrição diferente no marketplace, defina explicitamente `aboutDescription`. - - -### Publicação via CI - -Use este workflow do GitHub Actions para publicar automaticamente a cada release (usa [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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Para outros sistemas de CI (GitLab CI, CircleCI etc.), aplicam-se os mesmos três comandos: `yarn install`, `yarn twenty build` e, em seguida, `npm publish` a partir de `.twenty/output`. - - -**Proveniência do npm** é opcional, mas recomendada. Publicar com `--provenance` adiciona um selo de confiança à sua listagem no npm, permitindo que os usuários verifiquem que o pacote foi construído a partir de um commit específico em um pipeline de CI público. Consulte a [documentação de proveniência do npm](https://docs.npmjs.com/generating-provenance-statements) para instruções de configuração. - - -## Instalando aplicativos - -Depois que um app é publicado (npm) ou implantado (tarball), os espaços de trabalho podem instalá-lo pela interface do usuário. - -Vá para a página **Configurações > Aplicações** no Twenty, onde é possível navegar e instalar tanto apps do marketplace quanto apps implantados por tarball. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Você também pode instalar apps pela linha de comando: - -```bash filename="Terminal" -yarn twenty install -``` - - -O servidor impõe o versionamento semver na instalação, espelhando as regras da implantação: - -* Instalar a mesma versão que já está instalada no seu espaço de trabalho é rejeitado com um erro `APP_ALREADY_INSTALLED`. -* Instalar uma versão inferior à atualmente instalada é rejeitado com um erro `CANNOT_DOWNGRADE_APPLICATION`. - -Para instalar uma versão mais recente, implante ou publique-a primeiro e, em seguida, execute novamente `yarn twenty install`. - diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index 74ce062242..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Habilidades e agentes -description: Defina habilidades e agentes de IA para o seu aplicativo. -icon: robot ---- - - - Skills and agents are currently in alpha. O recurso é funcional, mas ainda está evoluindo. - - -Os aplicativos podem definir capacidades de IA que residem dentro do espaço de trabalho — instruções de habilidades reutilizáveis e agentes com prompts de sistema personalizados. - - - - -As habilidades definem instruções e capacidades reutilizáveis que os agentes de IA podem usar no seu espaço de trabalho. Use `defineSkill()` para definir habilidades com validação integrada: - -```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`, -}); -``` - -Pontos-chave: -* `name` é uma string de identificador exclusivo para a habilidade (recomenda-se kebab-case). -* `label` é o nome de exibição legível por humanos mostrado na UI. -* `content` contém as instruções da habilidade — este é o texto que o agente de IA usa. -* `icon` (opcional) define o ícone exibido na UI. -* `description` (opcional) fornece contexto adicional sobre a finalidade da habilidade. - - - - -Agentes são assistentes de IA que vivem dentro do seu espaço de trabalho. Use `defineAgent()` para criar agentes com um prompt de sistema personalizado: - -```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.', -}); -``` - -Pontos-chave: -* `name` é a string de identificador exclusiva do agente (recomenda-se kebab-case). -* `label` é o nome de exibição mostrado na UI. -* `prompt` é o prompt do sistema que define o comportamento do agente. -* `description` (opcional) fornece contexto sobre o que o agente faz. -* `icon` (opcional) define o ícone exibido na UI. -* `modelId` (opcional) substitui o modelo de IA padrão usado pelo agente. - - - diff --git a/packages/twenty-docs/l/pt/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/pt/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 99634b238f..0000000000 --- a/packages/twenty-docs/l/pt/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Aplicativos Twenty -description: Crie e gerencie personalizações do Twenty como código. ---- - - -Os aplicativos estão atualmente em testes alfa. O recurso é funcional, mas ainda está evoluindo. - - -## O que são aplicativos? - -Os aplicativos permitem que você estenda o Twenty com objetos, campos, funções de lógica, componentes de interface, habilidades de IA e mais — tudo gerenciado como código. Em vez de configurar tudo pela UI, você define seu modelo de dados e a lógica em TypeScript e implanta em um ou mais workspaces. - -**O que você pode criar:** - -* **Objetos e campos personalizados** — estenda seu modelo de dados com novas entidades ou adicione campos a objetos existentes como Empresa ou Pessoa -* **Funções de lógica** — funções no lado do servidor acionadas por eventos do banco de dados, agendamentos cron ou rotas HTTP -* **Componentes de interface** — componentes React que são exibidos na UI do Twenty (páginas de registro, menu de comandos, painéis laterais) -* **Habilidades e agentes de IA** — estenda a IA do Twenty com recursos personalizados -* **Visualizações e navegação** — visualizações salvas preconfiguradas e links na barra lateral - -## Início rápido - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Isso cria a estrutura de um novo aplicativo, inicia opcionalmente um servidor Twenty local e começa a monitorar seus arquivos por alterações. Veja o guia [Primeiros passos](/l/pt/developers/extend/apps/getting-started) para o passo a passo completo. - -## Guias detalhados - -| Guia | Descrição | -| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [Primeiros passos](/l/pt/developers/extend/apps/getting-started) | Criar a estrutura de um aplicativo, configurar um servidor local, estrutura do projeto, CI | -| [Criando aplicativos](/l/pt/developers/extend/apps/building) | Definições de entidades (`defineObject`, `defineLogicFunction`, `defineFrontComponent`, etc.), clientes de API, pacotes npm, recursos públicos, testes | -| [Publicação](/l/pt/developers/extend/apps/publishing) | Implantar em um servidor, publicar no npm, marketplace | - -## Conceitos principais - -### Detecção de entidades - -O SDK detecta entidades ao examinar seus arquivos TypeScript em busca de chamadas `export default define({...})`. A nomenclatura de arquivos e a estrutura de pastas são flexíveis — a detecção é baseada em AST, não em caminhos. - -### Tipos de entidade disponíveis - -| Função | Finalidade | -| ---------------------------------- | -------------------------------------------------------- | -| `defineApplication()` | Metadados do aplicativo (obrigatório, um por aplicativo) | -| `defineObject()` | Objetos personalizados com campos | -| `defineField()` | Campos em objetos existentes | -| `defineLogicFunction()` | Lógica no lado do servidor com gatilhos | -| `defineFrontComponent()` | Componentes React na UI do Twenty | -| `defineRole()` | Papéis de permissão | -| `defineView()` | Configurações de visualizações salvas | -| `defineNavigationMenuItem()` | Links de navegação da barra lateral | -| `defineSkill()` | Habilidades de agente de IA | -| `defineAgent()` | Agentes de IA com prompts | -| `definePageLayout()` | Layouts personalizados de páginas de registro | -| `definePreInstallLogicFunction()` | Executa antes da instalação do aplicativo | -| `definePostInstallLogicFunction()` | Executa após a instalação do aplicativo | - -### Fluxo de trabalho de desenvolvimento - -1. **`yarn twenty dev`** — observa os arquivos de origem, recompila quando há alterações, sincroniza com o servidor e gera clientes de API tipados -2. **`yarn twenty build`** — produz um build distribuível -3. **`yarn twenty deploy`** — implanta em um servidor Twenty remoto -4. **`yarn twenty add`** — cria a estrutura de uma nova entidade de forma interativa - -### Referência da CLI - -```bash filename="Terminal" -yarn twenty help # List all commands -yarn twenty server start # Start local dev server -yarn twenty remote add # Connect to a Twenty server -yarn twenty exec -n fn # Execute a logic function -yarn twenty logs -n fn # Stream function logs -``` - -Veja o guia [Primeiros passos](/l/pt/developers/extend/apps/getting-started) para a referência completa da CLI. diff --git a/packages/twenty-docs/l/pt/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/pt/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4180b67161..0000000000 --- a/packages/twenty-docs/l/pt/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Features - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/building.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/building.mdx deleted file mode 100644 index c281f78085..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Arhitectură -description: Cum funcționează aplicațiile Twenty — izolarea (sandboxing), ciclul de viață și elementele de bază. -icon: sitemap ---- - -Aplicațiile Twenty sunt pachete TypeScript care vă extind spațiul de lucru cu obiecte personalizate, logică, componente UI și capabilități AI. Acestea rulează pe platforma Twenty, cu izolare completă (sandboxing) și controale de permisiuni. - -## Cum funcționează aplicațiile - -O aplicație este o colecție de **entități** declarate folosind funcțiile `defineEntity()` din pachetul `twenty-sdk`. SDK-ul detectează aceste declarații prin analiză AST în timpul construirii și produce un **manifest** — o descriere completă a ceea ce aplicația dvs. adaugă unui spațiu de lucru. - -``` -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 -``` - - - **Organizarea fișierelor ține de dvs.** Detectarea entităților este bazată pe AST — SDK-ul găsește apelurile `export default defineEntity(...)` indiferent unde se află fișierul. Structura de foldere de mai sus este o convenție, nu o cerință. - - -## Tipuri de entități - -| Entitate | Scop | Documentație | -| -------------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------- | -| **Aplicație** | Identitatea aplicației, permisiuni, variabile | [Model de date](/l/ro/developers/extend/apps/data-model) | -| **Rol** | Seturi de permisiuni pentru obiecte și câmpuri | [Model de date](/l/ro/developers/extend/apps/data-model) | -| **Obiect** | Tabele de date personalizate cu câmpuri | [Model de date](/l/ro/developers/extend/apps/data-model) | -| **Câmp** | Extindeți obiectele existente, definiți relații | [Model de date](/l/ro/developers/extend/apps/data-model) | -| **Funcție logică** | TypeScript pe partea de server cu declanșatoare | [Funcții logice](/l/ro/developers/extend/apps/logic-functions) | -| **Componentă front-end** | UI React izolat în pagina Twenty | [Componente front-end](/l/ro/developers/extend/apps/front-components) | -| **Abilitate** | Instrucțiuni reutilizabile pentru agenți AI | [Abilități și agenți](/l/ro/developers/extend/apps/skills-and-agents) | -| **Agent** | Agenți AI cu prompturi personalizate | [Abilități și agenți](/l/ro/developers/extend/apps/skills-and-agents) | -| **Vizualizare** | Vizualizări preconfigurate ale listelor de înregistrări | [Aspect](/l/ro/developers/extend/apps/layout) | -| **Element de meniu de navigare** | Intrări personalizate în bara laterală | [Aspect](/l/ro/developers/extend/apps/layout) | -| **Layout pagină** | File și widgeturi personalizate ale paginii de înregistrare | [Aspect](/l/ro/developers/extend/apps/layout) | - -## Izolare (sandboxing) - -* **Funcțiile logice** rulează în procese Node.js izolate pe server. Acestea accesează datele doar prin clientul API tipizat, limitat de permisiunile rolului aplicației. -* **Componentele front-end** rulează în Web Workers folosind Remote DOM — izolate de pagina principală, dar randând elemente DOM native (nu iframes). Acestea comunică cu Twenty printr-un API al gazdei bazat pe transmiterea de mesaje. -* **Permisiunile** sunt aplicate la nivelul API-ului. Tokenul de rulare (`TWENTY_APP_ACCESS_TOKEN`) este derivat din rolul definit în `defineApplication()`. - -## Ciclul de viață al aplicației - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ Install flow │ -│ upload → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -├─────────────────────────────────────────────────────────┤ -│ Publish │ -│ npm publish → appears in Twenty marketplace │ -└─────────────────────────────────────────────────────────┘ -``` - -* **`yarn twenty dev`** — monitorizează fișierele sursă și sincronizează în timp real modificările către un server Twenty conectat. Clientul API tipizat este regenerat automat atunci când schema se schimbă. -* **`yarn twenty build`** — compilează TypeScript, împachetează funcțiile logice și componentele front-end cu esbuild și produce un manifest. -* **Hook-uri pre/post-instalare** — funcții logice opționale care rulează în timpul instalării. Consultați [Funcții logice](/l/ro/developers/extend/apps/logic-functions) pentru detalii. - -## Pașii următori - - - - Definiți obiecte, câmpuri, roluri și relații. - - - Funcții pe partea de server cu declanșatoare HTTP, cron și de evenimente. - - - Componente React izolate în interfața Twenty. - - - Vizualizări, elemente de navigare și layout-uri ale paginilor de înregistrare. - - - Abilități și agenți AI cu prompturi personalizate. - - - Comenzi CLI, testare, resurse, remote-uri și CI. - - - Implementați pe un server sau publicați în marketplace. - - diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index 2b8958457a..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI & Testare -description: Comenzi CLI, configurare pentru testare, resurse publice, pachete npm, remote-uri și configurare CI. -icon: terminal ---- - -## Resurse publice (folderul `public/`) - -Folderul `public/` din rădăcina aplicației conține fișiere statice — imagini, pictograme, fonturi sau orice alte resurse de care are nevoie aplicația la rulare. Aceste fișiere sunt incluse automat în build-uri, sincronizate în timpul modului de dezvoltare și încărcate pe server. - -Fișierele plasate în `public/` sunt: - -* **Accesibile public** — odată sincronizate pe server, resursele sunt servite la un URL public. Nu este necesară autentificarea pentru a le accesa. -* **Disponibile în componentele frontend** — folosiți URL-urile resurselor pentru a afișa imagini, pictograme sau orice media în componentele React. -* **Disponibile în funcțiile logice** — referiți URL-urile resurselor în e-mailuri, răspunsuri API sau orice logică pe server. -* **Utilizate pentru metadatele marketplace-ului** — câmpurile `logoUrl` și `screenshots` din `defineApplication()` fac referire la fișiere din acest folder (de ex., `public/logo.png`). Acestea sunt afișate în marketplace când aplicația este publicată. -* **Sincronizate automat în modul de dezvoltare** — când adăugați, actualizați sau ștergeți un fișier în `public/`, acesta este sincronizat automat cu serverul. Nu este nevoie de repornire. -* **Incluse în build-uri** — `yarn twenty build` împachetează toate resursele publice în outputul de distribuție. - -### Accesarea resurselor publice cu `getPublicAssetUrl` - -Utilizați helperul `getPublicAssetUrl` din `twenty-sdk` pentru a obține URL-ul complet al unui fișier din directorul `public/`. Funcționează atât în funcții logice, cât și în componente frontend. - -**Într-o funcție logică:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, -}); -``` - -**Într-o componentă frontend:** - -```tsx src/front-components/company-card.tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -Argumentul `path` este relativ la folderul `public/` al aplicației. Atât `getPublicAssetUrl('logo.png')`, cât și `getPublicAssetUrl('public/logo.png')` se rezolvă la același URL — prefixul `public/` este eliminat automat dacă este prezent. - -## Utilizarea pachetelor npm - -Puteți instala și utiliza orice pachet npm în aplicația dvs. Atât funcțiile logice, cât și componentele frontend sunt împachetate cu [esbuild](https://esbuild.github.io/), care integrează toate dependențele în output — nu sunt necesare `node_modules` la rulare. - -### Instalarea unui pachet - -```bash filename="Terminal" -yarn add axios -``` - -Apoi importați-l în codul dvs.: - -```ts src/logic-functions/fetch-data.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import axios from 'axios'; - -const handler = async (): Promise => { - 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, -}); -``` - -Același lucru funcționează și pentru componentele frontend: - -```tsx src/front-components/chart.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { format } from 'date-fns'; - -const DateWidget = () => { - return

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### Cum funcționează împachetarea - -Pasul de build folosește esbuild pentru a produce un singur fișier autonom pentru fiecare funcție logică și pentru fiecare componentă frontend. Toate pachetele importate sunt integrate în bundle. - -**Funcțiile logice** rulează într-un mediu Node.js. Modulele built-in Node (`fs`, `path`, `crypto`, `http` etc.) sunt disponibile și nu trebuie instalate. - -**Componentele frontend** rulează într-un Web Worker. Modulele built-in Node nu sunt disponibile — doar API-urile de browser și pachetele npm care funcționează într-un mediu de browser. - -Ambele medii au `twenty-client-sdk/core` și `twenty-client-sdk/metadata` disponibile ca module pre-furnizate — acestea nu sunt incluse în bundle, ci sunt rezolvate la rulare de către server. - -## Testarea aplicației - -SDK-ul oferă API-uri programatice care vă permit să construiți, să distribuiți, să instalați și să dezinstalați aplicația din codul de test. Combinat cu [Vitest](https://vitest.dev/) și clienții API tipizați, puteți scrie teste de integrare care verifică faptul că aplicația funcționează cap-coadă împotriva unui server Twenty real. - -### Configurare - -Aplicația generată (scaffolded) include deja Vitest. Dacă o configurați manual, instalați dependențele: - -```bash filename="Terminal" -yarn add -D vitest vite-tsconfig-paths -``` - -Creați un `vitest.config.ts` în rădăcina aplicației: - -```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', - }, - }, -}); -``` - -Creați un fișier de configurare care verifică faptul că serverul este accesibil înainte de rularea testelor: - -```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), - ); -}); -``` - -### API-uri SDK programatice - -Subruta `twenty-sdk/cli` exportă funcții pe care le puteți apela direct din codul de test: - -| Funcție | Descriere | -| -------------- | --------------------------------------------------------- | -| `appBuild` | Construiți aplicația și, opțional, împachetați un tarball | -| `appDeploy` | Încărcați un tarball pe server | -| `appInstall` | Instalați aplicația în spațiul de lucru activ | -| `appUninstall` | Dezinstalați aplicația din spațiul de lucru activ | - -Fiecare funcție returnează un obiect rezultat cu `success: boolean` și fie `data`, fie `error`. - -### Scrierea unui test de integrare - -Iată un exemplu complet care construiește, distribuie și instalează aplicația, apoi verifică faptul că aceasta apare în spațiul de lucru: - -```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(); - }); -}); -``` - -### Rularea testelor - -Asigurați-vă că serverul Twenty local rulează, apoi: - -```bash filename="Terminal" -yarn test -``` - -Sau în modul watch în timpul dezvoltării: - -```bash filename="Terminal" -yarn test:watch -``` - -### Verificarea tipurilor - -Puteți rula și verificarea tipurilor pe aplicație fără a rula testele: - -```bash filename="Terminal" -yarn twenty typecheck -``` - -Aceasta rulează `tsc --noEmit` și raportează orice erori de tip. - -## Referință CLI - -Dincolo de `dev`, `build`, `add` și `typecheck`, CLI oferă comenzi pentru executarea funcțiilor, vizualizarea jurnalelor și gestionarea instalărilor de aplicații. - -### Executarea funcțiilor (`yarn twenty exec`) - -Rulați manual o funcție logică fără a o declanșa prin HTTP, cron sau eveniment de bază de date: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Vizualizarea jurnalelor funcțiilor (`yarn twenty logs`) - -Transmiteți în flux jurnalele de execuție pentru funcțiile logice ale aplicației: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -Acest lucru este diferit de `yarn twenty server logs`, care afișează jurnalele containerului Docker. `yarn twenty logs` afișează jurnalele de execuție ale funcțiilor aplicației de pe serverul Twenty. - - -### Dezinstalarea unei aplicații (`yarn twenty uninstall`) - -Eliminați aplicația din spațiul de lucru activ: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## Gestionarea remote-urilor - -Un „remote” este un server Twenty la care se conectează aplicația. În timpul configurării, Scaffolderul creează automat unul pentru dvs. Puteți adăuga mai multe remote-uri sau comuta între ele oricând. - -```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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Acreditările dvs. sunt stocate în `~/.twenty/config.json`. - -## CI cu GitHub Actions - -Scaffolderul generează un workflow GitHub Actions gata de utilizare în `.github/workflows/ci.yml`. Rulează automat testele de integrare la fiecare push pe `main` și la pull request-uri. - -Workflow-ul: - -1. Preia codul -2. Pornește un server Twenty temporar folosind acțiunea `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Instalează dependențele cu `yarn install --immutable` -4. Rulează `yarn test` cu `TWENTY_API_URL` și `TWENTY_API_KEY` injectate din rezultatele acțiunii - -```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 }} -``` - -Nu trebuie să configurați niciun secret — acțiunea `spawn-twenty-docker-image` pornește un server Twenty efemer direct în runner și oferă detaliile de conectare. Secretul `GITHUB_TOKEN` este furnizat automat de GitHub. - -Pentru a fixa o versiune Twenty specifică în loc de `latest`, modificați variabila de mediu `TWENTY_VERSION` din partea de sus a workflow-ului. diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/connections.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/connections.mdx deleted file mode 100644 index c991122cf8..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/connections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Conexiuni -description: Permite aplicației tale să acționeze în numele unui utilizator în servicii ale terților prin OAuth. -icon: plug ---- - -Conexiunile sunt acreditări pe care un utilizator le deține pentru un serviciu extern (Linear, GitHub, Slack, ...). Aplicația ta declară **cum** sunt obținute acele acreditări — un **furnizor de conexiune** — și le folosește în timpul execuției pentru a efectua apeluri autentificate către API-ul terț. - -În prezent este acceptat doar OAuth 2.0. Tipurile viitoare de acreditări (jetoane de acces personale, chei API, autentificare de bază) se vor integra în aceeași interfață — aplicațiile care deja folosesc `defineConnectionProvider({ type: 'oauth', ... })` nu vor trebui să migreze. - - - - - -Un furnizor de conexiune descrie handshake-ul OAuth de care are nevoie aplicația ta. Utilizatorul face clic pe "Adaugă conexiune" în setările aplicației tale, completează ecranul de consimțământ al furnizorului și este creată o înregistrare `ConnectedAccount` în spațiul său de lucru. - -O configurație funcțională are nevoie de **două fișiere** — furnizorul de conexiune și o declarație `serverVariables` corespunzătoare în `defineApplication` care conține acreditările clientului OAuth. - -```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.', - defaultRoleUniversalIdentifier: '...', - // 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, - }, - }, -}); -``` - -Puncte cheie: - -* `name` este șirul identificator unic folosit în `listConnections({ providerName })` (kebab-case, trebuie să corespundă `^[a-z][a-z0-9-]*$`). -* `displayName` apare în fila de setări a aplicației și în lista de instrumente AI. -* `clientIdVariable` / `clientSecretVariable` sunt **nume**, nu valori — trebuie să se potrivească cheilor declarate în `defineApplication.serverVariables`. Valorile reale `client_id` și `client_secret` sunt introduse de administratorul serverului prin interfața de înregistrare a aplicației și nu sunt niciodată comise în repo-ul tău. -* Folosește `serverVariables` (nu `applicationVariables`) — acreditările OAuth sunt la nivel de server și există o singură aplicație OAuth pentru fiecare server Twenty. -* Până când ambele `serverVariables` sunt completate, fila de setări a aplicației afișează un indiciu "necesită administrator de server" și butonul "Adaugă conexiune" este dezactivat. -* `type: 'oauth'` este singura valoare acceptată în prezent. Discriminatorul este compatibil cu versiuni viitoare: tipurile viitoare (`'pat'`, `'api-key'`, ...) vor adăuga blocuri noi de sub-configurație alături de `oauth`. - -URL-ul de callback OAuth pe care furnizorul tău trebuie să îl includă pe lista albă este: - -``` -https:///apps/oauth/callback -``` - - - - - -În interiorul unui handler de funcție logică, `listConnections({ providerName })` returnează înregistrările `ConnectedAccount` ale acestei aplicații pentru furnizorul dat, cu tokenuri de acces reîmprospătate. - -```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 }; -}; -``` - -Fiecare conexiune are: - -| Câmp | Descriere | -| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | ID unic al înregistrării; pasează-l la `getConnection(id)` pentru a reobține acea înregistrare | -| `visibility` | `'user'` (privată pentru un membru al spațiului de lucru) sau `'workspace'` (partajată cu toți membrii) | -| `scopes` | Permisiunile OAuth acordate de furnizorul upstream (distincte de `visibility` — nu au legătură) | -| `userWorkspaceId` | ID-ul userWorkspace al deținătorului — util pentru a alege "conexiunea utilizatorului care face cererea" în declanșatoarele de rută HTTP | -| `accessToken` | Token de acces OAuth proaspăt (reîmprospătat automat dacă a expirat) | -| `name` / `handle` | Numele afișat al conexiunii (derivat automat la callback-ul OAuth, poate fi redenumit de utilizator) | -| `authFailedAt` | Setat când cea mai recentă reîmprospătare a eșuat; utilizatorul trebuie să se reconecteze | - -Puncte cheie: - -* Pasează `{ providerName }` pentru a filtra după furnizor; omite-l pentru a obține toate conexiunile pe care această aplicație le deține la toți furnizorii. -* Serverul reîmprospătează transparent tokenul de acces înainte de a returna. Handlerul tău vede întotdeauna un token utilizabil (sau `authFailedAt` setat). -* `getConnection(id)` este echivalentul pentru o singură înregistrare. - - - - - -Când un utilizator face clic pe "Adaugă conexiune", i se solicită să aleagă o vizibilitate: - -* **Doar pentru mine** — acreditarea este privată pentru utilizatorul care se conectează. Orice funcție logică apelată în numele lor (declanșator de rută HTTP cu `isAuthRequired: true`) o vede; declanșatoarele cron și evenimentele din bază de date nu. -* **Partajată la nivel de spațiu de lucru** — orice membru al spațiului de lucru poate folosi acreditarea. Declanșatoarele cron / din bază de date o văd, de asemenea, deoarece nu au un utilizator al cererii. - -Folosește-o pe cea potrivită pentru fiecare handler: - -```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'); -``` - -Sunt permise mai multe conexiuni per (utilizator, furnizor), astfel încât același utilizator poate avea "Personal Linear" și "Work Linear" una lângă alta. - - - - - -Pentru fiecare furnizor de conexiune, administratorul serverului trebuie mai întâi să înregistreze o aplicație OAuth la serviciul terț. - -1. Mergi la setările pentru dezvoltatori ale furnizorului (de ex. https://linear.app/settings/api/applications/new). -2. Setează **Redirect URI** la `\/apps/oauth/callback`. -3. Copiază **Client ID** și **Client Secret** generate. -4. Deschide aplicația instalată în Twenty ca administrator de server → setează valorile pe `serverVariables` corespunzătoare. -5. Membrii spațiului de lucru pot apoi să adauge conexiuni din secțiunea **Conexiuni** a fiecărei aplicații. - - - - diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/data-model.mdx deleted file mode 100644 index 878997c094..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,493 +0,0 @@ ---- -title: Model de date -description: Definiți obiecte, câmpuri, roluri și metadatele aplicației cu SDK-ul Twenty. -icon: database ---- - -Pachetul `twenty-sdk` furnizează funcții `defineEntity` pentru a declara modelul de date al aplicației dvs. Trebuie să folosiți `export default defineEntity({...})` pentru ca SDK-ul să detecteze entitățile. Aceste funcții validează configurația în timpul build-ului și oferă completare automată în IDE și siguranța tipurilor. - - - **Organizarea fișierelor ține de dvs.** - Detectarea entităților este bazată pe AST — SDK-ul găsește apelurile `export default defineEntity(...)` indiferent unde se află fișierul. Gruparea fișierelor după tip (de exemplu, `logic-functions/`, `roles/`) este doar o convenție pentru organizarea codului, nu o cerință. - - - - - -Rolurile încapsulează permisiuni asupra obiectelor și acțiunilor din spațiul dvs. de lucru. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -Fiecare aplicație trebuie să aibă exact un apel `defineApplication` care descrie: - -* **Identitate**: identificatori, nume de afișare și descriere. -* **Permisiuni**: ce rol folosesc funcțiile și componentele front-end ale acesteia. -* **(Opțional) Variabile**: perechi cheie–valoare expuse funcțiilor ca variabile de mediu. -* **(Opțional) funcții de pre-instalare / post-instalare**: funcții logice care rulează înainte sau după instalare. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -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, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Notițe: -* Câmpurile `universalIdentifier` sunt ID-uri deterministe pe care le dețineți. Generați-le o singură dată și mențineți-le stabile între sincronizări. -* `applicationVariables` devin variabile de mediu pentru funcțiile și componentele front-end (de exemplu, `DEFAULT_RECIPIENT_NAME` este disponibil ca `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` trebuie să facă referire la un rol definit cu `defineRole()` (vezi mai sus). -* Funcțiile de pre-instalare și post-instalare sunt detectate automat în timpul construirii manifestului — nu trebuie să le referiți în `defineApplication()`. - -#### Metadate pentru marketplace - -Dacă intenționați să [publicați aplicația](/l/ro/developers/extend/apps/publishing), aceste câmpuri opționale controlează modul în care apare în marketplace: - -| Câmp | Descriere | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | -| `autor` | Numele autorului sau al companiei | -| `categorie` | Categoria aplicației pentru filtrarea în marketplace | -| `logoUrl` | Calea către logo-ul aplicației (de ex., `public/logo.png`) | -| `screenshots` | Array de căi către capturi de ecran (de ex., `public/screenshot-1.png`) | -| `aboutDescription` | Descriere markdown mai lungă pentru fila "About". Dacă este omis, marketplace-ul folosește `README.md` al pachetului de pe npm | -| `websiteUrl` | Link către site-ul dvs. | -| `termsUrl` | Link către termenii de serviciu | -| `emailSupport` | Adresă de e-mail pentru suport | -| `issueReportUrl` | Link către sistemul de urmărire a problemelor | - -#### Roluri și permisiuni - -Câmpul `defaultRoleUniversalIdentifier` din `application-config.ts` desemnează rolul implicit utilizat de funcțiile logice și componentele front-end ale aplicației. Consultați `defineRole` mai sus pentru detalii. - -* Tokenul de runtime injectat ca `TWENTY_APP_ACCESS_TOKEN` este derivat din acest rol. -* Clientul tipizat este restricționat la permisiunile acordate acelui rol. -* Respectați principiul celui mai mic privilegiu: creați un rol dedicat doar cu permisiunile de care au nevoie funcțiile. - -##### Rol implicit pentru funcții - -Când generați o aplicație nouă, CLI creează un fișier de rol implicit: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -`universalIdentifier` al acestui rol este apoi referențiat în `application-config.ts` ca `defaultRoleUniversalIdentifier`. - -* **\*.role.ts** definește ce poate face rolul. -* **application-config.ts** indică acel rol, astfel încât funcțiile moștenesc permisiunile lui. - -Notițe: -* Porniți de la rolul generat, apoi restrângeți-l progresiv urmând principiul celui mai mic privilegiu. -* Înlocuiți `objectPermissions` și `fieldPermissions` cu obiectele și câmpurile de care au nevoie efectiv funcțiile. -* `permissionFlags` controlează accesul la capabilități la nivelul platformei. Mențineți-le la minimum. -* Vedeți un exemplu funcțional: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Obiectele personalizate descriu atât schema, cât și comportamentul înregistrărilor din spațiul dvs. de lucru. Utilizați `defineObject()` pentru a defini obiecte cu validare încorporată: - -```ts postCard.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, - }, - ], -}); -``` - -Puncte cheie: - -* Folosiți `defineObject()` pentru validare încorporată și suport mai bun în IDE. -* `universalIdentifier` trebuie să fie unic și stabil între implementări. -* Fiecare câmp necesită un `name`, un `type`, un `label` și propriul `universalIdentifier` stabil. -* Matricea `fields` este opțională — puteți defini obiecte fără câmpuri personalizate. -* Puteți genera obiecte noi folosind `yarn twenty add`, care vă ghidează prin denumire, câmpuri și relații. - - -**Câmpurile de bază sunt create automat.** Când definiți un obiect personalizat, Twenty adaugă automat câmpuri standard -precum `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` și `deletedAt`. -Nu trebuie să le definiți în tabloul `fields` — adăugați doar câmpurile personalizate proprii. -Puteți suprascrie câmpurile implicite definind un câmp cu același nume în tabloul `fields`, -dar acest lucru nu este recomandat. - - - - - -Utilizați `defineField()` pentru a adăuga câmpuri la obiecte pe care nu le dețineți — cum ar fi obiectele standard Twenty (Person, Company etc.). sau obiecte din alte aplicații. Spre deosebire de câmpurile inline din `defineObject()`, câmpurile independente necesită un `objectUniversalIdentifier` pentru a specifica obiectul pe care îl extind: - -```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' }, - ], -}); -``` - -Puncte cheie: -* `objectUniversalIdentifier` identifică obiectul țintă. Pentru obiectele standard, utilizați `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` exportați din `twenty-sdk`. -* Atunci când definiți câmpuri inline în `defineObject()`, nu aveți nevoie de `objectUniversalIdentifier` — este moștenit de la obiectul părinte. -* `defineField()` este singura modalitate de a adăuga câmpuri la obiecte pe care nu le-ați creat cu `defineObject()`. - - - - -Relațiile conectează obiectele între ele. În Twenty, relațiile sunt întotdeauna bidirecționale — definiți ambele părți, iar fiecare parte o referențiază pe cealaltă. - -Există două tipuri de relații: - -| Tip relație | Descriere | Are cheie străină? | -| ------------- | ---------------------------------------------------------------------------------- | --------------------- | -| `MANY_TO_ONE` | Multe înregistrări ale acestui obiect indică către o singură înregistrare a țintei | Da (`joinColumnName`) | -| `ONE_TO_MANY` | O înregistrare a acestui obiect are multe înregistrări ale țintei | Nu (partea inversă) | - -#### Cum funcționează relațiile - -Fiecare relație necesită **două câmpuri** care se referențiază reciproc: - -1. Partea **MANY_TO_ONE** — se află pe obiectul care deține cheia străină -2. Partea **ONE_TO_MANY** — se află pe obiectul care deține colecția - -Ambele câmpuri folosesc `FieldType.RELATION` și se referențiază încrucișat prin `relationTargetFieldMetadataUniversalIdentifier`. - -#### Exemplu: Post Card are mulți destinatari - -Presupuneți că un `PostCard` poate fi trimis către multe înregistrări `PostCardRecipient`. Fiecare destinatar aparține exact unui Post Card. - -**Pasul 1: Definiți partea ONE_TO_MANY pe PostCard** (partea "one"): - -```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, - }, -}); -``` - -**Pasul 2: Definiți partea MANY_TO_ONE pe PostCardRecipient** (partea "many" — deține cheia străină): - -```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', - }, -}); -``` - - -**Importuri circulare:** Ambele câmpuri de relație se referă unul la celălalt prin `universalIdentifier`. Pentru a evita problemele de import circular, exportați ID-urile câmpurilor ca constante denumite din fiecare fișier și importați-le în celălalt fișier. Sistemul de build le rezolvă în timpul compilării. - - -#### Relaționarea cu obiectele standard - -Pentru a crea o relație cu un obiect Twenty încorporat (Person, Company etc.), utilizați `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', - }, -}); -``` - -#### Proprietăți ale câmpului de relație - -| Proprietate | Obligatoriu | Descriere | -| ------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------- | -| `tip` | Da | Trebuie să fie `FieldType.RELATION` | -| `relationTargetObjectMetadataUniversalIdentifier` | Da | `universalIdentifier` al obiectului țintă | -| `relationTargetFieldMetadataUniversalIdentifier` | Da | `universalIdentifier` al câmpului corespunzător de pe obiectul țintă | -| `universalSettings.relationType` | Da | `RelationType.MANY_TO_ONE` sau `RelationType.ONE_TO_MANY` | -| `universalSettings.onDelete` | Doar MANY_TO_ONE | Ce se întâmplă atunci când înregistrarea referențiată este ștearsă: `CASCADE`, `SET_NULL`, `RESTRICT` sau `NO_ACTION` | -| `universalSettings.joinColumnName` | Doar MANY_TO_ONE | Numele coloanei din baza de date pentru cheia străină (de ex., `postCardId`) | - -#### Câmpuri de relație inline în defineObject - -Puteți defini, de asemenea, câmpuri de relație direct în `defineObject()`. În acest caz, omiteți `objectUniversalIdentifier` — este moștenit de la obiectul părinte: - -```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 - ], -}); -``` - - - -## Generarea scheletului entităților cu `yarn twenty add` - -În loc să creați manual fișiere de entități, puteți folosi generatorul interactiv (scaffolder): - -```bash filename="Terminal" -yarn twenty add -``` - -Acesta vă solicită să alegeți un tip de entitate și vă ghidează prin câmpurile necesare. Generează un fișier gata de utilizare, cu un `universalIdentifier` stabil și apelul corect `defineEntity()`. - -Puteți de asemenea să transmiteți direct tipul de entitate pentru a sări peste primul prompt: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Tipuri de entități disponibile - -| Tipul entității | Comandă | Fișier generat | -| ---------------------------- | ------------------------------------ | ------------------------------------------------------- | -| Obiect | `yarn twenty add object` | `src/objects/\.ts` | -| Câmp | `yarn twenty add field` | `src/fields/\.ts` | -| Funcție logică | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Componentă frontend | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Rol | `yarn twenty add role` | `src/roles/\.ts` | -| Abilitate | `yarn twenty add skill` | `src/skills/\.ts` | -| Agent | `yarn twenty add agent` | `src/agents/\.ts` | -| Vizualizare | `yarn twenty add view` | `src/views/\.ts` | -| Element de meniu de navigare | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Machetă de pagină | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### Ce generează scaffolder-ul - -Fiecare tip de entitate are propriul său șablon. De exemplu, `yarn twenty add object` solicită: - -1. **Nume (singular)** — de ex., `invoice` -2. **Nume (plural)** — de ex., `invoices` -3. **Etichetă (singular)** — completată automat din nume (de ex., `Invoice`) -4. **Etichetă (plural)** — completată automat (de ex., `Invoices`) -5. **Creați o vizualizare și un element de navigare?** — dacă răspundeți afirmativ, scaffolder-ul generează, de asemenea, o vizualizare corespunzătoare și un link în bara laterală pentru noul obiect. - -Alte tipuri de entități au prompturi mai simple — majoritatea cer doar un nume. - -Tipul de entitate `field` este mai detaliat: solicită numele câmpului, eticheta, tipul (dintr-o listă cu toate tipurile de câmp disponibile precum `TEXT`, `NUMBER`, `SELECT`, `RELATION` etc.) și `universalIdentifier` al obiectului țintă. - -### Cale de output personalizată - -Utilizați opțiunea `--path` pentru a plasa fișierul generat într-o locație personalizată: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/front-components.mdx deleted file mode 100644 index 4a4eccd01d..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: Componente front-end -description: Construiți componente React care se afișează în interfața Twenty, cu izolare în sandbox. -icon: window-maximize ---- - -Componentele front-end sunt componente React care se afișează direct în interfața Twenty. Rulează într-un **Web Worker** izolat folosind Remote DOM — codul este izolat (sandboxed), dar se redă nativ în pagină, nu într-un iframe. - -## Unde pot fi utilizate componentele frontale - -Componentele frontale pot fi afișate în două locații în cadrul Twenty: - -* **Panou lateral** — Componentele frontale care nu sunt headless se deschid în panoul lateral din dreapta. Acesta este comportamentul implicit atunci când o componentă frontală este declanșată din meniul de comenzi. -* **Widgeturi (tablouri de bord și pagini de înregistrare)** — Componentele frontale pot fi încorporate ca widgeturi în machetele de pagină. La configurarea unui tablou de bord sau a machetei unei pagini de înregistrare, utilizatorii pot adăuga un widget de componentă frontală. - -## Exemplu de bază - -Cel mai rapid mod de a vedea o componentă front-end în acțiune este să o înregistrați ca **element din meniul de comenzi**. Folosiți `defineCommandMenuItem` într-un fișier separat pentru ca componenta să apară ca buton de acțiune rapidă în colțul din dreapta sus al paginii: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -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', -}); -``` - -După sincronizarea cu `yarn twenty dev` (sau prin rularea comenzii `yarn twenty dev --once` o singură dată), acțiunea rapidă apare în colțul din dreapta sus al paginii: - -
- Buton de acțiune rapidă în colțul din dreapta sus -
- -Faceți clic pe el pentru a afișa componenta inline. - -## Câmpuri de configurare - -| Câmp | Obligatoriu | Descriere | -| --------------------- | ----------- | --------------------------------------------------------------------------- | -| `universalIdentifier` | Da | ID unic stabil pentru această componentă | -| `component` | Da | O funcție de componentă React | -| `name` | Nu | Nume afișat | -| `description` | Nu | Descriere a ceea ce face componenta | -| `isHeadless` | Nu | Setați la `true` dacă componenta nu are interfață vizibilă (vedeți mai jos) | - -## Plasarea unei componente front-end pe o pagină - -Dincolo de comenzi, puteți încorpora o componentă front-end direct într-o pagină de înregistrare adăugând-o ca widget într-un **layout de pagină**. Consultați secțiunea [definePageLayout](/l/ro/developers/extend/apps/skills-and-agents#definepagelayout) pentru detalii. - -## Headless vs non-headless - -Componentele frontale au două moduri de randare controlate de opțiunea `isHeadless`: - -**Non-headless (implicit)** — Componenta afișează o interfață vizibilă. Când este declanșat din meniul de comenzi, se deschide în panoul lateral. Acesta este comportamentul implicit când `isHeadless` este `false` sau omis. - -**Headless (`isHeadless: true`)** — Componenta se montează invizibil în fundal. Nu deschide panoul lateral. Componentele headless sunt concepute pentru acțiuni care execută logică și apoi se demontează — de exemplu, rularea unei sarcini asincrone, navigarea la o pagină sau afișarea unui modal de confirmare. Se potrivesc în mod natural cu componentele Command din SDK descrise mai jos. - -```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, -}); -``` - -Deoarece componenta returnează `null`, Twenty omite redarea unui container pentru ea — nu apare spațiu gol în layout. Componenta are în continuare acces la toate hook-urile și la API-ul de comunicare cu gazda. - -## Componentele Command din SDK - -Pachetul `twenty-sdk` oferă patru componente ajutătoare Command, concepute pentru componente front-end headless. Fiecare componentă execută o acțiune la montare, gestionează erorile afișând o notificare snackbar și demontează automat componenta de interfață la final. - -Importă-le din `twenty-sdk/command`: - -* **`Command`** — Rulează un callback asincron prin prop-ul `execute`. -* **`CommandLink`** — Navighează către o rută a aplicației. Props: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — Deschide un modal de confirmare. Dacă utilizatorul confirmă, execută callback-ul `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — Deschide o anumită pagină din panoul lateral. Props: `page`, `pageTitle`, `pageIcon`. - -Iată un exemplu complet de componentă front-end headless care folosește `Command` pentru a rula o acțiune din meniul de comenzi: - -```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 ; -}; - -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', -}); -``` - -Și un exemplu care folosește `CommandModal` pentru a cere confirmarea înainte de execuție: - -```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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, -}); -``` - -## Accesarea contextului de rulare - -În interiorul componentei, folosiți hook-urile SDK pentru a accesa utilizatorul curent, înregistrarea curentă și instanța componentei: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Hook-uri disponibile: - -| Hook | Returnează | Descriere | -| --------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------- | -| `useUserId()` | `string` sau `null` | ID-ul utilizatorului curent | -| `useSelectedRecordIds()` | `string[]` | Toate ID-urile înregistrărilor selectate (array gol dacă nu este selectată niciuna) | -| `useRecordId()` | `string` sau `null` | **Învechit.** Folosiți `useSelectedRecordIds()` în schimb | -| `useFrontComponentId()` | `string` | ID-ul acestei instanțe de componentă | -| `useFrontComponentExecutionContext(selector)` | variază | Accesați întregul context de execuție cu o funcție selector | - -## API-ul de comunicare cu gazda - -Componentele front-end pot declanșa navigare, ferestre modale și notificări folosind funcții din `twenty-sdk`: - -| Funcție | Descriere | -| ----------------------------------------------- | ----------------------------------- | -| `navigate(to, params?, queryParams?, options?)` | Navigați la o pagină din aplicație | -| `openSidePanelPage(params)` | Deschideți un panou lateral | -| `closeSidePanel()` | Închideți panoul lateral | -| `openCommandConfirmationModal(params)` | Afișați un dialog de confirmare | -| `enqueueSnackbar(params)` | Afișați o notificare tip toast | -| `unmountFrontComponent()` | Demontați componenta | -| `updateProgress(progress)` | Actualizați un indicator de progres | - -Iată un exemplu care folosește API-ul gazdă pentru a afișa un snackbar și a închide panoul lateral după finalizarea unei acțiuni: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -### Lucrul cu mai multe înregistrări - -Folosiți `useSelectedRecordIds()` pentru a gestiona mai multe înregistrări selectate. Acest lucru este util pentru operațiuni în masă: - -```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 ( -
-

Export {selectedRecordIds.length} selected record(s)?

- -
- ); -}; - -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, - }, -}); -``` - -## defineCommandMenuItem - -Folosiți `defineCommandMenuItem` pentru a înregistra o componentă front-end în meniul de comenzi (Cmd+K). Dacă `isPinned` este `true`, apare și ca buton de acțiune rapidă în colțul din dreapta sus al paginii. - -```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', -}); -``` - -| Câmp | Obligatoriu | Descriere | -| --------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `universalIdentifier` | Da | ID unic stabil pentru comandă | -| `label` | Da | Etichetă completă afișată în meniul de comenzi (Cmd+K) | -| `frontComponentUniversalIdentifier` | Da | `universalIdentifier` al componentei front-end pe care această comandă o deschide | -| `shortLabel` | Nu | Etichetă mai scurtă afișată pe butonul de acțiune rapidă fixat | -| `icon` | Nu | Numele pictogramei afișat lângă etichetă (de ex. `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Nu | Când este `true`, afișează comanda ca buton de acțiune rapidă în colțul din dreapta sus al paginii | -| `availabilityType` | Nu | Controlează unde apare comanda: `'GLOBAL'` (mereu disponibilă), `'RECORD_SELECTION'` (doar când sunt selectate înregistrări) sau `'FALLBACK'` (afișată când nicio altă comandă nu se potrivește) | -| `availabilityObjectUniversalIdentifier` | Nu | Restricționați comanda la paginile unui anumit tip de obiect (de ex., doar pe înregistrările Company) | -| `conditionalAvailabilityExpression` | Nu | O expresie booleană pentru a controla dinamic dacă comanda este vizibilă (vezi mai jos) | - -## Expresii de disponibilitate condițională - -Câmpul `conditionalAvailabilityExpression` vă permite să controlați când este vizibilă o comandă în funcție de contextul paginii curente. Importați variabile tipizate și operatori din `twenty-sdk` pentru a construi expresii: - -```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, - ), -}); -``` - -**Variabile de context** — acestea reprezintă starea curentă a paginii: - -| Variabilă | Tip | Descriere | -| ------------------------------ | --------- | ---------------------------------------------------------------------- | -| `pageType` | `string` | Tipul paginii curente (de ex. `'RecordIndexPage'`, `'RecordShowPage'`) | -| `isInSidePanel` | `boolean` | Dacă componenta este redată într-un panou lateral | -| `numberOfSelectedRecords` | `number` | Numărul de înregistrări selectate în prezent | -| `isSelectAll` | `boolean` | Dacă "select all" este activ | -| `selectedRecords` | `array` | Obiectele înregistrărilor selectate | -| `favoriteRecordIds` | `array` | ID-urile înregistrărilor marcate ca favorite | -| `objectPermissions` | `object` | Permisiuni pentru tipul de obiect curent | -| `targetObjectReadPermissions` | `object` | Permisiuni de citire pentru obiectul țintă | -| `targetObjectWritePermissions` | `object` | Permisiuni de scriere pentru obiectul țintă | -| `featureFlags` | `object` | Steaguri de caracteristici active | -| `objectMetadataItem` | `object` | Metadatele tipului de obiect curent | -| `hasAnySoftDeleteFilterOnView` | `boolean` | Dacă vizualizarea curentă are un filtru soft-delete | - -**Operatori** — combinați variabilele în expresii booleene: - -| Operator | Descriere | -| ----------------------------------- | ----------------------------------------------------------------------- | -| `isDefined(value)` | `true` dacă valoarea nu este null/undefined | -| `isNonEmptyString(value)` | `true` dacă valoarea este un șir nevid | -| `includes(array, value)` | `true` dacă array-ul conține valoarea | -| `includesEvery(array, prop, value)` | `true` dacă proprietatea fiecărui element include valoarea | -| `every(array, prop)` | `true` dacă proprietatea este truthy pentru fiecare element | -| `everyDefined(array, prop)` | `true` dacă proprietatea este definită pentru fiecare element | -| `everyEquals(array, prop, value)` | `true` dacă proprietatea este egală cu valoarea pentru fiecare element | -| `some(array, prop)` | `true` dacă proprietatea este truthy pe cel puțin un element | -| `someDefined(array, prop)` | `true` dacă proprietatea este definită pe cel puțin un element | -| `someEquals(array, prop, value)` | `true` dacă proprietatea este egală cu valoarea pe cel puțin un element | -| `someNonEmptyString(array, prop)` | `true` dacă proprietatea este un șir nevid pe cel puțin un element | -| `none(array, prop)` | `true` dacă proprietatea este falsy pentru fiecare element | -| `noneDefined(array, prop)` | `true` dacă proprietatea este nedefinită pentru fiecare element | -| `noneEquals(array, prop, value)` | `true` dacă proprietatea nu este egală cu valoarea pe niciun element | - -## Resurse publice - -Componentele front-end pot accesa fișiere din directorul `public/` al aplicației folosind `getPublicAssetUrl`: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -Consultați [secțiunea despre resurse publice](/l/ro/developers/extend/apps/cli-and-testing#public-assets-public-folder) pentru detalii. - -## Stilizare - -Componentele front-end acceptă mai multe abordări de stilizare. Puteți folosi: - -* **Stiluri inline** — `style={{ color: 'red' }}` -* **Componente Twenty UI** — import din `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar și altele) -* **Emotion** — CSS-in-JS cu `@emotion/react` -* **Styled-components** — pattern-uri `styled.div` -* **Tailwind CSS** — clase utilitare -* **Orice bibliotecă CSS-in-JS** compatibilă cu React - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/getting-started.mdx deleted file mode 100644 index cae329e476..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Începeți -icon: rocket -description: Creați prima dvs. aplicație Twenty în câteva minute. ---- - -## Cerințe - -* **Node.js 24+** — [Descărcați](https://nodejs.org/) -* **Yarn 4** — vine împreună cu Node prin Corepack. Activați-l: `corepack enable` -* **Docker** — [Descărcați](https://www.docker.com/products/docker-desktop/). Necesar pentru a rula un server Twenty local. Omiteți dacă rulați deja Twenty în altă parte. - -Crearea unei aplicații Twenty are trei faze. Generatorul de schelet le reduce la o singură comandă pe happy path, dar fiecare fază este un concept separat — când ceva eșuează, dacă știți în ce fază sunteți, știți ce trebuie să corectați. - -| Fază | Ce faceți | Instrument | Rezultat | -| ----------------------- | ------------------------------------------------ | ----------------------------- | ------------------------------ | -| **1. Creați scheletul** | Generați codul sursă al aplicației | `npx create-twenty-app` | Un proiect TypeScript pe disc | -| **2. Rulați un server** | Porniți un server Twenty cu care să sincronizați | Docker + `yarn twenty server` | O instanță Twenty care rulează | -| **3. Sincronizați** | Sincronizați în timp real codul cu serverul | `yarn twenty dev` | Modificările apar în UI | - ---- - -## Faza 1 — Creați scheletul proiectului - -Creați o nouă aplicație din șablon: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Vi se va cere un nume și o descriere — apăsați **Enter** pentru valorile implicite. Aceasta generează un proiect TypeScript în `my-twenty-app/` cu un fișier inițial `application-config.ts`, un rol implicit, un flux de lucru CI și un test de integrare. - -**După această fază:** aveți codul sursă al aplicației pe mașina dvs. Încă nu rulează — aceasta este Faza 2. - ---- - -## Faza 2 — Rulați un server Twenty local - -Aplicația are nevoie de un server Twenty cu care să se sincronizeze. Serverul este o instanță Twenty completă — UI, API GraphQL, PostgreSQL — care rulează local în Docker. Codul local încarcă definițiile pe acel server, făcându-le să apară în UI. - -Generatorul de schelet vă propune să pornească unul pentru dvs.: - -> **Doriți să configurați o instanță Twenty locală?** - -* **Yes (recomandat)** — descarcă imaginea Docker `twentycrm/twenty-app-dev` și o pornește pe portul `2020`. Asigurați-vă mai întâi că Docker rulează. -* **No** — alegeți această opțiune dacă aveți deja un server Twenty la care doriți să vă conectați. Îl puteți conecta ulterior cu `yarn twenty remote add`. - -
- Porniți instanța locală? -
- -După ce serverul pornește, se deschide un browser pentru autentificare. Folosiți contul demo preconfigurat: - -* **E-mail:** `tim@apple.dev` -* **Parolă:** `tim@apple.dev` - -
- Ecranul de autentificare Twenty -
- -Faceți clic pe **Authorize** pe ecranul următor — aceasta oferă CLI-ului acces la spațiul dvs. de lucru. - -
- Ecranul de autorizare Twenty CLI -
- -Terminalul va confirma că totul este configurat. - -
- Aplicația a fost creată cu succes -
- -**După această fază:** aveți un server Twenty care rulează la [http://localhost:2020](http://localhost:2020), iar CLI-ul dvs. este autorizat să sincronizeze cu acesta. - - -Dacă Docker nu este instalat sau nu rulează, generatorul de schelet vă va indica comanda corectă de pornire pentru sistemul dvs. de operare. După ce Docker rulează, puteți continua cu `yarn twenty server start` — nu este nevoie să recreați scheletul. - - ---- - -## Faza 3 — Sincronizați modificările - -Aceasta este bucla internă în care veți petrece cea mai mare parte a timpului. - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Aceasta monitorizează `src/`, reconstruiește la fiecare modificare și sincronizează rezultatul pe server. Editați un fișier, salvați, iar în decurs de o secundă serverul reflectă modificarea. Veți vedea în terminal un panou de stare în timp real. - -Pentru un output mai detaliat (jurnale de build, cereri de sincronizare, urme ale erorilor), adăugați `--verbose`. - -
- Ieșirea terminalului în modul de dezvoltare -
- -Deschideți [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Ar trebui să vedeți aplicația dvs. listată la **Your Apps**. - -
- Lista Your Apps care afișează My twenty app -
- -Faceți clic pe **My twenty app** pentru a vedea **înregistrarea aplicației** — o înregistrare la nivel de server care descrie aplicația dvs. (nume, identificator, credențiale OAuth, sursă). O singură înregistrare poate fi instalată în mai multe spații de lucru pe același server. - -
- Detalii despre înregistrarea aplicației -
- -Faceți clic pe **View installed app** pentru a vedea instalarea în spațiul de lucru. Fila **About** afișează versiunea și opțiunile de administrare. - -
- Aplicație instalată -
- -**După această fază:** aveți o buclă de dezvoltare în timp real. Editați orice fișier în `src/` și acesta apare în UI. - -### Sincronizare unică pentru CI și scripturi - -Adăugați `--once` pentru a rula un singur build + sync și a ieși — același flux, fără watcher: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Comandă | Comportament | Când se folosește | -| ------------------------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------------- | -| `yarn twenty dev` | Monitorizează și resincronizează la fiecare modificare. Rulează până când îl opriți. | Dezvoltare locală interactivă. | -| `yarn twenty dev --once` | Un singur build + sync, iese cu `0` la succes, `1` la eșec. | CI, hook-uri pre-commit, agenți AI, fluxuri de lucru scriptate. | - -Ambele moduri necesită un server în modul de dezvoltare și un remote autentificat. - - -Modul de dezvoltare este disponibil doar pe instanțele Twenty care rulează în modul development (`NODE_ENV=development`). Instanțele de producție resping cererile de sincronizare din modul de dezvoltare — folosiți `yarn twenty deploy` pentru a implementa pe serverele de producție. Consultați [Publicarea aplicațiilor](/l/ro/developers/extend/apps/publishing). - - ---- - -## Ce puteți construi - -Aplicațiile sunt compuse din **entități** — fiecare definită într-un fișier TypeScript cu un singur `export default`: - -| Entitate | Ce face | -| --------------------------- | ----------------------------------------------------------------------------------------------- | -| **Obiecte și câmpuri** | Modele de date personalizate (carte poștală, factură etc.) cu câmpuri tipizate | -| **Funcții logice** | TypeScript pe server declanșat de rute HTTP, programări cron sau evenimente din baza de date | -| **Componente front-end** | Componente React care se afișează în UI-ul Twenty (panou lateral, widgeturi, meniul de comenzi) | -| **Abilități și agenți** | Capabilități AI — instrucțiuni reutilizabile și asistenți autonomi | -| **Vizualizări și navigare** | Vizualizări de listă preconfigurate și elemente de meniu în bara laterală | -| **Layouturi de pagină** | Pagini personalizate de detalii ale înregistrărilor cu file și widgeturi | - -Referință completă: [Crearea aplicațiilor](/l/ro/developers/extend/apps/building). - -## Structura proiectului - -```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 -``` - -| Fișier / Folder | Scop | -| ---------------------------------------- | -------------------------------------------------------------------- | -| `src/application-config.ts` | **Necesar.** Fișierul principal de configurare pentru aplicație. | -| `src/default-role.ts` | Rol implicit care controlează la ce pot avea acces funcțiile logice. | -| `src/constants/universal-identifiers.ts` | UUID-uri generate automat și metadate (nume afișat, descriere). | -| `src/__tests__/` | Teste de integrare (configurare + test exemplu). | -| `public/` | Resurse statice (imagini, fonturi) servite împreună cu aplicația. | - -### Pornind de la un exemplu - -Folosiți `--example` pentru a începe cu un proiect mai complet (obiecte personalizate, câmpuri, funcții logice, componente front-end): - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Exemplele se află în [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples). Puteți, de asemenea, să creați scheletul entităților individuale într-un proiect existent cu `yarn twenty add` — vedeți [Crearea aplicațiilor](/l/ro/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add). - ---- - -## Gestionarea serverului local - -Folosiți `yarn twenty server` pentru a controla containerul Twenty local: - -| Comandă | Ce face | -| -------------------------------------- | ------------------------------------------------------------ | -| `yarn twenty server start` | Pornește serverul (descarcă imaginea dacă este necesar) | -| `yarn twenty server start --port 3030` | Pornește pe un port personalizat | -| `yarn twenty server stop` | Oprește serverul (păstrează datele) | -| `yarn twenty server status` | Afișează URL-ul, versiunea și credențialele de autentificare | -| `yarn twenty server logs` | Transmite în flux jurnalele serverului | -| `yarn twenty server reset` | Șterge datele și pornește de la zero | -| `yarn twenty server upgrade` | Descarcă cea mai recentă imagine `twenty-app-dev` | -| `yarn twenty server upgrade 2.2.0` | Actualizează la o versiune specifică | - -Datele persistă între reporniri în două volume Docker (`twenty-app-dev-data` pentru PostgreSQL, `twenty-app-dev-storage` pentru fișiere). Folosiți `reset` pentru a șterge totul. - -### Actualizarea imaginii serverului - -`yarn twenty server upgrade` descarcă cea mai recentă imagine, compară digest-urile și recreează containerul doar dacă s-a schimbat ceva. Volumele de date sunt păstrate — doar containerul este înlocuit. Dacă a fost descărcată o imagine nouă și containerul rula, actualizarea pornește automat un container nou; rulați apoi `yarn twenty server start` pentru a aștepta până când devine funcțional. - -```bash filename="Terminal" -yarn twenty server upgrade # Latest -yarn twenty server upgrade 2.2.0 # Specific version -``` - -Puteți verifica versiunea care rulează cu `yarn twenty server status` (aceasta afișează `APP_VERSION` încorporat în container). - -### Rularea unei instanțe de test în paralel - -Adăugați `--test` la orice comandă `server` pentru a gestiona o a doua instanță, complet izolată — utilă pentru teste de integrare sau pentru a experimenta fără a atinge datele principale de dezvoltare: - -| Comandă | Ce face | -| ----------------------------------- | --------------------------------------------------- | -| `yarn twenty server start --test` | Pornește instanța de test (implicit pe portul 2021) | -| `yarn twenty server stop --test` | Opriți-o | -| `yarn twenty server status --test` | Afișați-i starea | -| `yarn twenty server logs --test` | Transmiteți în flux jurnalele sale | -| `yarn twenty server reset --test` | Ștergeți-i datele | -| `yarn twenty server upgrade --test` | Actualizați-i imaginea | - -Instanța de test are propriul container (`twenty-app-dev-test`), propriile volume (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) și propria configurație — rulează alături de instanța principală, fără conflicte. Combinați `--test` cu `--port` pentru a înlocui portul 2021. - ---- - -## Configurare manuală (fără generator) - -Săriți peste generatorul de schelet dacă adăugați SDK-ul într-un proiect existent: - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -Adăugați scriptul în `package.json`: - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -Acum puteți rula `yarn twenty dev`, `yarn twenty server start` și restul. - - -Nu instalați `twenty-sdk` global — fixați-l per proiect astfel încât fiecare aplicație să folosească propria versiune. - - ---- - -## Depanare - -* **Erori Docker** — Asigurați-vă că Docker Desktop (sau daemonul) rulează înainte de `yarn twenty server start`. Mesajul de eroare va afișa comanda corectă de pornire pentru sistemul dvs. de operare. -* **Versiune Node greșită** — Aveți nevoie de 24+. Verificați cu `node -v`. -* **Lipsește Yarn 4** — Rulați `corepack enable`. -* **Dependențe nefuncționale** — `rm -rf node_modules && yarn install`. - -Blocat? Întrebați pe [Discordul Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/layout.mdx deleted file mode 100644 index 90297259e0..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Aspect -description: Definiți vizualizări, elemente de meniu de navigare și layouturi de pagină pentru a stabili modul în care aplicația dvs. se afișează în Twenty. -icon: table-columns ---- - -Entitățile de layout controlează modul în care aplicația dvs. apare în UI-ul Twenty — ce se află în bara laterală, care vizualizări salvate vin împreună cu aplicația și cum este aranjată pagina de detalii a unei înregistrări. - -## Concepte de layout - -| Concept | Ce controlează | Entitate | -| -------------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------- | -| **Vizualizare** | O configurație salvată a unei liste pentru un obiect — câmpuri vizibile, ordine, filtre, grupuri | `defineView` | -| **Element de meniu de navigare** | Un element în bara laterală stângă care face legătura către o vizualizare sau un URL extern | `defineNavigationMenuItem` | -| **Layout pagină** | Filele și widgeturile care alcătuiesc pagina de detalii a unei înregistrări | `definePageLayout` | -| **Filă layout pagină** | O filă independentă atașată unui layout pagină existent (standard sau al propriei tale aplicații) | `definePageLayoutTab` | - -Vizualizările, elementele de navigare și layouturile de pagină fac referire unele la altele prin `universalIdentifier`: - -* Un **element de meniu de navigare** de tip `VIEW` indică către un identificator `defineView`, astfel încât linkul din bara laterală deschide acea vizualizare salvată. -* Un **layout de pagină** de tip `RECORD_PAGE` vizează un obiect și poate încorpora [componente frontale](/l/ro/developers/extend/apps/front-components) în filele sale ca widgeturi. - - - - -Vizualizările sunt configurații salvate despre cum sunt afișate înregistrările unui obiect — inclusiv ce câmpuri sunt vizibile, ordinea lor și orice filtre sau grupuri aplicate. Utilizați `defineView()` pentru a livra vizualizări preconfigurate împreună cu aplicația: - -```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, - }, - ], -}); -``` - -Puncte cheie: -* `objectUniversalIdentifier` specifică la ce obiect se aplică această vizualizare. -* `key` determină tipul vizualizării (de ex., `ViewKey.INDEX` pentru vizualizarea principală de listă). -* `fields` controlează ce coloane apar și ordinea acestora. Fiecare câmp face referire la un `fieldMetadataUniversalIdentifier`. -* Puteți defini, de asemenea, `filters`, `filterGroups`, `groups` și `fieldGroups` pentru configurații mai avansate. -* `position` controlează ordonarea atunci când există mai multe vizualizări pentru același obiect. - - - - -Elementele de meniu de navigare adaugă intrări personalizate în bara laterală a spațiului de lucru. Utilizați `defineNavigationMenuItem()` pentru a crea linkuri către vizualizări, URL-uri externe sau obiecte: - -```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, -}); -``` - -Puncte cheie: -* `type` determină la ce face trimitere elementul de meniu: `NavigationMenuItemType.VIEW` pentru o vizualizare salvată sau `NavigationMenuItemType.LINK` pentru un URL extern. -* Pentru link-uri către vizualizări, setați `viewUniversalIdentifier`. Pentru link-uri externe, setați `link`. -* `position` controlează ordonarea în bara laterală. -* `icon` și `color` (opțional) personalizează aspectul. - - - - -Machetele de pagină vă permit să personalizați aspectul unei pagini de detalii a unei înregistrări — ce file apar, ce widgeturi sunt în fiecare filă și cum sunt aranjate. Utilizați `definePageLayout()` pentru a livra machete personalizate împreună cu aplicația: - -```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, - }, - }, - ], - }, - ], -}); -``` - -Puncte cheie: -* `type` este de obicei `'RECORD_PAGE'` pentru a personaliza vizualizarea de detaliu a unui obiect specific. -* `objectUniversalIdentifier` specifică la ce obiect se aplică această machetă. -* Fiecare `tab` definește o secțiune a paginii cu un `title`, `position` și `layoutMode` (`CANVAS` pentru layout liber). -* Fiecare `widget` dintr-o filă poate reda o componentă frontend, o listă de relații sau alte tipuri de widgeturi integrate. -* `position` pe file le controlează ordinea. Folosiți valori mai mari (de ex., 50) pentru a plasa filele personalizate după cele integrate. - - - - -`definePageLayoutTab` permite aplicației tale să atașeze o singură filă — cu widgeturi opționale — la un layout de pagină **existent**. Cel mai comun caz de utilizare este adăugarea unei file personalizate (de exemplu, o filă de analize sau o filă cu rezumat AI) la una dintre paginile de înregistrare predefinite ale Twenty sau la un layout de pagină pe care propria ta aplicație îl livrează deja. - -Layoutul de pagină țintă trebuie să fie fie un layout de pagină Twenty **standard**, fie unul definit de **propria ta aplicație**; referințele între aplicații la layouturi de pagină deținute de o altă aplicație instalată nu sunt acceptate în prezent. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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, - }, - }, - ], -}); -``` - -Puncte cheie: -* `pageLayoutUniversalIdentifier` este **necesar** când folosești `definePageLayoutTab` și trebuie să indice către un layout de pagină care există deja la momentul instalării (standard sau al aplicației tale). Când lipsește layoutul de pagină părinte, instalarea eșuează cu o eroare clară de validare. -* `widgets` sunt limitate doar la această filă — fac referire la componente front-end, vizualizări etc., exact ca widgeturile definite inline în `definePageLayout`. -* `position` controlează ordonarea în raport cu filele existente din layoutul țintă. Alege o valoare care să plaseze fila ta acolo unde dorești, relativ la filele predefinite. -* Folosește aceasta în loc de `definePageLayout` atunci când vrei doar să **adaugi** la un layout existent. Folosește `definePageLayout` când deții întregul layout (de obicei un `RECORD_PAGE` pentru un obiect pe care îl livrezi în aplicația ta sau un `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index cc808580df..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,566 +0,0 @@ ---- -title: Funcții logice -description: Definește funcții TypeScript pe partea de server cu declanșatoare HTTP, cron și de evenimente din baza de date. -icon: bolt ---- - -Funcțiile de logică sunt funcții TypeScript pe partea de server care rulează pe platforma Twenty. Acestea pot fi declanșate de solicitări HTTP, programări cron sau evenimente din baza de date — și pot fi, de asemenea, expuse ca instrumente pentru agenți AI. - - - - -Fiecare fișier de funcție folosește `defineLogicFunction()` pentru a exporta o configurație cu un handler și declanșatoare opționale. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Tipuri de declanșatoare disponibile: -* **httpRoute**: Expune funcția pe o cale și metodă HTTP **sub endpoint-ul `/s/`**: -> de ex. `path: '/post-card/create'` este apelabil la `https://your-twenty-server.com/s/post-card/create` -* **cron**: Rulează funcția pe un program folosind o expresie CRON. -* **databaseEvent**: Rulează la evenimentele ciclului de viață ale obiectelor din spațiul de lucru. Când operațiunea evenimentului este `updated`, câmpurile specifice de urmărit pot fi specificate în array-ul `updatedFields`. Dacă este lăsat nedefinit sau gol, orice actualizare va declanșa funcția. -> de ex. `person.updated`, `*.created`, `company.*` - - -Puteți, de asemenea, să executați manual o funcție folosind CLI: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Puteți urmări jurnalele cu: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Payload-ul declanșatorului de rută - -Când un declanșator de rută invocă funcția logică, aceasta primește un obiect `RoutePayload` care urmează -[AWS HTTP API v2 format](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). -Importați tipul `RoutePayload` din `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -Tipul `RoutePayload` are următoarea structură: - - | Proprietate | Tip | Descriere | Exemplu | - | ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | Anteturi HTTP (doar cele listate în `forwardedRequestHeaders`) | consultați secțiunea de mai jos | - | `queryStringParameters` | `Record\` | Parametri query string (valorile multiple unite cu virgule) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Parametri de cale extrași din modelul rutei | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Corpul cererii analizat (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Corpul original al cererii în UTF-8, înainte de parsarea JSON. Util pentru verificarea semnăturilor de tip HMAC pentru webhook-uri (de exemplu, `X-Hub-Signature-256` de la GitHub, Stripe). `undefined` atunci când mediul de execuție nu a păstrat-o. | | - | `isBase64Encoded` | `boolean` | Indică dacă corpul este codificat în base64 | | - | `requestContext.http.method` | `string` | Metoda HTTP (GET, POST, PUT, PATCH, DELETE) | | - | `requestContext.http.path` | `string` | Calea brută a cererii | | - - -#### forwardedRequestHeaders - -În mod implicit, anteturile HTTP din cererile de intrare **nu** sunt transmise funcției dvs. de logică din motive de securitate. -Pentru a accesa anumite anteturi, listați-le explicit în array-ul `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'], - }, -}); -``` - -În handler, accesați anteturile transmise mai departe astfel: - -```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 }; -}; -``` - - -Numele anteturilor sunt normalizate la litere mici. Accesați-le folosind chei cu litere mici (de exemplu, `event.headers['content-type']`). - - -#### Expunerea unei funcții ca instrument AI sau ca acțiune în fluxul de lucru - -Funcțiile logice pot fi expuse în două locuri, fiecare cu propriul declanșator: - -* **`toolTriggerSettings`** — face funcția descoperibilă de către funcționalitățile AI ale Twenty (chat, MCP, apelarea de funcții). Folosește JSON Schema standard, formatul pe care LLM-urile îl înțeleg nativ. -* **`workflowActionTriggerSettings`** — determină ca funcția să apară ca un pas în constructorul vizual de fluxuri de lucru. Folosește `InputSchema` bogat al Twenty, astfel încât constructorul să poată afișa editori de câmp adecvați, selectoare de variabile și etichete. - -O funcție poate opta pentru una, cealaltă sau ambele. Acestea stau alături de `cronTriggerSettings`, `databaseEventTriggerSettings` și `httpRouteTriggerSettings` — același tipar, aceeași formă. - -```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: {}, -}); -``` - -Puncte cheie: - -* O funcție poate combina suprafețele — declară atât `toolTriggerSettings`, cât și `workflowActionTriggerSettings` pentru a o expune atât în chat, cât și în constructorul de fluxuri de lucru. -* `toolTriggerSettings.inputSchema` și `workflowActionTriggerSettings.inputSchema` sunt ambele opționale. Când sunt omise, generatorul de manifest le deduce din codul sursă al handlerului (JSON Schema pentru instrumentul AI, `InputSchema` al Twenty pentru acțiunea de flux de lucru). Furnizează unul în mod explicit atunci când dorești o tipizare mai bogată — de exemplu, cu câmpuri compatibile cu `FieldMetadataType`, precum `CURRENCY` sau `RELATION` pentru constructorul de fluxuri de lucru, sau cu câmpuri `description` pe care agentul AI le poate citi: - -```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'], - }, - }, -}); -``` - - -**Scrieți o `description` bună.** Agenții AI se bazează pe câmpul `description` al funcției pentru a decide când să folosească instrumentul. Fiți specifici cu privire la ceea ce face instrumentul și când ar trebui apelat. - - - - - -O funcție post-instalare este o funcție logică care rulează automat după instalarea aplicației într-un spațiu de lucru. Serverul o execută **după** ce metadatele aplicației au fost sincronizate și clientul SDK a fost generat, astfel încât spațiul de lucru este complet pregătit pentru utilizare, iar noua schemă este disponibilă. Cazuri tipice de utilizare includ popularea cu date implicite, crearea de înregistrări inițiale, configurarea setărilor spațiului de lucru sau provizionarea resurselor în cadrul serviciilor terților. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Puteți, de asemenea, să executați manual funcția post-instalare oricând folosind CLI: - -```bash filename="Terminal" -yarn twenty exec --postInstall -``` - -Puncte cheie: -* Funcțiile de post-instalare folosesc `definePostInstallLogicFunction()` — o variantă specializată care omite setările de declanșare (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`). -* Handlerul primește un `InstallPayload` cu `{ previousVersion?: string; newVersion: string }` — `newVersion` este versiunea care este instalată, iar `previousVersion` este versiunea instalată anterior (sau `undefined` la o instalare nouă). Folosiți aceste valori pentru a distinge instalările noi de actualizări și pentru a rula logică de migrare specifică versiunii. -* **Când rulează hook-ul**: doar la instalări noi, în mod implicit. Transmiteți `shouldRunOnVersionUpgrade: true` dacă doriți să ruleze și atunci când aplicația este actualizată de la o versiune anterioară. Când este omis, indicatorul are implicit valoarea `false`, iar actualizările sar peste hook. -* **Model de execuție — implicit asincron, sincron opțional**: indicatorul `shouldRunSynchronously` controlează *modul în care* este executat post-install. - * `shouldRunSynchronously: false` *(implicit)* — hook-ul este **pus în coadă în message queue** cu `retryLimit: 3` și rulează asincron într-un worker. Răspunsul la instalare revine imediat ce jobul este pus în coadă, astfel încât un handler lent sau care eșuează nu blochează apelantul. Workerul va reîncerca de până la trei ori. **Folosiți acest mod pentru joburi de lungă durată** — popularea unor seturi mari de date, apelarea API-urilor lente ale terților, provizionarea resurselor externe, orice ar putea depăși o fereastră rezonabilă de răspuns HTTP. - * `shouldRunSynchronously: true` — hook-ul este executat **inline în timpul fluxului de instalare** (același executor ca pre-install). Cererea de instalare blochează până când handlerul se termină, iar dacă acesta aruncă o eroare, apelantul instalării primește un `POST_INSTALL_ERROR`. Fără reîncercări automate. **Folosiți acest mod pentru sarcini rapide, care trebuie să se finalizeze înainte de răspuns** — de exemplu, emiterea unei erori de validare către utilizator sau o configurare rapidă de care clientul va depinde imediat după ce apelul de instalare revine. Reține că migrarea metadatelor a fost deja aplicată până când rulează post-install, astfel încât un eșec în modul sincron **nu** anulează modificările de schemă — doar expune eroarea. -* Asigurați-vă că handlerul dvs. este idempotent. În modul asincron, coada poate reîncerca de până la trei ori; în oricare mod, hook-ul poate rula din nou la actualizări când `shouldRunOnVersionUpgrade: true`. -* Variabilele de mediu `APPLICATION_ID`, `APP_ACCESS_TOKEN` și `API_URL` sunt disponibile în interiorul handlerului (la fel ca în orice altă funcție logică), astfel încât puteți apela API-ul Twenty cu un token de acces al aplicației limitat la aplicația dvs. -* Este permisă o singură funcție de post-instalare per aplicație. Construirea manifestului va genera o eroare dacă este detectată mai mult de una. -* `universalIdentifier`, `shouldRunOnVersionUpgrade` și `shouldRunSynchronously` ale funcției sunt atașate automat la manifestul aplicației în câmpul `postInstallLogicFunction` în timpul build-ului — nu este nevoie să le referi în `defineApplication()`. -* Timpul de expirare implicit este setat la 300 de secunde (5 minute) pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor. -* **Nu se execută în modul dev**: când o aplicație este înregistrată local (prin `yarn twenty dev`), serverul sare complet peste fluxul de instalare și sincronizează fișierele direct prin watcher-ul CLI — astfel încât post-install nu rulează niciodată în modul dev, indiferent de `shouldRunSynchronously`. Folosiți `yarn twenty exec --postInstall` pentru a-l declanșa manual într-un workspace care rulează. - - - - -O funcție de pre-instalare este o funcție logică ce rulează automat în timpul instalării, **înainte ca migrarea metadatelor workspace-ului să fie aplicată**. Are aceeași structură a payload-ului ca post-install (`InstallPayload`), dar este plasată mai devreme în fluxul de instalare, astfel încât poate pregăti starea de care depinde migrarea iminentă — utilizări tipice includ realizarea unui backup al datelor, validarea compatibilității cu noua schemă sau arhivarea înregistrărilor care urmează să fie restructurate sau eliminate. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Puteți, de asemenea, să executați manual funcția de pre-instalare oricând folosind CLI: - -```bash filename="Terminal" -yarn twenty exec --preInstall -``` - -Puncte cheie: -* Funcțiile de pre-instalare folosesc `definePreInstallLogicFunction()` — aceeași configurare specializată ca pentru post-install, doar că atașată la un alt punct din ciclul de viață. -* Atât handlerele de pre-install, cât și cele de post-install primesc același tip `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importați-l o singură dată și reutilizați-l pentru ambele hook-uri. -* **Când rulează hook-ul**: poziționat chiar înainte de migrarea metadatelor workspace-ului (`synchronizeFromManifest`). Înainte de execuție, serverul rulează un "sync redus", pur aditiv, care înregistrează funcția de pre-instalare a versiunii **noi** în metadatele workspace-ului — nimic altceva nu este atins — și apoi o execută. Deoarece acest sync este doar aditiv, obiectele, câmpurile și datele versiunii precedente sunt încă intacte când rulează handlerul dvs.: puteți citi și face backup în siguranță stării pre-migrare. -* **Model de execuție**: pre-install este executat **sincron** și **blochează instalarea**. Dacă handlerul aruncă o eroare, instalarea este întreruptă înainte ca orice modificări de schemă să fie aplicate — workspace-ul rămâne la versiunea anterioară într-o stare consistentă. Acest lucru este intenționat: pre-install este ultima dvs. șansă de a refuza o actualizare riscantă. -* La fel ca la post-install, este permisă o singură funcție de pre-instalare per aplicație. Este atașată automat la manifestul aplicației sub `preInstallLogicFunction` în timpul build-ului. -* **Nu se execută în modul dev**: la fel ca post-install — fluxul de instalare este sărit complet pentru aplicațiile înregistrate local, astfel încât pre-install nu rulează niciodată sub `yarn twenty dev`. Folosiți `yarn twenty exec --preInstall` pentru a-l declanșa manual. - - - - -Ambele hook-uri fac parte din același flux de instalare și primesc același `InstallPayload`. Diferența constă în **momentul** în care rulează în raport cu migrarea metadatelor workspace-ului, iar asta schimbă ce date pot atinge în siguranță. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -Pre-install este întotdeauna **sincron** (blochează instalarea și o poate întrerupe). Post-install este **implicit asincron** — pus în coadă pe un worker cu reîncercări automate — dar poate opta pentru execuție sincronă cu `shouldRunSynchronously: true`. Consultați acordeonul `definePostInstallLogicFunction` de mai sus pentru când să folosiți fiecare mod. - -**Folosiți `post-install` pentru orice are nevoie ca noua schemă să existe.** Acesta este cazul obișnuit: - -* Popularea datelor implicite (crearea înregistrărilor inițiale, a vizualizărilor implicite, a conținutului demo) pentru obiectele și câmpurile adăugate recent. -* Înregistrarea webhook-urilor la servicii terțe, acum că aplicația are acreditările sale. -* Apelarea propriului tău API pentru a finaliza configurarea care depinde de metadatele sincronizate. -* Logică idempotentă de tipul "asigurați-vă că acest lucru există" care ar trebui să reconcilieze starea la fiecare actualizare — combină cu `shouldRunOnVersionUpgrade: true`. - -Exemplu — populează o înregistrare `PostCard` implicită după instalare: - -```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 => { - 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, -}); -``` - -**Folosiți `pre-install` atunci când o migrare altfel ar distruge sau ar corupe datele existente.** Deoarece pre-install rulează pe schema *anterioară* și eșecul său anulează actualizarea, acesta este locul potrivit pentru orice este riscant: - -* **Crearea unui backup al datelor care urmează să fie eliminate sau restructurate** — de exemplu, elimini un câmp în v2 și trebuie să-i copiezi valorile într-un alt câmp sau să le exporți în stocare înainte de rularea migrării. -* **Arhivarea înregistrărilor pe care o nouă constrângere le-ar invalida** — de exemplu, un câmp devine `NOT NULL` și trebuie mai întâi să ștergi sau să corectezi rândurile cu valori nule. -* **Validarea compatibilității și refuzarea actualizării dacă datele curente nu pot fi migrate fără probleme** — aruncă din handler și instalarea se oprește fără ca modificări să fie aplicate. Aceasta este mai sigur decât să descoperi incompatibilitatea în mijlocul migrării. -* **Redenumirea sau schimbarea cheilor datelor** înaintea unei modificări de schemă care ar pierde asocierile. - -Exemplu — arhivează înregistrări înainte de o migrare distructivă: - -```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 => { - // 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, -}); -``` - -**Regulă practică:** - -| Vrei să... | Folosiți | -| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | -| Populați date implicite, configurați workspace-ul, înregistrați resurse externe | `post-install` | -| Rulați populări de durată sau apeluri către terți care nu ar trebui să blocheze răspunsul la instalare | `post-install` (implicit — `shouldRunSynchronously: false`, cu reîncercări ale workerului) | -| Rulați o configurare rapidă de care apelantul va depinde imediat după ce apelul de instalare revine | `post-install` cu `shouldRunSynchronously: true` | -| Citești sau faci backup datelor pe care migrarea iminentă le-ar pierde | `pre-install` | -| Respingeți o actualizare care ar corupe datele existente | `pre-install` (aruncă din handler) | -| Rulați o reconciliere la fiecare actualizare | `post-install` cu `shouldRunOnVersionUpgrade: true` | -| Faceți o configurare unică doar la prima instalare | `post-install` cu `shouldRunOnVersionUpgrade: false` (implicit) | - - -Dacă aveți dubii, alegeți implicit **post-install**. Apelați la pre-install doar când migrarea în sine este distructivă și trebuie să interceptați starea anterioară înainte să dispară. - - - - - -## Clienți API tipizați (twenty-client-sdk) - -Pachetul `twenty-client-sdk` oferă doi clienți GraphQL tipați pentru a interacționa cu API-ul Twenty din funcțiile de logică și componentele Front. - -| Client | Importați | Endpoint | Generat? | -| ------------------- | ---------------------------- | ------------------------------------------------------------------- | ---------------------------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — date ale spațiului de lucru (înregistrări, obiecte) | Da, în timpul dev/build | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — configurarea spațiului de lucru, încărcări de fișiere | Nu, este livrat preconstruit | - - - - -`CoreApiClient` este clientul principal pentru interogarea și modificarea datelor din spațiul de lucru. Este generat din schema spațiului de lucru în timpul `yarn twenty dev` sau `yarn twenty build`, astfel încât este complet tipizat pentru a corespunde obiectelor și câmpurilor dvs. - -```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, - }, -}); -``` - -Clientul folosește o sintaxă de tip selection-set: transmiteți `true` pentru a include un câmp, folosiți `__args` pentru argumente și imbricați obiecte pentru relații. Obțineți autocompletare și verificare a tipurilor complete, pe baza schemei spațiului dvs. de lucru. - - -**CoreApiClient este generat în timpul dev/build.** Dacă îl utilizați fără a rula mai întâi `yarn twenty dev` sau `yarn twenty build`, va arunca o eroare. Generarea are loc automat — CLI inspectează schema GraphQL a spațiului dvs. de lucru și generează un client tipizat folosind `@genql/cli`. - - -#### Folosirea CoreSchema pentru adnotări de tip - -`CoreSchema` oferă tipuri TypeScript care corespund obiectelor din spațiul dvs. de lucru — utile pentru tiparea stării componentelor sau a parametrilor funcțiilor: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`MetadataApiClient` este livrat preconstruit împreună cu SDK-ul (nu este necesară generarea). Interoghează endpointul `/metadata` pentru configurarea spațiului de lucru, aplicații și încărcări de fișiere. - -```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 }, - }, - }, -}); -``` - -#### Încărcarea fișierelor - -`MetadataApiClient` include o metodă `uploadFile` pentru atașarea fișierelor la câmpuri de tip fișier: - -```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://...' } -``` - -| Parametru | Tip | Descriere | -| ---------------------------------- | -------- | ------------------------------------------------------------------- | -| `fileBuffer` | `Buffer` | Conținutul brut al fișierului | -| `filename` | `string` | Numele fișierului (folosit pentru stocare și afișare) | -| `contentType` | `string` | Tipul MIME (implicit `application/octet-stream` dacă este omis) | -| `fieldMetadataUniversalIdentifier` | `string` | `universalIdentifier` al câmpului de tip fișier de pe obiectul dvs. | - -Puncte cheie: -* Folosește `universalIdentifier` al câmpului (nu ID-ul specific spațiului de lucru), astfel încât codul dvs. de încărcare funcționează în orice spațiu de lucru în care aplicația dvs. este instalată. -* `url` returnat este un URL semnat pe care îl puteți folosi pentru a accesa fișierul încărcat. - - - - - - Când codul dvs. rulează pe Twenty (funcții de logică sau componente Front), platforma injectează acreditările ca variabile de mediu: - - * `TWENTY_API_URL` — URL-ul de bază al API-ului Twenty - * `TWENTY_APP_ACCESS_TOKEN` — Cheie cu durată scurtă, limitată la rolul implicit de funcție al aplicației - - Nu trebuie să le transmiteți clienților — aceștia citesc automat din `process.env`. Permisiunile cheii API sunt determinate de rolul referențiat în `defaultRoleUniversalIdentifier` din `application-config.ts`. - diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/publishing.mdx deleted file mode 100644 index 0b6418d664..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Publicare -icon: încarcă -description: Distribuie aplicația ta Twenty în marketplace sau implementeaz-o intern. ---- - -## Prezentare generală - -După ce aplicația ta este [construită și testată local](/l/ro/developers/extend/apps/building), ai două căi pentru distribuire: - -* **Implementați o arhivă tar** — încărcați aplicația direct pe un server Twenty anume pentru uz intern sau privat. -* **Publică pe npm** — listează aplicația ta în marketplace-ul Twenty pentru ca orice spațiu de lucru să o poată descoperi și instala. - -Ambele căi pornesc din aceeași etapă de **build**. - -## Construirea aplicației - -Rulează comanda `build` pentru a compila aplicația și a genera un `manifest.json` pregătit pentru distribuire: - -```bash filename="Terminal" -yarn twenty build -``` - -Aceasta compilează sursele TypeScript, transpilează funcțiile de logică și componentele de front-end și scrie totul în `.twenty/output/`. Adaugă `--tarball` pentru a produce și un pachet `.tgz` pentru distribuire manuală sau pentru comanda de deploy. - -## Implementare pe un server (tarball) - -Pentru aplicațiile pe care nu le dorești disponibile public — instrumente proprietare, integrări doar pentru enterprise sau build-uri experimentale — poți implementa un tarball direct pe un server Twenty. - -### Cerințe - -Înainte de implementare, ai nevoie de un remote configurat care să indice serverul țintă. Remote-urile stochează local URL-ul serverului și credențialele de autentificare în `~/.twenty/config.json`. - -Adaugă un remote: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Implementare - -Construiește și încarcă aplicația ta pe server într-un singur pas: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Partajarea unei aplicații implementate - - -Partajarea aplicațiilor private (tarball) între spații de lucru este o funcționalitate **Enterprise**. Fila **Distribution** va afișa un mesaj de actualizare în locul controalelor de partajare până când spațiul tău de lucru are o cheie Enterprise validă. Mergi la [Setări > Panou de administrare > Enterprise](/settings/admin-panel#enterprise) pentru a o activa. - - -Aplicațiile tarball nu sunt listate în marketplace-ul public, astfel încât alte spații de lucru de pe același server nu le vor descoperi prin navigare. După ce spațiul tău de lucru este pe planul Enterprise, poți partaja o aplicație implementată astfel: - -1. Mergi la **Setări > Aplicații > Înregistrări** și deschide aplicația ta -2. În fila **Distribuție**, fă clic pe **Copiază linkul de partajare** -3. Partajează acest link cu utilizatori din alte spații de lucru — îi duce direct la pagina de instalare a aplicației - -Linkul de partajare folosește URL-ul de bază al serverului (fără niciun subdomeniu de spațiu de lucru), astfel încât funcționează pentru orice spațiu de lucru de pe server. - -### Gestionarea versiunilor - -Când actualizezi o aplicație tarball deja implementată, serverul solicită ca `version` din `package.json` să fie **strict mai mare** (conform ordonării [semver](https://semver.org)) decât versiunea implementată în prezent. Redeployarea aceleiași versiuni sau trimiterea uneia inferioare este respinsă înainte ca tarball-ul să fie stocat — vei vedea o eroare `VERSION_ALREADY_EXISTS` de la CLI. - -Pentru a lansa o actualizare: - -1. Incrementați câmpul `version` din `package.json` (de ex. `1.2.3` → `1.2.4`, `1.3.0` sau `2.0.0`) -2. Rulează `yarn twenty deploy` (sau `yarn twenty deploy --remote production`) -3. Spațiile de lucru care au aplicația instalată vor vedea actualizarea disponibilă în setările lor - - -Etichetele de pre-lansare funcționează conform așteptărilor: incrementarea de la `1.0.0-rc.1` la `1.0.0-rc.2` este permisă, iar o lansare finală precum `1.0.0` este recunoscută corect ca fiind mai mare decât `1.0.0-rc.5`. Versiunea din `package.json` trebuie să fie ea însăși un șir semver valid. - - -{/* TODO: add screenshot of the Upgrade button */} - -### Compatibilitatea versiunii serverului - -Dacă aplicația ta folosește o funcționalitate introdusă într-o anumită versiune de server Twenty (de exemplu, furnizori OAuth adăugați în v2.3.0), ar trebui să declari versiunea minimă de server necesară aplicației folosind câmpul `engines.twenty` din `package.json`: - -```json filename="package.json" -{ - "name": "twenty-my-app", - "version": "1.0.0", - "engines": { - "node": "^24.5.0", - "twenty": ">=2.3.0" - } -} -``` - -Valoarea este un [interval semver](https://github.com/npm/node-semver#ranges) standard. Tipare comune: - -| Interval | Semnificație | -| ---------------------------------- | ------------------------------------------------------ | -| `>=2.3.0` | Orice server de la 2.3.0 încolo | -| `>=2.3.0 \<3.0.0` | 2.3.0 sau ulterior, dar sub următoarea versiune majoră | -| `^2.3.0` | La fel ca `>=2.3.0 \<3.0.0` | - -**Ce se întâmplă în timpul implementării și instalării:** - -* Dacă `engines.twenty` este setat și versiunea serverului țintă nu respectă intervalul, implementarea (încărcarea arhivei tarball) sau instalarea este respinsă cu eroarea `SERVER_VERSION_INCOMPATIBLE` și cu un mesaj care indică atât intervalul necesar, cât și versiunea efectivă a serverului. -* Dacă `engines.twenty` nu este setat, aplicația este acceptată pe orice versiune de server (retrocompatibilă cu aplicațiile existente). -* Dacă serverul nu are nicio `APP_VERSION` configurată, verificarea este omisă. - - -Serverul este verificarea autoritativă — validează `engines.twenty` atât la încărcarea arhivei tarball, cât și la instalarea în spațiul de lucru. Dacă implementezi un tarball în afara fluxului standard sau instalezi din marketplace, serverul impune în continuare compatibilitatea. - - -## CI/CD automatizat (fluxuri de lucru preconfigurate) - -Aplicațiile generate cu `create-twenty-app` vin, gata de utilizare, cu două fluxuri de lucru GitHub Actions, în `.github/workflows/`. Acestea sunt gata să ruleze imediat ce faci push al repozitoriului pe GitHub — nu este necesară nicio configurare suplimentară pentru CI, iar CD necesită doar un singur secret. - -### CI — `ci.yml` - -Rulează testele de integrare la fiecare push pe `main` și la fiecare pull request. - -**Ce face:** - -1. Preia codul sursă al aplicației. -2. Pornește o instanță de test Twenty izolată folosind acțiunea compozită `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (echivalentul din CI al `yarn twenty server start --test`). -3. Activează Corepack, configurează Node.js pe baza fișierului `.nvmrc` și instalează dependențele cu `yarn install --immutable`. -4. Rulează `yarn test`, transmitând `TWENTY_API_URL` și `TWENTY_API_KEY` din instanța pornită, astfel încât testele să poată comunica cu un server real. - -**Opțiuni de configurare:** - -* `TWENTY_VERSION` (variabilă de mediu, implicit `latest`) — fixează versiunea serverului Twenty folosită în CI editând acest parametru în `ci.yml`. -* Concurența este grupată după `github.ref` și anulează execuțiile în desfășurare la noile push-uri. - -Nu sunt necesare secrete — instanța de test este efemeră și există doar pe durata jobului. - -### CD — `cd.yml` - -Implementează aplicația pe un server Twenty configurat la fiecare push pe `main` și, opțional, dintr-un pull request când se aplică eticheta `deploy`. - -**Ce face:** - -1. Preia head-ul PR-ului (pentru PR-urile etichetate) sau commitul împins. -2. Rulează `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — echivalentul din CI al `yarn twenty deploy`. -3. Rulează `twentyhq/twenty/.github/actions/install-twenty-app@main` astfel încât versiunea nou implementată să fie instalată în spațiul de lucru țintă. - -**Configurare necesară:** - -| Setare | Unde | Scop | -| ----------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `TWENTY_DEPLOY_URL` | `env` în `cd.yml` (implicit `http://localhost:3000`) | Serverul Twenty la care se face implementarea. Modifică-l la URL-ul real al serverului înainte de prima utilizare. | -| `TWENTY_DEPLOY_API_KEY` | GitHub repo **Settings → Secrets and variables → Actions** | Cheie API cu permisiune de implementare pe serverul țintă. | - - -Valoarea implicită a `TWENTY_DEPLOY_URL`, `http://localhost:3000`, este un placeholder — nu va putea accesa nimic dintr-un runner găzduit de GitHub. Actualizează-l la URL-ul public al serverului tău (sau folosește un runner self-hosted cu acces la rețea) înainte de a activa CD. - - -**Declanșarea unei implementări de previzualizare dintr-un PR:** - -Adaugă eticheta `deploy` la un pull request. Condiția `if:` din `cd.yml` va rula jobul pentru acel PR folosind commitul head al PR-ului, permițându-ți să validezi o modificare pe serverul țintă înainte de a face merge. - -### Fixarea acțiunilor reutilizabile - -Ambele fluxuri de lucru fac referire la acțiuni reutilizabile la `@main`, astfel încât actualizările acțiunilor din repo-ul `twentyhq/twenty` sunt preluate automat. Dacă dorești builduri deterministe, înlocuiește `@main` cu un SHA de commit sau cu un tag de release pe fiecare linie `uses:`. - -## Publicarea pe npm - -Publicarea pe npm face ca aplicația ta să poată fi descoperită în marketplace-ul Twenty. Orice spațiu de lucru Twenty poate răsfoi, instala și actualiza aplicațiile din marketplace direct din interfață. - -### Cerințe - -* Un cont [npm](https://www.npmjs.com) -* Cuvântul cheie `twenty-app` din array-ul `keywords` al fișierului `package.json` (adaugă-l manual — nu este inclus în mod implicit în șablonul `create-twenty-app`) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Metadate pentru marketplace - -Configurația `defineApplication()` acceptă câmpuri opționale care controlează modul în care aplicația ta apare în marketplace. Folosește `logoUrl` și `screenshots` pentru a face referire la imaginile din folderul `public/`: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -Vezi [acordeonul defineApplication](/l/ro/developers/extend/apps/building#defineentity-functions) din pagina Building Apps pentru lista completă de câmpuri ale marketplace-ului (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.). - -#### Dimensiuni recomandate pentru capturi de ecran - -marketplace-ul redă `screenshots` într-un container fix cu raport `8:5` (de exemplu, `1600×1000 px`). - - -Capturile de ecran cu orice raport de aspect sunt afișate integral și nu sunt niciodată decupate, însă orice este semnificativ mai înalt sau mai îngust decât `8:5` va afișa benzi goale pe laterale. - - -### Publicare - -```bash filename="Terminal" -yarn twenty publish -``` - -Pentru a publica sub un dist-tag specific (de ex., `beta` sau `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### Cum funcționează descoperirea în marketplace - -Serverul Twenty sincronizează catalogul marketplace-ului din registrul npm **la fiecare oră**. - -Poți declanșa sincronizarea imediat, în loc să aștepți: - -```bash filename="Terminal" -yarn twenty server catalog-sync -# To target a specific remote: -# yarn twenty server catalog-sync --remote production -``` - -Metadatele afișate în marketplace provin din configurația `defineApplication()` — câmpuri precum `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` și `termsUrl`. - - -Dacă aplicația ta nu definește un `aboutDescription` în `defineApplication()`, piața va folosi automat fișierul `README.md` al pachetului tău de pe npm drept conținut pentru pagina Despre. Acest lucru înseamnă că poți menține un singur README atât pentru npm, cât și pentru piața Twenty. Dacă vrei o descriere diferită în piață, setează explicit `aboutDescription`. - - -### Publicare CI - -Folosește acest workflow GitHub Actions pentru a publica automat la fiecare release (folosește [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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Pentru alte sisteme CI (GitLab CI, CircleCI etc.), se aplică aceleași trei comenzi: `yarn install`, `yarn twenty build`, apoi `npm publish` din `.twenty/output`. - - -**npm provenance** este opțională, dar recomandată. Publicarea cu `--provenance` adaugă un badge de încredere la listarea ta în npm, permițând utilizatorilor să verifice că pachetul a fost construit dintr-un commit specific într-un pipeline CI public. Vezi [documentația npm provenance](https://docs.npmjs.com/generating-provenance-statements) pentru instrucțiuni de configurare. - - -## Instalarea aplicațiilor - -După ce o aplicație este publicată (npm) sau implementată (tarball), spațiile de lucru o pot instala prin interfața utilizatorului (UI). - -Mergi la pagina **Setări > Aplicații** din Twenty, unde pot fi parcurse și instalate atât aplicațiile din marketplace, cât și cele implementate prin tarball. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Poți instala aplicații și din linia de comandă: - -```bash filename="Terminal" -yarn twenty install -``` - - -Serverul impune versionarea semver la instalare, reflectând regulile de la deploy: - -* Instalarea aceleiași versiuni care este deja instalată în workspace-ul tău este respinsă cu o eroare `APP_ALREADY_INSTALLED`. -* Instalarea unei versiuni mai mici decât cea instalată în prezent este respinsă cu o eroare `CANNOT_DOWNGRADE_APPLICATION`. - -Pentru a instala o versiune mai nouă, fă mai întâi deploy sau public-o, apoi rulează din nou `yarn twenty install`. - diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index 659ecf03f7..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Abilități și agenți -description: Definiți abilități și agenți AI pentru aplicația dvs. -icon: robot ---- - - - Aptitudinile și agenții sunt în prezent în testare alfa. Caracteristica funcționează, dar este încă în dezvoltare. - - -Aplicațiile pot defini capabilități AI care există în interiorul spațiului de lucru — instrucțiuni reutilizabile pentru abilități și agenți cu prompturi de sistem personalizate. - - - - -Abilitățile definesc instrucțiuni și capabilități reutilizabile pe care agenții AI le pot folosi în spațiul dvs. de lucru. Folosiți `defineSkill()` pentru a defini abilități cu validare încorporată: - -```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`, -}); -``` - -Puncte cheie: -* `name` este un șir identificator unic pentru abilitate (se recomandă kebab-case). -* `label` este numele lizibil afișat în interfața cu utilizatorul (UI). -* `content` conține instrucțiunile abilității — acesta este textul pe care agentul AI îl folosește. -* `icon` (opțional) setează pictograma afișată în UI. -* `description` (opțional) oferă context suplimentar despre scopul abilității. - - - - -Agenții sunt asistenți AI care există în interiorul spațiului dvs. de lucru. Utilizați `defineAgent()` pentru a crea agenți cu un prompt de sistem personalizat: - -```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.', -}); -``` - -Puncte cheie: -* `name` este un șir identificator unic pentru agent (se recomandă kebab-case). -* `label` este numele de afișare din interfața cu utilizatorul (UI). -* `prompt` conține promptul de sistem — acesta este textul de instrucțiuni care definește comportamentul agentului. -* `description` (opțional) oferă context suplimentar despre scopul agentului. -* `icon` (opțional) setează pictograma afișată în UI. -* `modelId` (opțional) suprascrie modelul AI implicit utilizat de agent. - - - diff --git a/packages/twenty-docs/l/ro/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/ro/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 0f039a15de..0000000000 --- a/packages/twenty-docs/l/ro/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Aplicații Twenty -description: Construiți și gestionați personalizările Twenty sub formă de cod. ---- - - -Aplicațiile sunt în prezent în testare alfa. Caracteristica funcționează, dar este încă în dezvoltare. - - -## Ce sunt aplicațiile? - -Aplicațiile vă permit să extindeți Twenty cu obiecte personalizate, câmpuri, funcții logice, componente front-end, abilități IA și altele — toate gestionate ca cod. În loc să configurați totul prin interfața de utilizator (UI), vă definiți modelul de date și logica în TypeScript și le implementați în unul sau mai multe spații de lucru. - -**Ce puteți construi:** - -* **Obiecte și câmpuri personalizate** — extindeți modelul de date cu entități noi sau adăugați câmpuri la obiecte existente, precum Companie sau Persoană -* **Funcții logice** — funcții pe partea de server declanșate de evenimente ale bazei de date, programări cron sau rute HTTP -* **Componente front-end** — componente React care se afișează în interfața Twenty (pagini de înregistrări, meniul de comenzi, panouri laterale) -* **Abilități și agenți IA** — extindeți IA din Twenty cu capabilități personalizate -* **Vizualizări și navigare** — vizualizări salvate preconfigurate și linkuri în bara laterală - -## Pornire rapidă - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Aceasta creează scheletul unei aplicații noi, pornește opțional un server Twenty local și începe să monitorizeze fișierele pentru modificări. Consultați ghidul [Începeți](/l/ro/developers/extend/apps/getting-started) pentru prezentarea completă. - -## Ghiduri detaliate - -| Ghid | Descriere | -| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Începeți](/l/ro/developers/extend/apps/getting-started) | Creați scheletul unei aplicații, configurați un server local, structură de proiect, CI | -| [Crearea aplicațiilor](/l/ro/developers/extend/apps/building) | Definiții ale entităților (`defineObject`, `defineLogicFunction`, `defineFrontComponent` etc.), clienți API, pachete npm, resurse publice, testare | -| [Publicare](/l/ro/developers/extend/apps/publishing) | Implementare pe un server, publicare pe npm, marketplace | - -## Concepte cheie - -### Detectarea entităților - -SDK-ul detectează entitățile scanând fișierele TypeScript pentru apeluri `export default define({...})`. Denumirea fișierelor și structura folderelor sunt flexibile — detectarea este bazată pe AST, nu pe căi. - -### Tipuri de entități disponibile - -| Funcție | Scop | -| ---------------------------------- | -------------------------------------------------------- | -| `defineApplication()` | Metadate ale aplicației (obligatoriu, una per aplicație) | -| `defineObject()` | Obiecte personalizate cu câmpuri | -| `defineField()` | Câmpuri pe obiecte existente | -| `defineLogicFunction()` | Logică pe partea de server cu declanșatoare | -| `defineFrontComponent()` | Componente React în interfața Twenty | -| `defineRole()` | Roluri de permisiuni | -| `defineView()` | Configurații pentru vizualizări salvate | -| `defineNavigationMenuItem()` | Linkuri de navigare în bara laterală | -| `defineSkill()` | Abilități ale agentului IA | -| `defineAgent()` | Agenți IA cu prompturi | -| `definePageLayout()` | Dispuneri personalizate pentru paginile de înregistrare | -| `definePreInstallLogicFunction()` | Rulează înainte de instalarea aplicației | -| `definePostInstallLogicFunction()` | Rulează după instalarea aplicației | - -### Flux de lucru pentru dezvoltare - -1. **`yarn twenty dev`** — monitorizează fișierele sursă, reconstruiește la modificări, sincronizează cu serverul, generează clienți API tipizați -2. **`yarn twenty build`** — produce o versiune distribuibilă -3. **`yarn twenty deploy`** — implementează pe un server Twenty la distanță -4. **`yarn twenty add`** — generează interactiv o entitate nouă - -### Referință CLI - -```bash filename="Terminal" -yarn twenty help # Listează toate comenzile -yarn twenty server start # Pornește serverul local de dezvoltare -yarn twenty remote add # Conectează-te la un server Twenty -yarn twenty exec -n fn # Execută o funcție logică -yarn twenty logs -n fn # Transmite în flux jurnalele funcției -``` - -Consultați ghidul [Începeți](/l/ro/developers/extend/apps/getting-started) pentru referința completă CLI. diff --git a/packages/twenty-docs/l/ro/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/ro/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index d41bded64e..0000000000 --- a/packages/twenty-docs/l/ro/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Funcții ale Laboratorului - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Accesați **Setări → Lansări** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/building.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/building.mdx deleted file mode 100644 index 31d5a785e8..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Архитектура -description: Как работают приложения Twenty — изоляция в песочнице, жизненный цикл и базовые элементы. -icon: sitemap ---- - -Приложения Twenty — это пакеты TypeScript, которые расширяют ваше рабочее пространство пользовательскими объектами, логикой, компонентами интерфейса и возможностями ИИ. Они работают на платформе Twenty с полной изоляцией в песочнице и контролем прав доступа. - -## Как работают приложения - -Приложение — это набор **сущностей**, объявленных с помощью функций `defineEntity()` из пакета `twenty-sdk`. SDK обнаруживает эти объявления посредством анализа AST на этапе сборки и формирует **манифест** — полное описание того, что ваше приложение добавляет в рабочее пространство. - -``` -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 -``` - - - **Организация файлов — на ваше усмотрение.** Обнаружение сущностей основано на AST — SDK находит вызовы `export default defineEntity(...)` независимо от расположения файла. Структура папок выше — это соглашение, а не требование. - - -## Типы сущностей - -| Сущность | Назначение | Документация | -| ------------------------ | ------------------------------------------------------------ | ---------------------------------------------------------------- | -| **Приложение** | Идентификация приложения, права доступа, переменные | [Модель данных](/l/ru/developers/extend/apps/data-model) | -| **Роль** | Наборы прав для объектов и полей | [Модель данных](/l/ru/developers/extend/apps/data-model) | -| **Object** | Пользовательские таблицы данных с полями | [Модель данных](/l/ru/developers/extend/apps/data-model) | -| **Поле** | Расширение существующих объектов, определение связей | [Модель данных](/l/ru/developers/extend/apps/data-model) | -| **Логическая функция** | Серверный TypeScript с триггерами | [Логические функции](/l/ru/developers/extend/apps/logic-functions) | -| **Компонент фронтенда** | Изолированный в песочнице интерфейс React на странице Twenty | [Компоненты фронтенда](/l/ru/developers/extend/apps/front-components) | -| **Навык** | Повторно используемые инструкции для ИИ-агента | [Навыки и агенты](/l/ru/developers/extend/apps/skills-and-agents) | -| **Агент** | ИИ-агенты с пользовательскими промптами | [Навыки и агенты](/l/ru/developers/extend/apps/skills-and-agents) | -| **Представление** | Преднастроенные представления списков записей | [Макет](/l/ru/developers/extend/apps/layout) | -| **Пункт меню навигации** | Пользовательские элементы боковой панели | [Макет](/l/ru/developers/extend/apps/layout) | -| **Макет страницы** | Пользовательские вкладки и виджеты страницы записи | [Макет](/l/ru/developers/extend/apps/layout) | - -## Изоляция в песочнице - -* **Логические функции** выполняются в изолированных процессах Node.js на сервере. Они получают доступ к данным только через типизированный клиент API, ограниченный правами роли приложения. -* **Компоненты фронтенда** запускаются в Web Workers с использованием Remote DOM — изолированы от основной страницы, но при этом рендерят нативные элементы DOM (не iframes). Они взаимодействуют с Twenty через хостовый API обмена сообщениями. -* **Права доступа** применяются на уровне API. Токен времени выполнения (`TWENTY_APP_ACCESS_TOKEN`) выводится из роли, определённой в `defineApplication()`. - -## Жизненный цикл приложения - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ 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 build`** — компилирует TypeScript, упаковывает логические функции и фронтенд-компоненты с помощью esbuild и формирует манифест. -* **Хуки до/после установки** — необязательные логические функции, которые выполняются во время установки. См. [Логические функции](/l/ru/developers/extend/apps/logic-functions) для подробностей. - -## Следующие шаги - - - - Определяйте объекты, поля, роли и связи. - - - Серверные функции с HTTP-, cron- и событийными триггерами. - - - Изолированные в песочнице компоненты React внутри интерфейса Twenty. - - - Представления, пункты навигации и макеты страниц записей. - - - ИИ-навыки и агенты с пользовательскими промптами. - - - Команды CLI, тестирование, ассеты, удалённые модули и CI. - - - Разверните на сервере или опубликуйте в маркетплейсе. - - diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index 8edef63f1c..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI и тестирование -description: Команды CLI, настройка тестирования, публичные ресурсы, пакеты npm, удалённые репозитории и конфигурация CI. -icon: terminal ---- - -## Публичные ресурсы (папка `public/`) - -Папка `public/` в корне вашего приложения содержит статические файлы — изображения, значки, шрифты и любые другие ресурсы, необходимые вашему приложению во время выполнения. Эти файлы автоматически включаются в сборки, синхронизируются в режиме разработки и загружаются на сервер. - -Файлы, размещённые в `public/`, являются: - -* **Публично доступными** — после синхронизации с сервером ресурсы доступны по публичному URL. Для доступа к ним аутентификация не требуется. -* **Доступными в компонентах фронтенда** — используйте URL ресурсов для отображения изображений, значков или любого медиа внутри ваших компонентов React. -* **Доступными в логических функциях** — используйте URL ресурсов в письмах, ответах API или любой серверной логике. -* **Используются для метаданных маркетплейса** — поля `logoUrl` и `screenshots` в `defineApplication()` ссылаются на файлы из этой папки (например, `public/logo.png`). Они отображаются в маркетплейсе при публикации вашего приложения. -* **Автосинхронизация в режиме разработки** — когда вы добавляете, обновляете или удаляете файл в `public/`, он автоматически синхронизируется с сервером. Перезапуск не требуется. -* **Включены в сборки** — `yarn twenty build` упаковывает все публичные ресурсы в выходной дистрибутив. - -### Доступ к публичным ресурсам с помощью `getPublicAssetUrl` - -Используйте хелпер `getPublicAssetUrl` из `twenty-sdk`, чтобы получить полный URL файла в каталоге `public/` вашего приложения. Он работает как в **логических функциях**, так и в **компонентах фронтенда**. - -**В логической функции:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -Аргумент `path` задаётся относительно папки `public/` вашего приложения. И `getPublicAssetUrl('logo.png')`, и `getPublicAssetUrl('public/logo.png')` приводят к одному и тому же URL — префикс `public/`, если он есть, удаляется автоматически. - -## Использование пакетов 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 => { - 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

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -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` — они не включаются в бандл, а подставляются сервером во время выполнения. - -## Тестирование вашего приложения - -SDK предоставляет программные API, которые позволяют собирать, разворачивать, устанавливать и удалять ваше приложение из тестового кода. В сочетании с [Vitest](https://vitest.dev/) и типизированными клиентами API вы можете писать интеграционные тесты, которые проверяют, что ваше приложение работает сквозным образом на реальном сервере Twenty. - -### Настройка - -Приложение, созданное скэффолдером, уже включает 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), - ); -}); -``` - -### Программные API SDK - -Подпуть `twenty-sdk/cli` экспортирует функции, которые можно вызывать напрямую из тестового кода: - -| Функция | Описание | -| -------------- | ---------------------------------------------------------- | -| `appBuild` | Собрать приложение и при необходимости упаковать tar-архив | -| `appDeploy` | Загрузить tar-архив на сервер | -| `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 typecheck -``` - -Это запускает `tsc --noEmit` и сообщает о любых ошибках типов. - -## Справочник по CLI - -Помимо `dev`, `build`, `add` и `typecheck`, CLI предоставляет команды для выполнения функций, просмотра логов и управления установками приложений. - -### Выполнение функций (`yarn twenty exec`) - -Запустите логическую функцию вручную, не вызывая её через HTTP, cron или событие базы данных: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Просмотр логов функций (`yarn twenty logs`) - -Потоковая передача журналов выполнения логических функций вашего приложения: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -Это отличается от `yarn twenty server logs`, который показывает логи контейнера Docker. `yarn twenty logs` показывает журналы выполнения функций вашего приложения с сервера Twenty. - - -### Удаление приложения (`yarn twenty uninstall`) - -Удалите свое приложение из активного рабочего пространства: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty 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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Ваши учётные данные хранятся в `~/.twenty/config.json`. - -## CI с GitHub Actions - -Скэффолдер генерирует готовый к использованию рабочий процесс GitHub Actions в `.github/workflows/ci.yml`. Он автоматически запускает ваши интеграционные тесты при каждом пуше в `main` и в pull request'ах. - -Рабочий процесс: - -1. Извлекает ваш код -2. Поднимает временный сервер Twenty с помощью экшена `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` -3. Устанавливает зависимости с помощью `yarn install --immutable` -4. Запускает `yarn test` с `TWENTY_API_URL` и `TWENTY_API_KEY`, переданными из выходных данных экшена - -```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 автоматически. - -Чтобы закрепить конкретную версию Twenty вместо `latest`, измените переменную окружения `TWENTY_VERSION` в начале рабочего процесса. diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/connections.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/connections.mdx deleted file mode 100644 index 728adb1cf6..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/connections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Подключения -description: Разрешите вашему приложению действовать от имени пользователя в сторонних сервисах с помощью OAuth. -icon: plug ---- - -Подключения — это учетные данные, которыми пользователь располагает для внешнего сервиса (Linear, GitHub, Slack, ...). Ваше приложение определяет, **как** получают эти учетные данные — через **провайдера подключения** — и использует их во время выполнения для выполнения аутентифицированных вызовов к стороннему API. - -На данный момент поддерживается только OAuth 2.0. Будущие типы учетных данных (персональные токены доступа, ключи API, базовая аутентификация) будут подключаться к тому же интерфейсу — приложения, уже использующие `defineConnectionProvider({ type: 'oauth', ... })` не потребуют миграции. - - - - - -Провайдер подключения описывает процедуру OAuth-обмена, которая требуется вашему приложению. Пользователь нажимает "Добавить подключение" в настройках вашего приложения, подтверждает разрешения на экране согласия провайдера, и в его рабочем пространстве создается запись `ConnectedAccount`. - -Рабочей конфигурации нужны **два файла** — провайдер подключения и соответствующее объявление `serverVariables` в `defineApplication`, которое содержит учетные данные клиента OAuth. - -```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.', - defaultRoleUniversalIdentifier: '...', - // 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, - }, - }, -}); -``` - -Основные моменты: - -* `name` — это уникальная строка-идентификатор, используемая в `listConnections({ providerName })` (kebab-case, должна соответствовать `^[a-z][a-z0-9-]*$`). -* `displayName` отображается на вкладке настроек приложения и в списке инструментов ИИ. -* `clientIdVariable` / `clientSecretVariable` — это **имена**, а не значения — они должны совпадать с ключами, объявленными в `defineApplication.serverVariables`. Фактические `client_id` и `client_secret` вводятся администратором сервера через интерфейс регистрации приложения и никогда не коммитятся в ваш репозиторий. -* Используйте `serverVariables` (не `applicationVariables`) — учетные данные OAuth являются общими для сервера, и на каждом сервере Twenty используется одно приложение OAuth. -* Пока оба `serverVariables` не заполнены, на вкладке настроек приложения показывается подсказка "нужен администратор сервера", а кнопка "Добавить подключение" отключена. -* `type: 'oauth'` — единственное поддерживаемое сегодня значение. Дискриминатор совместим с будущими версиями: будущие типы (`'pat'`, `'api-key'`, ...) добавят новые блоки подконфигурации рядом с `oauth`. - -URL обратного вызова OAuth, который вашему провайдеру нужно добавить в список разрешенных: - -``` -https:///apps/oauth/callback -``` - - - - - -Внутри обработчика логической функции `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` | Уникальный идентификатор записи; передайте его в `getConnection(id)`, чтобы повторно получить одну запись | -| `visibility` | `'user'` (приватно для одного участника рабочего пространства) или `'workspace'` (доступно всем участникам) | -| `scopes` | Разрешения OAuth, предоставленные внешним провайдером (отличаются от `visibility` — это несвязанные вещи) | -| `userWorkspaceId` | Идентификатор userWorkspace владельца — полезно для выбора "подключения пользователя запроса" в триггерах HTTP-маршрутов | -| `accessToken` | Актуальный токен доступа OAuth (обновляется автоматически при истечении срока действия) | -| `name` / `handle` | Отображаемое имя подключения (автоматически определяется при обратном вызове OAuth, может быть переименовано пользователем) | -| `authFailedAt` | Устанавливается, если последняя попытка обновления не удалась; пользователю нужно переподключиться | - -Основные моменты: - -* Передайте `{ providerName }`, чтобы отфильтровать по провайдеру; опустите, чтобы получить все подключения этого приложения у всех провайдеров. -* Сервер прозрачно обновляет токен доступа перед возвратом. Ваш обработчик всегда получает рабочий токен (или установлено `authFailedAt`). -* `getConnection(id)` — эквивалент для одной записи. - - - - - -Когда пользователь нажимает "Добавить подключение", ему предлагается выбрать видимость: - -* **Только для меня** — учетные данные приватны для подключившегося пользователя. Любая логическая функция, вызываемая от его имени (триггер HTTP-маршрута с `isAuthRequired: true`), видит их; триггеры 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" одновременно. - - - - - -Для каждого провайдера подключения администратору сервера сначала нужно зарегистрировать у стороннего сервиса приложение OAuth. - -1. Перейдите в настройки разработчика провайдера (например, https://linear.app/settings/api/applications/new). -2. Установите **Redirect URI** в значение `\/apps/oauth/callback`. -3. Скопируйте сгенерированные **Client ID** и **Client Secret**. -4. Откройте установленное приложение в Twenty под учетной записью администратора сервера → задайте значения в соответствующих `serverVariables`. -5. Затем участники рабочего пространства смогут добавлять подключения в разделе **Подключения** конкретного приложения. - - - - diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/data-model.mdx deleted file mode 100644 index 4aed302e29..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,493 +0,0 @@ ---- -title: Модель данных -description: Определяйте объекты, поля, роли и метаданные приложения с помощью Twenty SDK. -icon: database ---- - -Пакет `twenty-sdk` предоставляет функции `defineEntity` для определения модели данных вашего приложения. Вы должны использовать `export default defineEntity({...})`, чтобы SDK обнаруживал ваши сущности. Эти функции проверяют вашу конфигурацию на этапе сборки и обеспечивают автодополнение в IDE и безопасность типов. - - - **Организация файлов — на ваше усмотрение.** - Обнаружение сущностей основано на AST — SDK находит вызовы `export default defineEntity(...)` независимо от расположения файла. Группировка файлов по типу (например, `logic-functions/`, `roles/`) — это лишь соглашение, а не требование. - - - - - -Роли инкапсулируют права на объекты и действия вашего рабочего пространства. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -В каждом приложении должен быть ровно один вызов `defineApplication`, который описывает: - -* **Идентификация**: идентификаторы, отображаемое имя и описание. -* **Разрешения**: какую роль используют его функции и фронтенд-компоненты. -* **(Необязательно) Переменные**: пары ключ–значение, доступные вашим функциям как переменные окружения. -* **(Необязательно) Предустановочные / постустановочные функции**: логические функции, которые запускаются до или после установки. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -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, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Заметки: -* Поля `universalIdentifier` — это детерминированные идентификаторы, которые принадлежат вам. Сгенерируйте их один раз и сохраняйте неизменными между синхронизациями. -* `applicationVariables` становятся переменными окружения для ваших функций и фронтенд-компонентов (например, `DEFAULT_RECIPIENT_NAME` доступна как `process.env.DEFAULT_RECIPIENT_NAME`). -* `defaultRoleUniversalIdentifier` должен ссылаться на роль, определённую с помощью `defineRole()` (см. выше). -* Предустановочные и постустановочные функции обнаруживаются автоматически во время сборки манифеста — вам не нужно указывать их в `defineApplication()`. - -#### Метаданные маркетплейса - -Если вы планируете [опубликовать приложение](/l/ru/developers/extend/apps/publishing), эти необязательные поля определяют, как оно отображается в маркетплейсе: - -| Поле | Описание | -| ------------------ | ------------------------------------------------------------------------------------------------------------------- | -| `author` | Имя автора или название компании | -| `category` | Категория приложения для фильтрации в маркетплейсе | -| `logoUrl` | Путь к логотипу вашего приложения (например, `public/logo.png`) | -| `screenshots` | Массив путей к скриншотам (например, `public/screenshot-1.png`) | -| `aboutDescription` | Расширенное описание в Markdown для вкладки "About". Если опущено, маркетплейс использует `README.md` пакета из npm | -| `websiteUrl` | Ссылка на ваш сайт | -| `termsUrl` | Ссылка на условия предоставления услуг | -| `emailSupport` | Адрес электронной почты поддержки | -| `issueReportUrl` | Ссылка на систему отслеживания проблем | - -#### Роли и разрешения - -Поле `defaultRoleUniversalIdentifier` в `application-config.ts` обозначает роль по умолчанию, используемую логическими функциями и фронтенд-компонентами вашего приложения. Подробности см. в `defineRole` выше. - -* Токен времени выполнения, подставляемый как `TWENTY_APP_ACCESS_TOKEN`, формируется из этой роли. -* Типизированный клиент ограничен правами, предоставленными этой ролью. -* Следуйте принципу наименьших привилегий: создайте отдельную роль только с теми правами, которые нужны вашим функциям. - -##### Роль функции по умолчанию - -Когда вы генерируете новое приложение, CLI создаёт файл роли по умолчанию: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -Значение `universalIdentifier` этой роли указывается в `application-config.ts` как `defaultRoleUniversalIdentifier`: - -* **\*.role.ts** определяет, что может делать роль. -* **application-config.ts** указывает на эту роль, чтобы ваши функции наследовали её права. - -Заметки: -* Начните со сгенерированной роли, затем постепенно ограничивайте её, следуя принципу наименьших привилегий. -* Замените `objectPermissions` и `fieldPermissions` на объекты и поля, которые действительно нужны вашим функциям. -* `permissionFlags` управляют доступом к возможностям на уровне платформы. Сведите их к минимуму. -* См. рабочий пример: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Пользовательские объекты описывают как схему, так и поведение записей в вашем рабочем пространстве. Используйте `defineObject()` для определения объектов со встроенной валидацией: - -```ts postCard.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, - }, - ], -}); -``` - -Основные моменты: - -* Используйте `defineObject()` для встроенной валидации и лучшей поддержки в IDE. -* `universalIdentifier` должен быть уникальным и стабильным между развёртываниями. -* Каждому полю требуются `name`, `type`, `label` и собственный стабильный `universalIdentifier`. -* Массив `fields` необязателен — вы можете определять объекты без пользовательских полей. -* Вы можете сгенерировать новые объекты с помощью `yarn twenty add`, который проведёт вас через выбор именования, полей и связей. - - -**Базовые поля создаются автоматически.** Когда вы определяете пользовательский объект, Twenty автоматически добавляет стандартные поля, -такие как `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` и `deletedAt`. -Вам не нужно определять их в массиве `fields` — добавляйте только свои пользовательские поля. -Вы можете переопределить поля по умолчанию, определив поле с тем же именем в массиве `fields`, -но это не рекомендуется. - - - - - -Используйте `defineField()` для добавления полей к объектам, которые вам не принадлежат — например, к стандартным объектам Twenty (Person, Company и т. д.). или к объектам из других приложений. В отличие от встроенных полей в `defineObject()`, отдельные поля требуют `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` определяет целевой объект. Для стандартных объектов используйте `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`, экспортируемые из `twenty-sdk`. -* При определении полей непосредственно в `defineObject()` вам не нужен `objectUniversalIdentifier` — он наследуется от родительского объекта. -* `defineField()` — единственный способ добавить поля к объектам, которые вы не создавали с помощью `defineObject()`. - - - - -Отношения связывают объекты между собой. В Twenty отношения всегда двунаправленные — вы определяете обе стороны, и каждая сторона ссылается на другую. - -Существуют два типа отношений: - -| Тип отношения | Описание | Есть внешний ключ? | -| ------------- | --------------------------------------------------------------------- | ---------------------- | -| `MANY_TO_ONE` | Многие записи этого объекта указывают на одну запись целевого объекта | Да (`joinColumnName`) | -| `ONE_TO_MANY` | Одна запись этого объекта имеет много записей целевого объекта | Нет (обратная сторона) | - -#### Как работают отношения - -Каждое отношение требует **двух полей**, которые ссылаются друг на друга: - -1. Сторона **MANY_TO_ONE** — находится в объекте, который содержит внешний ключ -2. Сторона **ONE_TO_MANY** — находится в объекте, которому принадлежит коллекция - -Оба поля используют `FieldType.RELATION` и ссылаются друг на друга через `relationTargetFieldMetadataUniversalIdentifier`. - -#### Пример: Почтовая открытка имеет много получателей - -Предположим, `PostCard` может быть отправлен множству записей `PostCardRecipient`. Каждый получатель относится ровно к одной открытке. - -**Шаг 1: Определите сторону ONE_TO_MANY на PostCard** (сторона "one"): - -```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: Определите сторону MANY_TO_ONE на PostCardRecipient** (сторона "many" — содержит внешний ключ): - -```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', - }, -}); -``` - - -**Циклические импорты:** Оба поля отношений ссылаются на `universalIdentifier` друг друга. Чтобы избежать проблем с циклическими импортами, экспортируйте идентификаторы полей как именованные константы из каждого файла и импортируйте их в другом файле. Система сборки разрешает это на этапе компиляции. - - -#### Связывание со стандартными объектами - -Чтобы создать отношение со встроенным объектом 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 - -Вы также можете определять поля отношений непосредственно внутри `defineObject()`. В этом случае опустите `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 - ], -}); -``` - - - -## Создание заготовок сущностей с помощью `yarn twenty add` - -Вместо ручного создания файлов сущностей вы можете использовать интерактивный генератор: - -```bash filename="Terminal" -yarn twenty add -``` - -Он предложит выбрать тип сущности и проведёт вас по обязательным полям. Он генерирует готовый к использованию файл со стабильным `universalIdentifier` и корректным вызовом `defineEntity()`. - -Вы также можете передать тип сущности напрямую, чтобы пропустить первый запрос: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Доступные типы сущностей - -| Тип сущности | Команда | Сгенерированный файл | -| -------------------- | ------------------------------------ | ------------------------------------------------------- | -| Объект | `yarn twenty add object` | `src/objects/\.ts` | -| Поле | `yarn twenty add field` | `src/fields/\.ts` | -| Логическая функция | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Компонент фронтенда | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Роль | `yarn twenty add role` | `src/roles/\.ts` | -| Навык | `yarn twenty add skill` | `src/skills/\.ts` | -| Агент | `yarn twenty add agent` | `src/agents/\.ts` | -| Представление | `yarn twenty add view` | `src/views/\.ts` | -| Пункт меню навигации | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Макет страницы | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### Что генерирует скэффолдер - -У каждого типа сущности есть свой шаблон. Например, `yarn twenty add object` запрашивает: - -1. **Имя (единственное число)** — например, `invoice` -2. **Имя (множественное число)** — например, `invoices` -3. **Метка (единственное число)** — заполняется автоматически из имени (например, `Invoice`) -4. **Метка (множественное число)** — заполняется автоматически (например, `Invoices`) -5. **Создать представление и пункт навигации?** — если вы ответите «да», скэффолдер также сгенерирует соответствующее представление и ссылку в боковой панели для нового объекта. - -У других типов сущностей подсказки проще — в большинстве случаев запрашивается только имя. - -Тип сущности `field` более детализирован: он запрашивает имя поля, метку, тип (из списка всех доступных типов полей, таких как `TEXT`, `NUMBER`, `SELECT`, `RELATION` и т. д.), а также `universalIdentifier` целевого объекта. - -### Пользовательский путь вывода - -Используйте флаг `--path`, чтобы поместить сгенерированный файл в пользовательское расположение: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/front-components.mdx deleted file mode 100644 index bf15835534..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: Компоненты фронтенда -description: Создавайте компоненты React, которые отображаются внутри интерфейса Twenty в изолированной песочнице. -icon: window-maximize ---- - -Фронтенд-компоненты — это компоненты React, которые отображаются непосредственно внутри интерфейса Twenty. Они выполняются в изолированном Web Worker с использованием Remote DOM — ваш код изолирован (sandboxed), но рендерится нативно на странице, а не в iframe. - -## Где можно использовать фронт-компоненты - -Фронт-компоненты могут отображаться в двух местах внутри Twenty: - -* **Боковая панель** — фронт-компоненты с интерфейсом открываются в правой боковой панели. Это поведение по умолчанию, когда фронт-компонент запускается из меню команд. -* **Виджеты (дашборды и страницы записей)** — фронт-компоненты можно встраивать как виджеты в макеты страниц. При настройке дашборда или макета страницы записи пользователи могут добавить виджет фронт-компонента. - -## Простой пример - -Самый быстрый способ увидеть фронтенд-компонент в действии — зарегистрировать его в качестве **пункта меню команд**. Используйте `defineCommandMenuItem` в отдельном файле, чтобы компонент отображался как кнопка быстрого действия в правом верхнем углу страницы: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -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` (или однократного запуска `yarn twenty dev --once`) быстрое действие появится в правом верхнем углу страницы: - -
- Кнопка быстрого действия в правом верхнем углу -
- -Нажмите её, чтобы отобразить компонент инлайн. - -## Поля конфигурации - -| Поле | Обязательно | Описание | -| --------------------- | ----------- | -------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Да | Стабильный уникальный идентификатор для этого компонента | -| `component` | Да | Функция компонента React | -| `name` | Нет | Отображаемое имя | -| `description` | Нет | Описание того, что делает компонент | -| `isHeadless` | Нет | Установите значение `true`, если у компонента нет видимого пользовательского интерфейса (см. ниже) | - -## Размещение фронт-компонента на странице - -Помимо команд, вы можете встроить фронт-компонент непосредственно на страницу записи, добавив его как виджет в **макет страницы**. См. раздел [definePageLayout](/l/ru/developers/extend/apps/skills-and-agents#definepagelayout) для подробностей. - -## Headless и non-headless - -Фронт-компоненты поддерживают два режима отображения, управляемых опцией `isHeadless`: - -**Non-headless (по умолчанию)** — компонент отображает видимый интерфейс. При запуске из меню команд он открывается в боковой панели. Это поведение по умолчанию, когда `isHeadless` имеет значение `false` или опущен. - -**Headless (`isHeadless: true`)** — компонент монтируется невидимо в фоновом режиме. Он не открывает боковую панель. Компоненты headless предназначены для действий, которые выполняют логику и затем размонтируются — например, запуск асинхронной задачи, переход на страницу или показ модального окна подтверждения. Они естественно сочетаются с компонентами 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` предоставляет четыре вспомогательных компонента Command, предназначенных для headless фронт-компонентов. Каждый компонент выполняет действие при монтировании, обрабатывает ошибки, показывая уведомление snackbar, и автоматически размонтирует фронт-компонент по завершении. - -Импортируйте их из `twenty-sdk/command`: - -* **`Command`** — запускает асинхронный колбэк через проп `execute`. -* **`CommandLink`** — переходит по пути внутри приложения. Пропы: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — открывает модальное окно подтверждения. Если пользователь подтвердит, выполняет колбэк `execute`. Пропы: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — открывает конкретную страницу боковой панели. Пропы: `page`, `pageTitle`, `pageIcon`. - -Полный пример headless фронт-компонента, использующего `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 ; -}; - -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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, -}); -``` - -## Доступ к контексту времени выполнения - -Внутри вашего компонента используйте хуки 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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Доступные хуки: - -| Хук | Возвращает | Описание | -| --------------------------------------------- | ------------------- | ---------------------------------------------------------------------------- | -| `useUserId()` | `string` или `null` | ID текущего пользователя | -| `useSelectedRecordIds()` | `string[]` | Все выбранные идентификаторы записей (пустой массив, если ничего не выбрано) | -| `useRecordId()` | `string` или `null` | **Устарело.** Используйте `useSelectedRecordIds()` вместо этого | -| `useFrontComponentId()` | `string` | ID этого экземпляра компонента | -| `useFrontComponentExecutionContext(selector)` | различается | Доступ к полному контексту выполнения с помощью функции-селектора | - -## API взаимодействия с хостом - -Компоненты фронтенда могут вызывать навигацию, модальные окна и уведомления с помощью функций из `twenty-sdk`: - -| Функция | Описание | -| ----------------------------------------------- | -------------------------------- | -| `navigate(to, params?, queryParams?, options?)` | Перейти на страницу в приложении | -| `openSidePanelPage(params)` | Открыть боковую панель | -| `closeSidePanel()` | Закрыть боковую панель | -| `openCommandConfirmationModal(params)` | Показать диалог подтверждения | -| `enqueueSnackbar(params)` | Показать всплывающее уведомление | -| `unmountFrontComponent()` | Размонтировать компонент | -| `updateProgress(progress)` | Обновить индикатор прогресса | - -Пример, который использует API хоста для показа snackbar и закрытия боковой панели после завершения действия: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -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 ( -
-

Export {selectedRecordIds.length} selected record(s)?

- -
- ); -}; - -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, - }, -}); -``` - -## defineCommandMenuItem - -Используйте `defineCommandMenuItem`, чтобы зарегистрировать фронтенд-компонент в меню команд (Cmd+K). Если `isPinned` имеет значение `true`, команда также отображается как кнопка быстрого действия в правом верхнем углу страницы. - -```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` | Да | Стабильный уникальный идентификатор для команды | -| `label` | Да | Полная метка, отображаемая в меню команд (Cmd+K) | -| `frontComponentUniversalIdentifier` | Да | `universalIdentifier` фронтенд-компонента, который открывается этой командой | -| `shortLabel` | Нет | Короткая метка, отображаемая на закреплённой кнопке быстрого действия | -| `icon` | Нет | Имя значка, отображаемое рядом с меткой (например, `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Нет | При значении `true` показывает команду как кнопку быстрого действия в правом верхнем углу страницы | -| `availabilityType` | Нет | Определяет, где отображается команда: `'GLOBAL'` (доступна всегда), `'RECORD_SELECTION'` (только при выборе записей) или `'FALLBACK'` (показывается, когда другие команды не подходят) | -| `availabilityObjectUniversalIdentifier` | Нет | Ограничивает команду страницами определённого типа объектов (например, только для записей Company) | -| `conditionalAvailabilityExpression` | Нет | Логическое выражение для динамического управления видимостью команды (см. ниже) | - -## Выражения условной доступности - -Поле `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, - ), -}); -``` - -**Переменные контекста** — представляют текущее состояние страницы: - -| Переменная | Тип | Описание | -| ------------------------------ | --------- | ------------------------------------------------------------------------ | -| `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)` | `true`, если значение не null/undefined | -| `isNonEmptyString(value)` | `true`, если значение — непустая строка | -| `includes(array, value)` | `true`, если массив содержит значение | -| `includesEvery(array, prop, value)` | `true`, если свойство каждого элемента включает значение | -| `every(array, prop)` | `true`, если свойство истинно для каждого элемента | -| `everyDefined(array, prop)` | `true`, если свойство определено у каждого элемента | -| `everyEquals(array, prop, value)` | `true`, если свойство равно значению у каждого элемента | -| `some(array, prop)` | `true`, если свойство истинно хотя бы у одного элемента | -| `someDefined(array, prop)` | `true`, если свойство определено хотя бы у одного элемента | -| `someEquals(array, prop, value)` | `true`, если свойство равно значению хотя бы у одного элемента | -| `someNonEmptyString(array, prop)` | `true`, если свойство является непустой строкой хотя бы у одного элемента | -| `none(array, prop)` | `true`, если свойство ложно для каждого элемента | -| `noneDefined(array, prop)` | `true`, если свойство не определено ни у одного элемента | -| `noneEquals(array, prop, value)` | `true`, если свойство не равно значению ни у одного элемента | - -## Публичные ресурсы - -Компоненты фронтенда могут получать доступ к файлам из каталога приложения `public/` с помощью `getPublicAssetUrl`: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -См. [раздел о публичных ресурсах](/l/ru/developers/extend/apps/cli-and-testing#public-assets-public-folder) для подробностей. - -## Стилизация - -Компоненты фронтенда поддерживают несколько подходов к стилизации. Вы можете использовать: - -* **Встроенные стили** — `style={{ color: 'red' }}` -* **Компоненты Twenty UI** — импорт из `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar и другие) -* **Emotion** — CSS-in-JS с `@emotion/react` -* **Styled-components** — паттерны `styled.div` -* **Tailwind CSS** — утилитарные классы -* **Любая библиотека CSS-in-JS**, совместимая с React - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/getting-started.mdx deleted file mode 100644 index 063f5031bd..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Начало работы -icon: rocket -description: Создайте своё первое приложение Twenty за считанные минуты. ---- - -## Требования - -* **Node.js 24+** — [Скачать здесь](https://nodejs.org/) -* **Yarn 4** — поставляется вместе с Node.js через Corepack. Включите его, выполнив `corepack enable` -* **Docker** — [Скачать здесь](https://www.docker.com/products/docker-desktop/). Требуется для запуска локального экземпляра Twenty. Пропустите, если у вас уже запущен Twenty в другом месте. - -Создание приложения Twenty включает три фазы. Генератор каркаса объединяет их в одну команду для идеального сценария (happy path), но каждая фаза — отдельная концепция: когда что-то идёт не так, понимание того, на какой фазе вы находитесь, подскажет, что исправить. - -| Фаза | Что вы делаете | Инструмент | Результат | -| ----------------------- | ------------------------------------------------- | ----------------------------- | -------------------------------------- | -| **1. Создание каркаса** | Сгенерировать исходный код приложения | `npx create-twenty-app` | Проект TypeScript на диске | -| **2. Запустить сервер** | Запустить сервер Twenty для синхронизации | Docker + `yarn twenty server` | Запущенный экземпляр Twenty | -| **3. Синхронизация** | Синхронизируйте код с сервером в реальном времени | `yarn twenty dev` | Ваши изменения появляются в интерфейсе | - ---- - -## Фаза 1 — Сгенерируйте каркас проекта - -Создайте новое приложение из шаблона: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Вам будет предложено ввести имя и описание — нажмите **Enter**, чтобы принять значения по умолчанию. Это создаст проект TypeScript в `my-twenty-app/` с начальным файлом `application-config.ts`, ролью по умолчанию, рабочим процессом CI и интеграционным тестом. - -**После этой фазы:** у вас есть исходный код приложения на вашем компьютере. Он ещё не запущен — это фаза 2. - ---- - -## Фаза 2 — Запустите локальный сервер Twenty - -Вашему приложению нужен сервер Twenty для синхронизации. Сервер — это полноценный экземпляр Twenty — UI, GraphQL API, PostgreSQL — работающий локально в Docker. Ваш локальный код загружает свои определения на этот сервер, благодаря чему они появляются в интерфейсе. - -Генератор каркаса предложит запустить его за вас: - -> **Хотите настроить локальный экземпляр Twenty?** - -* **Да (рекомендуется)** — скачивает Docker-образ `twentycrm/twenty-app-dev` и запускает его на порту `2020`. Сначала убедитесь, что Docker запущен. -* **Нет** — выберите это, если у вас уже есть сервер Twenty, к которому вы хотите подключиться. Позже вы можете подключить его с помощью `yarn twenty remote add`. - -
- Запустить локальный экземпляр? -
- -Когда сервер будет запущен, откроется браузер для входа. Используйте предварительно созданную демонстрационную учётную запись: - -* **Электронная почта:** `tim@apple.dev` -* **Пароль:** `tim@apple.dev` - -
- Экран входа в Twenty -
- -На следующем экране нажмите **Authorize** — это даст CLI доступ к вашему рабочему пространству. - -
- Экран авторизации Twenty CLI -
- -В вашем терминале появится подтверждение, что всё настроено. - -
- Каркас приложения успешно создан -
- -**После этой фазы:** у вас запущен сервер Twenty на [http://localhost:2020](http://localhost:2020), а ваш CLI авторизован для синхронизации с ним. - - -Если Docker не установлен или не запущен, генератор каркаса подскажет правильную команду запуска для вашей ОС. Когда Docker будет запущен, вы можете продолжить с `yarn twenty server start` — заново генерировать каркас не нужно. - - ---- - -## Фаза 3 — Синхронизируйте свои изменения - -Это внутренний цикл, в котором вы проведёте большую часть времени. - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Эта команда отслеживает `src/`, пересобирает при каждом изменении и синхронизирует результат с сервером. Отредактируйте файл, сохраните — и через секунду сервер отразит изменения. В терминале появится панель текущего статуса. - -Для более подробного вывода (журналы сборки, запросы синхронизации, трассировки ошибок) добавьте `--verbose`. - -
- Вывод терминала в режиме разработки -
- -Откройте [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Вы должны увидеть своё приложение в разделе **Your Apps**. - -
- Список Your Apps с приложением My twenty app -
- -Нажмите **My twenty app**, чтобы открыть его регистрацию приложения — запись на уровне сервера, описывающую ваше приложение (имя, идентификатор, учётные данные OAuth, источник). Одну и ту же регистрацию можно установить в нескольких рабочих пространствах на одном сервере. - -
- Сведения о регистрации приложения -
- -Нажмите **View installed app**, чтобы посмотреть установку в рабочем пространстве. Вкладка **About** показывает версию и параметры управления. - -
- Установленное приложение -
- -**После этой фазы:** у вас есть интерактивный цикл разработки. Отредактируйте любой файл в `src/`, и он появится в интерфейсе. - -### Разовая синхронизация для CI и скриптов - -Передайте `--once`, чтобы выполнить одну сборку и синхронизацию и завершить работу — тот же конвейер, без наблюдателя: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Команда | Поведение | Когда использовать | -| ------------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -| `yarn twenty dev` | Отслеживает и повторно синхронизирует при каждом изменении. Продолжает работать, пока вы его не остановите. | Интерактивная локальная разработка. | -| `yarn twenty dev --once` | Одна сборка и синхронизация, завершает работу с кодом `0` при успехе и `1` при ошибке. | CI, хуки pre-commit, AI-агенты, скриптовые рабочие процессы. | - -Оба режима требуют сервер в режиме разработки и аутентифицированный удалённый сервер. - - -Режим разработки доступен только на экземплярах Twenty, запущенных в режиме разработки (`NODE_ENV=development`). Экземпляры в продакшене отклоняют запросы синхронизации из режима разработки — используйте `yarn twenty deploy` для развёртывания на производственные серверы. См. [Публикация приложений](/l/ru/developers/extend/apps/publishing). - - ---- - -## Что вы можете создать - -Приложения состоят из **сущностей** — каждая определена как файл TypeScript с одним `export default`: - -| Сущность | Что делает | -| ----------------------------- | --------------------------------------------------------------------------------------------------------- | -| **Объекты и поля** | Пользовательские модели данных (почтовая открытка, счёт и т. д.) с типизированными полями | -| **Логические функции** | Серверный TypeScript, запускаемый HTTP-маршрутами, расписаниями cron или событиями базы данных | -| **Фронтенд-компоненты** | React-компоненты, которые отображаются внутри интерфейса Twenty (боковая панель, виджеты, командное меню) | -| **Навыки и агенты** | Возможности ИИ — многократно используемые инструкции и автономные помощники | -| **Представления и навигация** | Предварительно настроенные представления списков и элементы бокового меню | -| **Макеты страниц** | Пользовательские страницы сведений о записи с вкладками и виджетами | - -Полная справка: [Создание приложений](/l/ru/developers/extend/apps/building). - -## Структура проекта - -```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/` | Статические ресурсы (изображения, шрифты), обслуживаемые вместе с вашим приложением. | - -### Начните с примера - -Используйте `--example`, чтобы начать с более полного проекта (пользовательские объекты, поля, логические функции, фронтенд-компоненты): - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Примеры берутся из каталога [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) на GitHub. Вы также можете сгенерировать каркас отдельных сущностей в существующем проекте с помощью `yarn twenty add` — см. [Создание приложений](/l/ru/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add). - ---- - -## Управление локальным сервером - -Используйте `yarn twenty server` для управления локальным контейнером Twenty: - -| Команда | Что делает | -| -------------------------------------- | ---------------------------------------------------- | -| `yarn twenty server start` | Запустить сервер (при необходимости скачивает образ) | -| `yarn twenty server start --port 3030` | Запустить на пользовательском порту | -| `yarn twenty server stop` | Остановить сервер (данные сохраняются) | -| `yarn twenty server status` | Показать URL, версию и учётные данные для входа | -| `yarn twenty server logs` | Потоковый вывод журналов сервера | -| `yarn twenty server reset` | Стереть данные и начать заново | -| `yarn twenty server upgrade` | Скачать последний образ `twenty-app-dev` | -| `yarn twenty server upgrade 2.2.0` | Обновить до конкретной версии | - -Данные сохраняются между перезапусками в двух томах Docker (`twenty-app-dev-data` для PostgreSQL, `twenty-app-dev-storage` для файлов). Используйте `reset`, чтобы стереть всё. - -### Обновление образа сервера - -`yarn twenty server upgrade` скачивает последний образ, сравнивает дайджесты и пересоздаёт контейнер только если действительно что-то изменилось. Ваши тома данных сохраняются — заменяется только контейнер. Если был скачан новый образ и контейнер работал, при обновлении автоматически запускается новый контейнер; затем выполните `yarn twenty server start`, чтобы дождаться его готовности. - -```bash filename="Terminal" -yarn twenty server upgrade # Latest -yarn twenty server upgrade 2.2.0 # Specific version -``` - -Проверьте запущенную версию с помощью `yarn twenty server status` (эта команда показывает `APP_VERSION`, встроенную в контейнер). - -### Запуск параллельного тестового экземпляра - -Передайте `--test` любой команде `server`, чтобы управлять вторым, полностью изолированным экземпляром — это полезно для запуска интеграционных тестов или экспериментов, не затрагивая ваши основные данные разработки. - -| Команда | Что делает | -| ----------------------------------- | ------------------------------------------------------- | -| `yarn twenty server start --test` | Запустить тестовый экземпляр (по умолчанию — порт 2021) | -| `yarn twenty server stop --test` | Остановить его | -| `yarn twenty server status --test` | Показать его статус | -| `yarn twenty server logs --test` | Транслировать его журналы | -| `yarn twenty server reset --test` | Стереть его данные | -| `yarn twenty server upgrade --test` | Обновить его образ | - -Тестовый экземпляр запускается в собственном контейнере Docker (`twenty-app-dev-test`) с выделенными томами (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) и собственной конфигурацией, поэтому он может работать параллельно с вашим основным экземпляром без конфликтов. Совместите `--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 server start` и остальные команды. - - -Не устанавливайте `twenty-sdk` глобально — фиксируйте версию в каждом проекте, чтобы каждое приложение использовало свою собственную версию. - - ---- - -## Устранение неполадок - -* **Ошибки Docker** — убедитесь, что Docker Desktop (или демон) запущен перед выполнением `yarn twenty server start`. Сообщение об ошибке укажет правильную команду запуска для вашей ОС. -* **Неподходящая версия Node** — нужна 24+. Проверьте с помощью `node -v`. -* **Отсутствует Yarn 4** — выполните `corepack enable`. -* **Зависимости повреждены** — `rm -rf node_modules && yarn install`. - -Застряли? Попросите помощи на [Discord-сервере Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/layout.mdx deleted file mode 100644 index 6492175fea..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Макет -description: Определите представления, пункты меню навигации и макеты страниц, чтобы сформировать внешний вид вашего приложения в Twenty. -icon: table-columns ---- - -Сущности макета определяют, как ваше приложение представлено в интерфейсе Twenty — что находится в боковой панели, какие сохранённые представления поставляются с приложением и как устроена страница сведений о записи. - -## Концепции макета - -| Понятие | Что определяет | Сущность | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -------------------------- | -| **Представление** | Сохранённая конфигурация списка для объекта — видимые поля, порядок, фильтры, группы | `defineView` | -| **Пункт меню навигации** | Элемент в левой боковой панели, который ссылается на представление или внешний URL | `defineNavigationMenuItem` | -| **Макет страницы** | Вкладки и виджеты, из которых состоит страница сведений о записи | `definePageLayout` | -| **Вкладка компоновки страницы** | Отдельная вкладка, прикреплённая к существующей компоновке страницы (стандартной или созданной в вашем приложении) | `definePageLayoutTab` | - -Представления, пункты меню навигации и макеты страниц ссылаются друг на друга по `universalIdentifier`: - -* **Пункт меню навигации** типа `VIEW` указывает на идентификатор `defineView`, поэтому ссылка в боковой панели открывает это сохранённое представление. -* **Макет страницы** типа `RECORD_PAGE` ориентирован на объект и может встраивать [фронт-компоненты](/l/ru/developers/extend/apps/front-components) во вкладки в качестве виджетов. - - - - -Представления — это сохранённые конфигурации отображения записей объекта: какие поля видны, их порядок, а также применённые фильтры и группы. Используйте `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` указывает, к какому объекту применяется это представление. -* `key` определяет тип представления (например, `ViewKey.INDEX` для основного списка). -* `fields` управляет тем, какие столбцы отображаются и в каком порядке. Каждое поле ссылается на `fieldMetadataUniversalIdentifier`. -* Также вы можете определить `filters`, `filterGroups`, `groups` и `fieldGroups` для более продвинутых конфигураций. -* `position` управляет порядком, когда для одного и того же объекта существует несколько представлений. - - - - -Пункты навигационного меню добавляют пользовательские элементы в боковую панель рабочего пространства. Используйте `defineNavigationMenuItem()` для ссылок на представления, внешние 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` определяет, на что ссылается пункт меню: `NavigationMenuItemType.VIEW` для сохранённого представления или `NavigationMenuItemType.LINK` для внешнего URL. -* Для ссылок на представления укажите `viewUniversalIdentifier`. Для внешних ссылок укажите `link`. -* `position` управляет порядком в боковой панели. -* `icon` и `color` (необязательно) настраивают внешний вид. - - - - -Макеты страниц позволяют настраивать вид страницы с деталями записи: какие вкладки отображаются, какие виджеты внутри каждой вкладки и как они расположены. Используйте `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` внутри вкладки может отображать компонент фронтенда, список связей или другие встроенные типы виджетов. -* `position` у вкладок управляет их порядком. Используйте большие значения (например, 50), чтобы разместить пользовательские вкладки после встроенных. - - - - -`definePageLayoutTab` позволяет вашему приложению прикрепить одну вкладку — с необязательными виджетами — к **существующему** макету страницы. Самый распространённый сценарий — добавление пользовательской вкладки (например, вкладки с аналитикой или сводкой ИИ) к одной из встроенных страниц записей Twenty или к макету страницы, который уже поставляется вашим приложением. - -Целевой макет страницы должен быть либо **стандартным** макетом страницы Twenty, либо определённым **вашим собственным приложением**; ссылки между приложениями на макеты страниц, принадлежащие другому установленному приложению, пока не поддерживаются. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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` является **обязательным** при использовании `definePageLayoutTab` и должен указывать на макет страницы, который уже существует на момент установки (стандартный или вашего приложения). Если родительский макет страницы отсутствует, установка завершается с понятной ошибкой проверки. -* `widgets` ограничены только этой вкладкой — они ссылаются на компоненты фронтенда, представления и т. п. точно так же, как виджеты, определённые непосредственно в `definePageLayout`. -* `position` управляет порядком относительно существующих вкладок в целевом макете. Выберите значение, которое поместит вашу вкладку в нужное место относительно встроенных вкладок. -* Используйте это вместо `definePageLayout`, когда вы хотите только **добавить** к существующему макету. Используйте `definePageLayout`, когда вы владеете всем макетом (обычно это `RECORD_PAGE` для объекта, который вы поставляете в своём приложении, или `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index 4e337f2a0c..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,565 +0,0 @@ ---- -title: Логические функции -description: Определяйте серверные функции на TypeScript с триггерами HTTP, cron и событиями базы данных. -icon: bolt ---- - -Функции логики — это серверные функции на TypeScript, которые выполняются на платформе Twenty. Их можно запускать HTTP-запросами, расписаниями cron или событиями базы данных — а также предоставлять как инструменты для ИИ-агентов. - - - - -Каждый файл функции использует `defineLogicFunction()` для экспорта конфигурации с обработчиком и необязательными триггерами. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Доступные типы триггеров: -* **httpRoute**: Публикует вашу функцию по HTTP-пути и методу **под конечной точкой `/s/`**: -> например, `path: '/post-card/create'` вызывается по адресу `https://your-twenty-server.com/s/post-card/create` -* **cron**: Запускает вашу функцию по расписанию с использованием выражения CRON. -* **databaseEvent**: Запускается при событиях жизненного цикла объектов рабочего пространства. Когда операция события — `updated`, можно указать конкретные поля для отслеживания в массиве `updatedFields`. Если оставить не заданным или пустым, любое обновление будет вызывать функцию. -> например, `person.updated`, `*.created`, `company.*` - - -Вы также можете вручную выполнить функцию с помощью CLI: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Вы можете просматривать логи с помощью: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Полезная нагрузка триггера маршрута - -Когда триггер маршрута вызывает вашу логическую функцию, она получает объект `RoutePayload`, который соответствует [формату AWS HTTP API v2](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). -Импортируйте тип `RoutePayload` из `twenty-sdk`: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -Тип `RoutePayload` имеет следующую структуру: - - | Свойство | Тип | Описание | Пример | - | ---------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | HTTP-заголовки (только перечисленные в `forwardedRequestHeaders`) | см. раздел ниже | - | `queryStringParameters` | `Record\` | Параметры строки запроса (несколько значений объединяются запятыми) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Параметры пути, извлечённые из шаблона маршрута | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Разобранное тело запроса (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | Исходное тело запроса в кодировке UTF-8, до разбора JSON. Полезно для проверки подписей вебхуков в стиле HMAC (например, `X-Hub-Signature-256` от GitHub, Stripe). `undefined`, если среда выполнения не сохранила его. | | - | `isBase64Encoded` | `boolean` | Является ли тело закодированным в 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 }; -}; -``` - - -Имена заголовков приводятся к нижнему регистру. Обращайтесь к ним, используя ключи в нижнем регистре (например, `event.headers['content-type']`). - - -#### Предоставление функции в качестве инструмента ИИ или действия рабочего процесса - -Функции логики могут быть представлены в двух интерфейсах, у каждого — свой триггер: - -* **`toolTriggerSettings`** — делает функцию обнаруживаемой для возможностей ИИ Twenty (чат, MCP, вызов функций). Использует стандартную JSON Schema — формат, который модели LLM изначально понимают. -* **`workflowActionTriggerSettings`** — делает функцию доступной как шаг в визуальном конструкторе рабочих процессов. Использует расширенную `InputSchema` от Twenty, чтобы конструктор мог отрисовывать корректные редакторы полей, селекторы переменных и подписи. - -Функция может выбрать один, другой или оба варианта. Они идут рядом с `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` — обе необязательны. Если они опущены, конструктор манифеста выводит их из исходного кода обработчика (JSON Schema — для инструмента ИИ, `InputSchema` от Twenty — для действия рабочего процесса). Укажите её явно, когда вам нужна более богатая типизация — например, с полями, учитывающими `FieldMetadataType`, такими как `CURRENCY` или `RELATION`, для конструктора рабочих процессов, или с полями `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'], - }, - }, -}); -``` - - -**Напишите хорошее описание в поле `description`.** Агенты ИИ опираются на поле `description` функции, чтобы решить, когда использовать инструмент. Чётко опишите, что делает инструмент и когда его следует вызывать. - - - - - -Послеустановочная функция — это функция логики, которая автоматически выполняется после завершения установки вашего приложения в рабочем пространстве. Сервер выполняет её **после** того, как метаданные приложения синхронизированы и клиент SDK сгенерирован, так что рабочее пространство полностью готово к использованию, а новая схема уже применена. Типичные сценарии использования включают предзаполнение данных по умолчанию, создание начальных записей, настройку параметров рабочего пространства или выделение ресурсов в сторонних сервисах. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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 exec --postInstall -``` - -Основные моменты: -* Послеустановочные функции используют `definePostInstallLogicFunction()` — специализированный вариант, который опускает настройки триггеров (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`). -* Обработчик получает `InstallPayload` с `{ previousVersion?: string; newVersion: string }` — `newVersion` — это устанавливаемая версия, а `previousVersion` — версия, установленная ранее (или `undefined` при чистой установке). Используйте эти значения, чтобы отличать чистые установки от обновлений и запускать логику миграции, зависящую от версии. -* **Когда запускается хук**: по умолчанию только при чистой установке. Передайте `shouldRunOnVersionUpgrade: true`, если хотите, чтобы он также выполнялся при обновлении приложения с предыдущей версии. Если флаг опущен, по умолчанию он равен `false`, и при обновлении хук пропускается. -* **Модель выполнения — по умолчанию асинхронно, синхронный режим по выбору**: флаг `shouldRunSynchronously` определяет, *как* выполняется post-install. - * `shouldRunSynchronously: false` *(по умолчанию)* — хук **помещается в очередь сообщений** с `retryLimit: 3` и выполняется асинхронно в воркере. Ответ на установку возвращается сразу после постановки задания в очередь, поэтому медленный или дающий сбой обработчик не блокирует вызывающую сторону. Воркер выполнит до трёх повторных попыток. **Используйте это для длительных задач** — наполнение большими наборами данных, вызовы медленных сторонних API, подготовка внешних ресурсов — всего, что может выйти за разумное окно ответа HTTP. - * `shouldRunSynchronously: true` — хук выполняется **непосредственно в процессе установки** (тот же исполнитель, что и для pre-install). Запрос установки блокируется, пока обработчик не завершится, и если он генерирует исключение, вызывающая сторона установки получает `POST_INSTALL_ERROR`. Автоматических повторов нет. **Используйте это для быстрых задач, которые должны завершиться до отправки ответа** — например, выдача ошибки валидации пользователю или быстрая настройка, на которую клиент будет полагаться сразу после возврата вызова установки. Имейте в виду, что к моменту запуска post-install миграция метаданных уже применена, поэтому сбой в синхронном режиме **не** откатывает изменения схемы — он лишь выявляет ошибку. -* Убедитесь, что ваш обработчик идемпотентен. В асинхронном режиме очередь может выполнить до трёх повторных попыток; в любом режиме хук может запускаться снова при обновлениях, когда `shouldRunOnVersionUpgrade: true`. -* Переменные окружения `APPLICATION_ID`, `APP_ACCESS_TOKEN` и `API_URL` доступны внутри обработчика (как и в любой другой логической функции), поэтому вы можете вызывать API Twenty с токеном доступа приложения, ограниченным вашим приложением. -* Для каждого приложения допускается только одна послеустановочная функция. Сборка манифеста завершится ошибкой, если будет обнаружено более одной такой функции. -* Параметры функции `universalIdentifier`, `shouldRunOnVersionUpgrade` и `shouldRunSynchronously` автоматически добавляются в манифест приложения в поле `postInstallLogicFunction` во время сборки — вам не нужно указывать их в `defineApplication()`. -* Тайм-аут по умолчанию установлен на 300 секунд (5 минут), чтобы позволить выполнять более длительные задачи настройки, такие как инициализация данных. -* **Не выполняется в режиме разработки**: когда приложение зарегистрировано локально (через `yarn twenty dev`), сервер полностью пропускает процесс установки и синхронизирует файлы напрямую через наблюдатель CLI — поэтому post-install никогда не запускается в режиме разработки, независимо от `shouldRunSynchronously`. Используйте `yarn twenty exec --postInstall`, чтобы запустить это вручную для запущенного рабочего пространства. - - - - -Функция pre-install — это логическая функция, которая автоматически выполняется во время установки, **до применения миграции метаданных рабочего пространства**. Она использует ту же структуру полезной нагрузки, что и post-install (`InstallPayload`), но находится раньше в процессе установки, чтобы подготовить состояние, от которого зависит предстоящая миграция, — типичные сценарии включают резервное копирование данных, проверку совместимости с новой схемой или архивирование записей, которые будут реструктурированы или удалены. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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 exec --preInstall -``` - -Основные моменты: -* Функции pre-install используют `definePreInstallLogicFunction()` — та же специализированная конфигурация, что и у post-install, только привязанная к другому этапу жизненного цикла. -* И обработчики pre-, и post-install получают один и тот же тип `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Импортируйте его один раз и используйте повторно в обоих хуках. -* **Когда запускается хук**: выполняется непосредственно перед миграцией метаданных рабочего пространства (`synchronizeFromManifest`). Перед выполнением сервер запускает чисто добавочную «урезанную синхронизацию», которая регистрирует в метаданных рабочего пространства pre-install функцию **новой** версии — ничего больше не затрагивается — а затем выполняет её. Поскольку эта синхронизация только добавляет, объекты, поля и данные предыдущей версии остаются нетронутыми к моменту запуска вашего обработчика: вы можете безопасно читать и сохранять состояние до миграции. -* **Модель выполнения**: pre-install выполняется **синхронно** и **блокирует установку**. Если обработчик генерирует исключение, установка прерывается до применения каких-либо изменений схемы — рабочее пространство остаётся на предыдущей версии в согласованном состоянии. Это сделано намеренно: pre-install — ваш последний шанс отказать в рискованном обновлении. -* Как и в случае с post-install, для каждого приложения допускается только одна предустановочная функция. Она автоматически добавляется в манифест приложения в поле `preInstallLogicFunction` во время сборки. -* **Не выполняется в режиме разработки**: как и post-install, процесс установки полностью пропускается для локально зарегистрированных приложений, поэтому pre-install никогда не запускается при `yarn twenty dev`. Используйте `yarn twenty exec --preInstall`, чтобы запустить это вручную. - - - - -Оба хука являются частью одного и того же процесса установки и получают один и тот же `InstallPayload`. Разница в том, **когда** они запускаются относительно миграции метаданных рабочего пространства, и это определяет, к каким данным можно безопасно обращаться. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -Pre-install всегда **синхронный** (он блокирует установку и может её прервать). Post-install **по умолчанию асинхронный** — ставится в очередь воркера с автоматическими повторами — но может перейти к синхронному выполнению с `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 => { - 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`, когда миграция в противном случае уничтожит или повредит существующие данные.** Поскольку 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 => { - // 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` (по умолчанию) | - - -Если сомневаетесь, выбирайте по умолчанию **post-install**. Обращайтесь к pre-install только тогда, когда сама миграция разрушительна и вам нужно перехватить предыдущее состояние, прежде чем оно исчезнет. - - - - - -## Типизированные клиенты API (twenty-client-sdk) - -Пакет `twenty-client-sdk` предоставляет два типизированных клиента GraphQL для взаимодействия с API Twenty из ваших логических функций и фронт-компонентов. - -| Клиент | Импорт | Конечная точка | Генерируется? | -| ------------------- | ---------------------------- | ----------------------------------------------------------------- | -------------------------------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — данные рабочего пространства (записи, объекты) | Да, на этапе dev/build | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — конфигурация рабочего пространства, загрузка файлов | Нет, поставляется в готовом виде | - - - - -`CoreApiClient` — основной клиент для запросов и изменений данных рабочего пространства. Он **генерируется из схемы вашего рабочего пространства** во время `yarn twenty dev` или `yarn twenty 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` для аргументов и вкладывайте объекты для отношений. Вы получаете полное автодополнение и проверку типов на основе схемы вашего рабочего пространства. - - -**CoreApiClient генерируется на этапе dev/build.** Если вы используете его, не запустив сначала `yarn twenty dev` или `yarn twenty build`, он выбросит ошибку. Генерация происходит автоматически — CLI анализирует GraphQL-схему вашего рабочего пространства и создает типизированный клиент с помощью `@genql/cli`. - - -#### Использование CoreSchema для аннотаций типов - -`CoreSchema` предоставляет типы TypeScript, соответствующие объектам вашего рабочего пространства — это полезно для типизации состояния компонентов или параметров функций: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`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` поля (а не его идентификатор, специфичный для рабочего пространства), поэтому ваш код загрузки будет работать в любом рабочем пространстве, где установлено ваше приложение. -* Возвращаемый `url` — это подписанный URL, который можно использовать для доступа к загруженному файлу. - - - - - - Когда ваш код выполняется на Twenty (логические функции или фронт-компоненты), платформа предоставляет учётные данные в виде переменных окружения: - - * `TWENTY_API_URL` — базовый URL API Twenty - * `TWENTY_APP_ACCESS_TOKEN` — краткоживущий ключ, ограниченный ролью функции по умолчанию вашего приложения - - Вам не нужно передавать их клиентам — они автоматически читаются из `process.env`. Права ключа API определяются ролью, указанной в `defaultRoleUniversalIdentifier` в вашем `application-config.ts`. - diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/publishing.mdx deleted file mode 100644 index 067eff33bc..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Публикация -icon: загрузить -description: Распространяйте своё приложение Twenty в маркетплейсе или разверните его для внутреннего использования. ---- - -## Обзор - -После того как ваше приложение [собрано и протестировано локально](/l/ru/developers/extend/apps/building), у вас есть два пути для его распространения: - -* **Разверните tar-архив** — загрузите своё приложение напрямую на конкретный сервер Twenty для внутреннего или частного использования. -* **Опубликовать в npm** — разместите ваше приложение в маркетплейсе Twenty, чтобы любое рабочее пространство могло его найти и установить. - -Оба пути начинаются с одного и того же шага **build**. - -## Сборка вашего приложения - -Выполните команду сборки, чтобы скомпилировать приложение и сгенерировать готовый к распространению `manifest.json`: - -```bash filename="Terminal" -yarn twenty build -``` - -Это компилирует исходные файлы TypeScript, транспилирует функции логики и фронтенд-компоненты и записывает всё в `.twenty/output/`. Добавьте `--tarball`, чтобы также создать пакет `.tgz` для ручного распространения или для команды deploy. - -## Развертывание на сервер (tarball) - -Для приложений, которые вы не хотите делать общедоступными — собственные инструменты, интеграции только для предприятий или экспериментальные сборки — вы можете развернуть tarball напрямую на сервер Twenty. - -### Требования - -Перед развертыванием вам нужен настроенный remote, указывающий на целевой сервер. Remotes локально хранят URL сервера и учётные данные аутентификации в `~/.twenty/config.json`. - -Добавьте remote: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Развертывание - -Соберите и загрузите ваше приложение на сервер в одном шаге: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Общий доступ к развернутому приложению - - -Совместный доступ к частным приложениям (tarball) между рабочими пространствами — функция уровня **Enterprise**. Вкладка **Distribution** будет показывать предложение обновиться вместо элементов управления совместным доступом, пока в вашем рабочем пространстве не будет действительного ключа Enterprise. Перейдите в [Настройки > Панель администратора > Enterprise](/settings/admin-panel#enterprise), чтобы включить её. - - -Приложения в формате tarball не отображаются в публичном маркетплейсе, поэтому другие рабочие пространства на том же сервере не найдут их при просмотре. Как только ваше рабочее пространство перейдёт на тарифный план Enterprise, вы сможете поделиться развёрнутым приложением следующим образом: - -1. Перейдите в **Настройки > Приложения > Регистрации** и откройте ваше приложение -2. На вкладке **Распространение** нажмите **Копировать ссылку для общего доступа** -3. Поделитесь этой ссылкой с пользователями в других рабочих пространствах — она ведёт их прямо на страницу установки приложения - -Ссылка общего доступа использует базовый URL сервера (без какого-либо поддомена рабочего пространства), поэтому она работает для любого рабочего пространства на сервере. - -### Управление версиями - -When updating an already deployed tarball app, the server requires the `version` in `package.json` to be **strictly higher** (per [semver](https://semver.org) ordering) than the currently deployed version. Повторное развёртывание той же версии или публикация более низкой версии отклоняются до сохранения tarball — в CLI вы увидите ошибку `VERSION_ALREADY_EXISTS`. - -Чтобы выпустить обновление: - -1. Увеличьте значение поля `version` в вашем `package.json` (например: `1.2.3` → `1.2.4`, `1.3.0` или `2.0.0`). -2. Выполните `yarn twenty deploy` (или `yarn twenty deploy --remote production`) -3. Рабочие пространства, в которых установлено приложение, увидят доступное обновление в своих настройках - - -Пререлизные теги работают как ожидается: повышение версии `1.0.0-rc.1` → `1.0.0-rc.2` допускается, а финальный релиз вроде `1.0.0` корректно распознаётся как более высокий, чем `1.0.0-rc.5`. Версия в `package.json` должна сама по себе быть корректной строкой semver. - - -{/* TODO: add screenshot of the Upgrade button */} - -### Совместимость версий сервера - -Если ваше приложение использует функцию, появившуюся в конкретной версии сервера Twenty (например, провайдеры OAuth, добавленные в v2.3.0), следует объявить минимальную требуемую версию сервера с помощью поля `engines.twenty` в `package.json`: - -```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` не задано, проверка пропускается. - - -Сервер выполняет окончательную проверку — он проверяет `engines.twenty` как при загрузке tarball-архива, так и при установке в рабочем пространстве. Если вы развёртываете tarball вне стандартного процесса или устанавливаете из маркетплейса, сервер всё равно принудительно проверяет совместимость. - - -## Автоматизированный CI/CD (рабочие процессы, сгенерированные шаблоном) - -Приложения, созданные с помощью `create-twenty-app`, «из коробки» включают два рабочих процесса GitHub Actions в каталоге `.github/workflows/`. Они готовы к запуску, как только вы запушите репозиторий на GitHub — для CI не требуется дополнительной настройки, а для CD нужен лишь один секрет. - -### CI — `ci.yml` - -Автоматически запускает интеграционные тесты при каждом пуше в `main` и для каждого pull request. - -**Что делает:** - -1. Извлекает исходный код вашего приложения. -2. Запускает изолированный тестовый экземпляр Twenty с помощью составного действия `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (эквивалент для CI `yarn twenty server start --test`). -3. Включает Corepack, настраивает Node.js на основе вашего `.nvmrc` и устанавливает зависимости с помощью `yarn install --immutable`. -4. Запускает `yarn test`, передавая `TWENTY_API_URL` и `TWENTY_API_KEY` из запущенного экземпляра, чтобы ваши тесты могли взаимодействовать с реальным сервером. - -**Параметры конфигурации:** - -* `TWENTY_VERSION` (переменная окружения, по умолчанию `latest`) — зафиксируйте версию сервера Twenty, используемую в CI, отредактировав это значение в `ci.yml`. -* Параллельные запуски группируются по `github.ref` и отменяют выполняющиеся прогоны при новых пушах. - -Секреты не требуются — тестовый экземпляр эфемерен и существует только на время выполнения задания. - -### CD — `cd.yml` - -Разворачивает ваше приложение на настроенном сервере Twenty при каждом пуше в `main` и, при необходимости, из pull request при наличии метки `deploy`. - -**Что делает:** - -1. Извлекает head-коммит PR (для PR с меткой) или запушенный коммит. -2. Запускает `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — эквивалент для CI `yarn twenty deploy`. -3. Запускает `twentyhq/twenty/.github/actions/install-twenty-app@main`, чтобы новая развернутая версия была установлена в целевое рабочее пространство. - -**Обязательная конфигурация:** - -| Настройка | Где | Назначение | -| ----------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `env` в `cd.yml` (по умолчанию `http://localhost:3000`) | Сервер Twenty, на который выполняется деплой. Перед первым использованием замените это на URL вашего реального сервера. | -| `TWENTY_DEPLOY_API_KEY` | В репозитории GitHub — **Settings → Secrets and variables → Actions** | Ключ API с правом деплоя на целевом сервере. | - - -Значение `TWENTY_DEPLOY_URL` по умолчанию — `http://localhost:3000` — это заглушка: с хостируемого GitHub раннера к ней не будет доступа. Перед включением CD замените его на публичный URL вашего сервера (или используйте self-hosted раннер с сетевым доступом). - - -**Запуск предварительного деплоя из PR:** - -Добавьте к pull request метку `deploy`. Условие `if:` в `cd.yml` запустит задачу для этого PR, используя его head-коммит, что позволит проверить изменение на целевом сервере до слияния. - -### Закрепление версий повторно используемых действий - -Оба рабочих процесса ссылаются на повторно используемые действия с указанием `@main`, поэтому обновления действий в репозитории `twentyhq/twenty` подхватываются автоматически. Если вам нужны детерминированные сборки, замените `@main` на SHA коммита или тег релиза в каждой строке `uses:`. - -## Публикация в npm - -Публикация в npm делает ваше приложение видимым в маркетплейсе Twenty. Любое рабочее пространство Twenty может просматривать, устанавливать и обновлять приложения из маркетплейса непосредственно из интерфейса. - -### Требования - -* Учётная запись [npm](https://www.npmjs.com) -* Ключевое слово `twenty-app` в массиве `keywords` вашего `package.json` (добавьте его вручную — по умолчанию оно не включено в шаблон `create-twenty-app`) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Метаданные маркетплейса - -Конфигурация `defineApplication()` поддерживает необязательные поля, которые определяют, как ваше приложение отображается в маркетплейсе. Используйте `logoUrl` и `screenshots`, чтобы ссылаться на изображения из папки `public/`: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -См. [аккордеон defineApplication](/l/ru/developers/extend/apps/building#defineentity-functions) на странице «Создание приложений» для полного списка полей маркетплейса (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl` и т. д.). - -#### Рекомендуемые размеры скриншотов - -Маркетплейс отображает `screenshots` в контейнере с фиксированным соотношением сторон `8:5` (например, `1600×1000 px`). - - -Скриншоты с любым соотношением сторон отображаются полностью и никогда не обрезаются, но всё, что значительно выше или уже, чем `8:5`, будет иметь пустые поля по бокам. - - -### Публикация - -```bash filename="Terminal" -yarn twenty publish -``` - -Чтобы опубликовать с определённым dist-tag (например, `beta` или `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### Как работает обнаружение приложений в маркетплейсе - -Сервер Twenty синхронизирует каталог маркетплейса из реестра npm **каждый час**. - -Вы можете запустить синхронизацию немедленно, вместо ожидания: - -```bash filename="Terminal" -yarn twenty server catalog-sync -# To target a specific remote: -# yarn twenty server catalog-sync --remote production -``` - -Метаданные, отображаемые в маркетплейсе, берутся из конфигурации `defineApplication()` — из таких полей, как `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` и `termsUrl`. - - -Если ваше приложение не определяет `aboutDescription` в `defineApplication()`, маркетплейс автоматически использует `README.md` вашего пакета из npm в качестве содержимого страницы «О приложении». Это означает, что вы можете поддерживать единый README как для npm, так и для маркетплейса Twenty. Если вы хотите другое описание в маркетплейсе, явно задайте `aboutDescription`. - - -### Публикация через CI - -Используйте этот workflow 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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Для других CI-систем (GitLab CI, CircleCI и др.) применимы те же три команды: `yarn install`, `yarn twenty build`, затем `npm publish` из `.twenty/output`. - - -**npm provenance** — опционально, но рекомендуется. Публикация с флагом `--provenance` добавляет к вашему пакету в npm значок доверия, позволяя пользователям проверить, что пакет был собран из конкретного коммита в общедоступном конвейере CI. См. инструкции по настройке в [документации по npm provenance](https://docs.npmjs.com/generating-provenance-statements). - - -## Установка приложений - -После публикации приложения (npm) или его развертывания (tarball) рабочие пространства могут установить его через интерфейс. - -Перейдите на страницу **Настройки > Приложения** в Twenty, где можно просматривать и устанавливать как приложения из маркетплейса, так и развернутые через tarball. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Вы также можете устанавливать приложения из командной строки: - -```bash filename="Terminal" -yarn twenty install -``` - - -Сервер при установке применяет версионирование semver, аналогичное правилам при развёртывании: - -* Установка той же версии, которая уже установлена в вашем рабочем пространстве, отклоняется с ошибкой `APP_ALREADY_INSTALLED`. -* Установка версии ниже текущей отклоняется с ошибкой `CANNOT_DOWNGRADE_APPLICATION`. - -Чтобы установить более новую версию, сначала разверните или опубликуйте её, затем снова выполните `yarn twenty install`. - diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index bf9be23fe4..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Навыки и агенты -description: Определите навыки и агентов ИИ для вашего приложения. -icon: robot ---- - - - Навыки и агенты сейчас проходят альфа-тестирование. Функция работает, но продолжает развиваться. - - -Приложения могут определять возможности ИИ, которые находятся внутри рабочего пространства — повторно используемые инструкции для навыков и агенты с настраиваемыми системными подсказками. - - - - -Навыки определяют многократно используемые инструкции и возможности, которые агенты ИИ могут использовать в вашем рабочем пространстве. Используйте `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` содержит инструкции навыка — это текст, который использует агент ИИ. -* `icon` (необязательно) задаёт значок, отображаемый в UI. -* `description` (необязательно) предоставляет дополнительный контекст о назначении навыка. - - - - -Агенты — это ИИ-помощники, работающие в вашем рабочем пространстве. Используйте `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` (необязательно) переопределяет модель ИИ по умолчанию, используемую агентом. - - - diff --git a/packages/twenty-docs/l/ru/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/ru/developers/extend/capabilities/apps.mdx deleted file mode 100644 index dccbcbc8d7..0000000000 --- a/packages/twenty-docs/l/ru/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Приложения Twenty -description: Создавайте и управляйте настройками Twenty в виде кода. ---- - - -Приложения сейчас проходят альфа-тестирование. Функция работает, но продолжает развиваться. - - -## Что такое приложения? - -Приложения позволяют расширять Twenty с помощью пользовательских объектов, полей, логических функций, фронтенд-компонентов, навыков ИИ и многого другого — всё это управляется как код. Вместо настройки всего через интерфейс вы определяете модель данных и логику на TypeScript и развёртываете её в одном или нескольких рабочих пространствах. - -**Что вы можете создать:** - -* **Пользовательские объекты и поля** — расширяйте модель данных новыми сущностями или добавляйте поля к существующим объектам, таким как Company или Person -* **Логические функции** — серверные функции, запускаемые событиями базы данных, расписаниями cron или HTTP-маршрутами -* **Фронтенд-компоненты** — компоненты React, которые отображаются в интерфейсе Twenty (страницы записей, командное меню, боковые панели) -* **Навыки и агенты ИИ** — расширяйте ИИ Twenty пользовательскими возможностями -* **Представления и навигация** — преднастроенные сохранённые представления и ссылки боковой панели - -## Быстрый старт - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Это создаёт каркас нового приложения, при необходимости запускает локальный сервер Twenty и начинает отслеживать изменения в ваших файлах. См. руководство [Начало работы](/l/ru/developers/extend/apps/getting-started) для полного пошагового разбора. - -## Подробные руководства - -| Руководство | Описание | -| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [Начало работы](/l/ru/developers/extend/apps/getting-started) | Создание каркаса приложения, настройка локального сервера, структура проекта, CI | -| [Создание приложений](/l/ru/developers/extend/apps/building) | Определения сущностей (`defineObject`, `defineLogicFunction`, `defineFrontComponent` и т. д.), клиенты API, пакеты npm, публичные ассеты, тестирование | -| [Публикация](/l/ru/developers/extend/apps/publishing) | Развёртывание на сервер, публикация в npm и маркетплейсе | - -## Ключевые понятия - -### Обнаружение сущностей - -SDK обнаруживает сущности, сканируя ваши файлы TypeScript в поисках вызовов `export default define({...})`. Имена файлов и структура папок гибкие — обнаружение основано на AST, а не на путях. - -### Доступные типы сущностей - -| Функция | Назначение | -| ---------------------------------- | ------------------------------------------------------------ | -| `defineApplication()` | Метаданные приложения (обязательно, по одному на приложение) | -| `defineObject()` | Пользовательские объекты с полями | -| `defineField()` | Поля в существующих объектах | -| `defineLogicFunction()` | Серверная логика с триггерами | -| `defineFrontComponent()` | Компоненты React в интерфейсе Twenty | -| `defineRole()` | Роли доступа | -| `defineView()` | Конфигурации сохранённых представлений | -| `defineNavigationMenuItem()` | Ссылки боковой панели навигации | -| `defineSkill()` | Навыки агента ИИ | -| `defineAgent()` | ИИ-агенты с промптами | -| `definePageLayout()` | Пользовательские макеты страниц записей | -| `definePreInstallLogicFunction()` | Выполняется перед установкой приложения | -| `definePostInstallLogicFunction()` | Выполняется после установки приложения | - -### Рабочий процесс разработки - -1. **`yarn twenty dev`** — следит за исходными файлами, пересобирает при изменениях, синхронизируется с сервером, генерирует типизированные клиенты API -2. **`yarn twenty build`** — создаёт дистрибутивную сборку -3. **`yarn twenty deploy`** — развёртывает на удалённый сервер Twenty -4. **`yarn twenty add`** — интерактивно создаёт каркас новой сущности - -### Справочник по CLI - -```bash filename="Terminal" -yarn twenty help # Показать все команды -yarn twenty server start # Запустить локальный сервер разработки -yarn twenty remote add # Подключиться к серверу Twenty -yarn twenty exec -n fn # Выполнить логическую функцию -yarn twenty logs -n fn # Транслировать логи функции -``` - -См. руководство [Начало работы](/l/ru/developers/extend/apps/getting-started) для полной справки по CLI. diff --git a/packages/twenty-docs/l/ru/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/ru/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index b8ec5f2edf..0000000000 --- a/packages/twenty-docs/l/ru/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Настройки выпусков -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Функции Лаборатории - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Перейдите в **Настройки → Выпуски** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/building.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/building.mdx deleted file mode 100644 index a54378d48a..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Mimari -description: Twenty uygulamaları nasıl çalışır — korumalı alan, yaşam döngüsü ve yapı taşları. -icon: sitemap ---- - -Twenty uygulamaları, çalışma alanınızı özel nesneler, mantık, UI bileşenleri ve yapay zekâ yetenekleriyle genişleten TypeScript paketleridir. Tam korumalı alan ve izin kontrolleriyle Twenty platformunda çalışırlar. - -## Uygulamalar nasıl çalışır - -Bir uygulama, `twenty-sdk` paketindeki `defineEntity()` işlevleri kullanılarak bildirilen **varlıklar** koleksiyonudur. SDK, bu bildirimleri derleme sırasında AST analiziyle algılar ve bir **manifest** üretir — uygulamanızın bir çalışma alanına neler eklediğinin eksiksiz bir açıklaması. - -``` -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 -``` - - - **Dosya organizasyonu size kalmış.** Varlık algılama AST tabanlıdır — dosyanın nerede bulunduğundan bağımsız olarak SDK `export default defineEntity(...)` çağrılarını bulur. Yukarıdaki klasör yapısı bir gelenektir, zorunluluk değildir. - - -## Varlık türleri - -| Varlık | Amaç | Belgeler | -| ------------------------ | ------------------------------------------------------------- | --------------------------------------------------------------------- | -| **Uygulama** | Uygulama kimliği, izinler, değişkenler | [Veri Modeli](/l/tr/developers/extend/apps/data-model) | -| **Rol** | Nesneler ve alanlar için izin kümeleri | [Veri Modeli](/l/tr/developers/extend/apps/data-model) | -| **Object** | Alanlara sahip özel veri tabloları | [Veri Modeli](/l/tr/developers/extend/apps/data-model) | -| **Alan** | Mevcut nesneleri genişletme, ilişkileri tanımlama | [Veri Modeli](/l/tr/developers/extend/apps/data-model) | -| **Mantık İşlevi** | Tetikleyicilerle sunucu tarafı TypeScript | [Mantıksal İşlevler](/l/tr/developers/extend/apps/logic-functions) | -| **Ön Uç Bileşeni** | Twenty'nin sayfasında korumalı alanda React kullanıcı arayüzü | [Ön Uç Bileşenleri](/l/tr/developers/extend/apps/front-components) | -| **Beceri** | Yeniden kullanılabilir yapay zekâ temsilcisi yönergeleri | [Beceriler ve Temsilciler](/l/tr/developers/extend/apps/skills-and-agents) | -| **Temsilci** | Özel istemlere sahip yapay zekâ asistanları | [Beceriler ve Temsilciler](/l/tr/developers/extend/apps/skills-and-agents) | -| **Görünüm** | Önceden yapılandırılmış kayıt listesi görünümleri | [Düzen](/l/tr/developers/extend/apps/layout) | -| **Gezinme Menüsü Öğesi** | Özel kenar çubuğu öğeleri | [Düzen](/l/tr/developers/extend/apps/layout) | -| **Sayfa Düzeni** | Özel kayıt sayfası sekmeleri ve widget'lar | [Düzen](/l/tr/developers/extend/apps/layout) | - -## Korumalı alan - -* **Mantık işlevleri** sunucuda yalıtılmış Node.js işlemlerinde çalışır. Verilere yalnızca, kapsamı uygulamanın rol izinleriyle sınırlandırılmış tipli API istemcisi üzerinden erişirler. -* **Ön uç bileşenleri**, Remote DOM kullanan Web Worker'larda çalışır — ana sayfadan yalıtılmıştır ancak yerel DOM öğelerini (iframe'ler değil) oluşturur. Twenty ile mesaj iletimi yapan bir ana makine API'si aracılığıyla iletişim kurarlar. -* **İzinler**, API düzeyinde uygulanır. Çalışma zamanı belirteci (`TWENTY_APP_ACCESS_TOKEN`), `defineApplication()` içinde tanımlanan rolden türetilir. - -## Uygulama yaşam döngüsü - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ Install flow │ -│ upload → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -├─────────────────────────────────────────────────────────┤ -│ Publish │ -│ npm publish → appears in Twenty marketplace │ -└─────────────────────────────────────────────────────────┘ -``` - -* **`yarn twenty dev`** — kaynak dosyalarınızı izler ve bağlı bir Twenty sunucusuna değişiklikleri canlı olarak senkronize eder. Şema değiştiğinde tipli API istemcisi otomatik olarak yeniden oluşturulur. -* **`yarn twenty build`** — TypeScript'i derler, mantık işlevlerini ve ön uç bileşenlerini esbuild ile paketler ve bir manifest üretir. -* **Kurulum öncesi/sonrası kancaları** — kurulum sırasında çalışan isteğe bağlı mantık işlevleri. Ayrıntılar için [Mantık İşlevleri](/l/tr/developers/extend/apps/logic-functions) bölümüne bakın. - -## Sonraki adımlar - - - - Nesneleri, alanları, rolleri ve ilişkileri tanımlayın. - - - HTTP, cron ve olay tetikleyicilerine sahip sunucu tarafı işlevler. - - - Twenty'nin kullanıcı arayüzünde korumalı alanda React bileşenleri. - - - Görünümler, gezinme öğeleri ve kayıt sayfası düzenleri. - - - Özel istemlere sahip yapay zekâ becerileri ve temsilciler. - - - CLI komutları, test, varlıklar, uzak depolar ve CI. - - - Bir sunucuya dağıtın veya pazaryerine yayınlayın. - - diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index d815565d3b..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI ve Testler -description: CLI komutları, test kurulumu, genel varlıklar, npm paketleri, uzaklar ve CI yapılandırması. -icon: terminal ---- - -## Genel varlıklar (`public/` klasörü) - -Uygulamanızın kökündeki `public/` klasörü, statik dosyaları barındırır — görseller, simgeler, yazı tipleri veya uygulamanızın çalışma zamanında ihtiyaç duyduğu diğer varlıklar. Bu dosyalar derlemelere otomatik olarak dahil edilir, geliştirme modunda senkronize edilir ve sunucuya yüklenir. - -`public/` içine yerleştirilen dosyalar şunlardır: - -* **Herkese açık olarak erişilebilir** — sunucuya senkronize edildikten sonra varlıklar genel bir URL'den sunulur. Onlara erişmek için kimlik doğrulama gerekmez. -* **Ön uç bileşenlerinde kullanılabilir** — React bileşenlerinizin içinde görseller, simgeler veya herhangi bir medyayı göstermek için varlık URL'lerini kullanın. -* **Mantık işlevlerinde kullanılabilir** — e-postalarda, API yanıtlarında veya herhangi bir sunucu tarafı mantıkta varlık URL'lerine referans verin. -* **Pazar yeri üst verileri için kullanılır** — `defineApplication()` içindeki `logoUrl` ve `screenshots` alanları bu klasördeki dosyalara referans verir (örn. `public/logo.png`). Bunlar, uygulamanız yayımlandığında pazar yerinde görüntülenir. -* **Geliştirme modunda otomatik senkronize edilir** — `public/` içinde bir dosya eklediğinizde, güncellediğinizde veya sildiğinizde otomatik olarak sunucuya senkronize edilir. Yeniden başlatma gerekmez. -* **Derlemelere dahil edilir** — `yarn twenty build`, tüm genel varlıkları dağıtım çıktısına paketler. - -### `getPublicAssetUrl` ile genel varlıklara erişme - -`twenty-sdk` içindeki `getPublicAssetUrl` yardımcı işlevini kullanarak `public/` dizininizdeki bir dosyanın tam URL'sini alın. Hem **mantık işlevlerinde** hem de **ön uç bileşenlerinde** çalışır. - -**Bir mantık işlevinde:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, -}); -``` - -**Bir ön uç bileşeninde:** - -```tsx src/front-components/company-card.tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -`path` bağımsız değişkeni, uygulamanızın `public/` klasörüne göre görelidir. Hem `getPublicAssetUrl('logo.png')` hem de `getPublicAssetUrl('public/logo.png')` aynı URL'ye çözümlenir — `public/` öneki varsa otomatik olarak kaldırılır. - -## npm paketlerini kullanma - -Uygulamanızda herhangi bir npm paketini yükleyip kullanabilirsiniz. Hem mantık işlevleri hem de ön uç bileşenleri, tüm bağımlılıkları çıktıya satır içi olarak ekleyen [esbuild](https://esbuild.github.io/) ile paketlenir — çalışma zamanında `node_modules` gerekmez. - -### Bir paketi yükleme - -```bash filename="Terminal" -yarn add axios -``` - -Ardından kodunuza içe aktarın: - -```ts src/logic-functions/fetch-data.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import axios from 'axios'; - -const handler = async (): Promise => { - 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, -}); -``` - -Aynısı ön uç bileşenleri için de geçerlidir: - -```tsx src/front-components/chart.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { format } from 'date-fns'; - -const DateWidget = () => { - return

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'date-widget', - component: DateWidget, -}); -``` - -### Paketleme nasıl çalışır - -Derleme adımı, her mantık işlevi ve her ön uç bileşeni için tek bir bağımsız dosya üretmek üzere esbuild kullanır. Tüm içe aktarılan paketler pakete satır içi eklenir. - -**Mantık işlevleri**, Node.js ortamında çalışır. Node yerleşik modülleri (`fs`, `path`, `crypto`, `http` vb.) kullanılabilir ve kurulmaları gerekmez. - -**Ön uç bileşenleri**, bir Web Worker içinde çalışır. Node'un yerleşik modülleri **kullanılamaz** — yalnızca tarayıcı ortamında çalışan tarayıcı API'leri ve npm paketleri kullanılabilir. - -Her iki ortamda da `twenty-client-sdk/core` ve `twenty-client-sdk/metadata` önceden sağlanmış modüller olarak mevcuttur — bunlar paketlenmez, ancak çalışma zamanında sunucu tarafından çözülür. - -## Uygulamanızı test etme - -SDK, test kodundan uygulamanızı derlemenize, dağıtmanıza, yüklemenize ve kaldırmanıza olanak tanıyan programatik API'ler sağlar. Tiplenmiş API istemcileriyle birlikte [Vitest](https://vitest.dev/) kullanarak, uygulamanızın gerçek bir Twenty sunucusunda uçtan uca çalıştığını doğrulayan entegrasyon testleri yazabilirsiniz. - -### Kurulum - -İskelet aracıyla oluşturulan uygulama zaten Vitest'i içerir. Manuel kurulum yaparsanız, bağımlılıkları yükleyin: - -```bash filename="Terminal" -yarn add -D vitest vite-tsconfig-paths -``` - -Uygulamanızın kök dizininde bir `vitest.config.ts` oluşturun: - -```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', - }, - }, -}); -``` - -Testler çalışmadan önce sunucuya erişilebildiğini doğrulayan bir kurulum dosyası oluşturun: - -```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), - ); -}); -``` - -### Programatik SDK API'leri - -`twenty-sdk/cli` alt yolu, test kodundan doğrudan çağırabileceğiniz fonksiyonları dışa aktarır: - -| Fonksiyon | Açıklama | -| -------------- | ----------------------------------------------------------------- | -| `appBuild` | Uygulamayı derleyin ve isteğe bağlı olarak bir tarball paketleyin | -| `appDeploy` | Bir tarball'ı sunucuya yükleyin | -| `appInstall` | Uygulamayı etkin çalışma alanına yükleyin | -| `appUninstall` | Uygulamayı etkin çalışma alanından kaldırın | - -Her fonksiyon, `success: boolean` ile birlikte `data` veya `error` içeren bir sonuç nesnesi döndürür. - -### Bir entegrasyon testi yazma - -İşte uygulamayı derleyen, dağıtan ve yükleyen; ardından çalışma alanında göründüğünü doğrulayan tam bir örnek: - -```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(); - }); -}); -``` - -### Testleri çalıştırma - -Yerel Twenty sunucunuzun çalıştığından emin olun, ardından: - -```bash filename="Terminal" -yarn test -``` - -Veya geliştirme sırasında izleme modunda: - -```bash filename="Terminal" -yarn test:watch -``` - -### Tip denetimi - -Ayrıca testleri çalıştırmadan uygulamanızda tip denetimi çalıştırabilirsiniz: - -```bash filename="Terminal" -yarn twenty typecheck -``` - -Bu, `tsc --noEmit` komutunu çalıştırır ve tüm tip hatalarını raporlar. - -## CLI başvurusu - -`dev`, `build`, `add` ve `typecheck` dışında CLI, fonksiyonları çalıştırma, günlükleri görüntüleme ve uygulama kurulumlarını yönetme komutları sağlar. - -### Fonksiyonları çalıştırma (`yarn twenty exec`) - -Bir mantık fonksiyonunu HTTP, cron veya veritabanı olayıyla tetiklemeden manuel olarak çalıştırın: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### Fonksiyon günlüklerini görüntüleme (`yarn twenty logs`) - -Uygulamanızın mantık fonksiyonlarının yürütme günlüklerini akış olarak alın: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -Bu, Docker konteyner günlüklerini gösteren `yarn twenty server logs` komutundan farklıdır. `yarn twenty logs`, uygulamanızın fonksiyon yürütme günlüklerini Twenty sunucusundan gösterir. - - -### Bir uygulamayı kaldırma (`yarn twenty uninstall`) - -Uygulamanızı etkin çalışma alanından kaldırın: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## Uzakları yönetme - -Bir **uzak**, uygulamanızın bağlandığı Twenty sunucusudur. Kurulum sırasında iskelet oluşturucu sizin için otomatik olarak bir tane oluşturur. Dilediğiniz zaman daha fazla uzak ekleyebilir veya aralarında geçiş yapabilirsiniz. - -```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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -Kimlik bilgileriniz `~/.twenty/config.json` içinde saklanır. - -## GitHub Actions ile CI - -İskelet oluşturucu, `.github/workflows/ci.yml` konumunda kullanıma hazır bir GitHub Actions iş akışı üretir. Entegrasyon testlerinizi `main` dalına yapılan her itmede ve çekme isteklerinde otomatik olarak çalıştırır. - -İş akışı: - -1. Kodunuzu çalışma alanına alır -2. `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` eylemini kullanarak geçici bir Twenty sunucusu başlatır -3. `yarn install --immutable` ile bağımlılıkları kurar -4. Eylem çıktılarından enjekte edilen `TWENTY_API_URL` ve `TWENTY_API_KEY` ile `yarn test` çalıştırır - -```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 }} -``` - -Herhangi bir gizli değişken yapılandırmanız gerekmez — `spawn-twenty-docker-image` eylemi, koşucu içinde doğrudan geçici bir Twenty sunucusu başlatır ve bağlantı ayrıntılarını çıktı olarak verir. `GITHUB_TOKEN` gizli değişkeni GitHub tarafından otomatik olarak sağlanır. - -`latest` yerine belirli bir Twenty sürümünü sabitlemek için iş akışının başındaki `TWENTY_VERSION` ortam değişkenini değiştirin. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/connections.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/connections.mdx deleted file mode 100644 index 4ec4781635..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/connections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Bağlantılar -description: Uygulamanızın, OAuth aracılığıyla üçüncü taraf hizmetlerde kullanıcı adına işlem yapmasına izin verin. -icon: plug ---- - -Bağlantılar, bir kullanıcının harici bir hizmet için (Linear, GitHub, Slack, ...) sahip olduğu kimlik bilgileridir. Uygulamanız bu kimlik bilgilerinin **nasıl** elde edildiğini — bir **bağlantı sağlayıcısı** — bildirir ve çalışma zamanında üçüncü taraf API'sine kimlik doğrulamalı çağrılar yapmak için bunları kullanır. - -Bugün yalnızca OAuth 2.0 destekleniyor. Gelecekteki kimlik bilgisi türleri (kişisel erişim belirteçleri, API anahtarları, basic auth) aynı yüzeye bağlanacak — halihazırda `defineConnectionProvider({ type: 'oauth', ... })` kullanan uygulamaların geçiş yapması gerekmeyecek. - - - - - -Bir bağlantı sağlayıcısı, uygulamanızın ihtiyaç duyduğu OAuth el sıkışmasını açıklar. Kullanıcı, uygulamanızın ayarlarında "Bağlantı ekle"ye tıklar, sağlayıcının izin ekranını tamamlar ve çalışma alanında bir `ConnectedAccount` satırı oluşturulur. - -Çalışan bir kurulum **iki dosya** gerektirir — bağlantı sağlayıcısı ve OAuth istemci kimlik bilgilerini tutan `defineApplication` üzerindeki eşleşen bir `serverVariables` bildirimi. - -```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.', - defaultRoleUniversalIdentifier: '...', - // 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, - }, - }, -}); -``` - -Önemli noktalar: - -* `name`, `listConnections({ providerName })` içinde kullanılan benzersiz tanımlayıcı dizedir (kebab-case, `^[a-z][a-z0-9-]*$` ile eşleşmelidir). -* `displayName` uygulama başına ayarlar sekmesinde ve Yapay Zeka araç listesinde gösterilir. -* `clientIdVariable` / `clientSecretVariable` değer değil, **isimdir** — `defineApplication.serverVariables` içinde bildirilen anahtarlarla eşleşmelidir. Gerçek `client_id` ve `client_secret`, sunucu yöneticisi tarafından uygulama kayıt arayüzü üzerinden girilir; deponuza asla commit edilmez. -* `serverVariables` kullanın (`applicationVariables` değil) — OAuth kimlik bilgileri sunucu genelidir ve her Twenty sunucusu için bir OAuth uygulaması vardır. -* Her iki `serverVariables` da doldurulana kadar, uygulama başına ayarlar sekmesi "sunucu yöneticisine ihtiyaç var" ipucunu gösterir ve "Bağlantı ekle" düğmesi devre dışı bırakılır. -* `type: 'oauth'` bugün desteklenen tek değerdir. Seçici ileriye dönük uyumludur: gelecekteki türler (`'pat'`, `'api-key'`, ...) `oauth` yanında yeni alt yapılandırma blokları eklenecektir. - -Sağlayıcınızın beyaz listeye alması gereken OAuth geri çağrı URL'si şudur: - -``` -https:///apps/oauth/callback -``` - - - - - -Bir mantık işlevi işleyicisi içinde, `listConnections({ providerName })`, verilen sağlayıcı için bu uygulamanın `ConnectedAccount` satırlarını, yenilenmiş erişim belirteçleriyle döndürür. - -```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 }; -}; -``` - -Her bağlantı şunlara sahiptir: - -| Alan | Açıklama | -| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `id` | Tekil satır kimliği; tek bir tanesini yeniden getirmek için `getConnection(id)` işlevine iletin | -| `visibility` | `'user'` (bir çalışma alanı üyesine özel) veya `'workspace'` (tüm üyelerle paylaşılan) | -| `scopes` | Üst sağlayıcı tarafından verilen OAuth izinleri (`visibility` ile karıştırılmamalıdır — bunlar ilişkili değildir) | -| `userWorkspaceId` | Sahibinin userWorkspace kimliği — HTTP rota tetikleyicilerinde "istek kullanıcısının bağlantısını" seçmek için kullanışlıdır | -| `accessToken` | Yeni OAuth erişim belirteci (süresi dolmuşsa otomatik olarak yenilenir) | -| `name` / `handle` | Bağlantının görünen adı (OAuth geri çağrısında otomatik türetilir, kullanıcı tarafından yeniden adlandırılabilir) | -| `authFailedAt` | En son yenileme başarısız olduğunda ayarlanır; kullanıcı yeniden bağlanmalıdır | - -Önemli noktalar: - -* Sağlayıcıya göre filtrelemek için `{ providerName }` iletin; bu uygulamanın tüm sağlayıcılardaki tüm bağlantılarını almak için bunu atlayın. -* Sunucu, döndürmeden önce erişim belirtecini şeffaf bir şekilde yeniler. İşleyiciniz her zaman kullanılabilir bir belirteç görür (veya `authFailedAt` ayarlanmıştır). -* `getConnection(id)`, tek satırlık karşılığıdır. - - - - - -Bir kullanıcı "Bağlantı ekle"ye tıkladığında, bir görünürlük seçmesi istenir: - -* **Yalnızca benim için** — kimlik bilgisi, bağlanan kullanıcıya özeldir. Adlarına çağrılan herhangi bir mantık işlevi (`isAuthRequired: true` ile HTTP rota tetikleyicisi) bunu görür; cron tetikleyicileri ve veritabanı olayları görmez. -* **Çalışma alanı paylaşımlı** — herhangi bir çalışma alanı üyesi bu kimlik bilgisini kullanabilir. Cron / veritabanı tetikleyicileri de görür, çünkü istek kullanıcısı yoktur. - -Her işleyici için doğru olanı kullanın: - -```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'); -``` - -Kullanıcı ve sağlayıcı başına birden çok bağlantıya izin verilir; böylece aynı kullanıcı "Personal Linear" ve "Work Linear" bağlantılarını yan yana tutabilir. - - - - - -Her bağlantı sağlayıcısı için, sunucu yöneticisinin önce üçüncü tarafta bir OAuth uygulaması kaydetmesi gerekir. - -1. Sağlayıcının geliştirici ayarlarına gidin (örn. https://linear.app/settings/api/applications/new). -2. **Redirect URI**'yi `\/apps/oauth/callback` olarak ayarlayın. -3. Oluşturulan **Client ID** ve **Client Secret**'ı kopyalayın. -4. Yüklü uygulamayı Twenty'de bir sunucu yöneticisi olarak açın → karşılık gelen `serverVariables` üzerinde değerleri ayarlayın. -5. Ardından çalışma alanı üyeleri, uygulama başına **Bağlantılar** bölümünden bağlantılar ekleyebilir. - - - - diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/data-model.mdx deleted file mode 100644 index 477c8980f3..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,493 +0,0 @@ ---- -title: Veri modeli -description: Twenty SDK ile nesneleri, alanları, rolleri ve uygulama meta verilerini tanımlayın. -icon: database ---- - -`twenty-sdk` paketi, uygulamanızın veri modelini tanımlamak için `defineEntity` işlevlerini sağlar. SDK'nin varlıklarınızı algılayabilmesi için `export default defineEntity({...})` kullanmanız gerekir. Bu fonksiyonlar, derleme zamanında yapılandırmanızı doğrular ve IDE otomatik tamamlama ile tür güvenliği sağlar. - - - **Dosya organizasyonu size kalmış.** - Varlık algılama AST tabanlıdır — dosyanın nerede bulunduğundan bağımsız olarak SDK `export default defineEntity(...)` çağrılarını bulur. Dosyaları türe göre gruplamak (örn. `logic-functions/`, `roles/`) bir gereklilik değil, yalnızca bir gelenektir. - - - - - -Roller, çalışma alanınızdaki nesneler ve eylemler üzerindeki izinleri kapsar. - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -Her uygulamanın, şunları tanımlayan tam olarak bir adet `defineApplication` çağrısı olmalıdır: - -* **Kimlik**: tanımlayıcılar, görünen ad ve açıklama. -* **İzinler**: işlevlerinin ve ön uç bileşenlerinin hangi rolü kullandığı. -* **(İsteğe bağlı) Değişkenler**: fonksiyonlarınıza ortam değişkenleri olarak sunulan anahtar–değer çiftleri. -* **(İsteğe bağlı) Kurulum öncesi / kurulum sonrası fonksiyonlar**: kurulumdan önce veya sonra çalışan mantık fonksiyonları. - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -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, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -Notlar: -* `universalIdentifier` alanları, size ait deterministik kimliklerdir. Bunları bir kez oluşturun ve senkronizasyonlar boyunca kararlı tutun. -* `applicationVariables`, fonksiyonlarınız ve ön uç bileşenleriniz için ortam değişkenlerine dönüşür (örn. `DEFAULT_RECIPIENT_NAME`, `process.env.DEFAULT_RECIPIENT_NAME` olarak kullanılabilir). -* `defaultRoleUniversalIdentifier`, `defineRole()` ile tanımlanmış bir role referans vermelidir (yukarıya bakın). -* Kurulum öncesi ve kurulum sonrası fonksiyonlar manifest derlemesi sırasında otomatik olarak algılanır — bunlara `defineApplication()` içinde referans vermeniz gerekmez. - -#### Pazaryeri meta verileri - -Eğer [uygulamanızı yayımlamayı](/l/tr/developers/extend/apps/publishing) planlıyorsanız, bu isteğe bağlı alanlar uygulamanızın pazaryerinde nasıl görüneceğini kontrol eder: - -| Alan | Açıklama | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -| `author` | Yazar veya şirket adı | -| `category` | Pazaryerinde filtreleme için uygulama kategorisi | -| `logoUrl` | Uygulamanızın logosuna giden yol (örn. `public/logo.png`) | -| `screenshots` | Ekran görüntüsü yollarının dizisi (örn. `public/screenshot-1.png`) | -| `aboutDescription` | "Hakkında" sekmesi için daha uzun bir markdown açıklaması. Belirtilmezse, pazaryeri npm'deki paketin `README.md` dosyasını kullanır | -| `websiteUrl` | Web sitenize bağlantı | -| `termsUrl` | Hizmet Koşulları'na bağlantı | -| `emailSupport` | Destek e-posta adresi | -| `issueReportUrl` | Sorun izleyicisine bağlantı | - -#### Roller ve izinler - -`application-config.ts` içindeki `defaultRoleUniversalIdentifier`, uygulamanızın mantık fonksiyonları ve ön uç bileşenleri tarafından kullanılan varsayılan rolü belirtir. Ayrıntılar için yukarıdaki `defineRole` bölümüne bakın. - -* `TWENTY_APP_ACCESS_TOKEN` olarak enjekte edilen çalışma zamanı belirteci bu rolden türetilir. -* Türlendirilmiş istemci, o role tanınan izinlerle sınırlandırılır. -* En az ayrıcalık ilkesini izleyin: Yalnızca fonksiyonlarınızın ihtiyaç duyduğu izinlere sahip özel bir rol oluşturun. - -##### Varsayılan fonksiyon rolü - -Yeni bir uygulama iskeleti oluşturduğunuzda, CLI varsayılan bir rol dosyası oluşturur: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -Bu rolün `universalIdentifier` değeri, `application-config.ts` içinde `defaultRoleUniversalIdentifier` olarak referans verilir: - -* **\*.role.ts**, bir rolün neler yapabileceğini tanımlar. -* **application-config.ts**, fonksiyonlarınızın izinlerini devralması için bu role işaret eder. - -Notlar: -* Oluşturulan rolden başlayın ve en az ayrıcalık ilkesini izleyerek bunu aşamalı olarak kısıtlayın. -* `objectPermissions` ve `fieldPermissions` değerlerini, fonksiyonlarınızın ihtiyaç duyduğu nesneler ve alanlarla değiştirin. -* `permissionFlags`, platform düzeyindeki yeteneklere erişimi kontrol eder. Bunları asgari düzeyde tutun. -* Çalışan bir örnek için bkz.: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). - - - - -Özel nesneler, çalışma alanınızdaki kayıtlar için hem şemayı hem de davranışı tanımlar. Yerleşik doğrulamayla nesneler tanımlamak için `defineObject()` kullanın: - -```ts postCard.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, - }, - ], -}); -``` - -Önemli noktalar: - -* Yerleşik doğrulama ve daha iyi IDE desteği için `defineObject()` kullanın. -* `universalIdentifier` dağıtımlar arasında benzersiz ve kararlı olmalıdır. -* Her alan bir `name`, `type`, `label` ve kendi kararlı `universalIdentifier` değerini gerektirir. -* `fields` dizisi isteğe bağlıdır — özel alanlar olmadan da nesneler tanımlayabilirsiniz. -* `yarn twenty add` kullanarak, adlandırma, alanlar ve ilişkiler konusunda sizi yönlendirerek yeni nesneler oluşturabilirsiniz. - - -**Temel alanlar otomatik olarak oluşturulur.** Özel bir nesne tanımladığınızda Twenty, standart alanları otomatik olarak ekler -örneğin `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` ve `deletedAt`. -Bunları `fields` dizinizde tanımlamanız gerekmez — yalnızca özel alanlarınızı ekleyin. -`fields` dizinizde aynı ada sahip bir alan tanımlayarak varsayılan alanları geçersiz kılabilirsiniz, -ancak bu önerilmez. - - - - - -Sahibi olmadığınız nesnelere alan eklemek için `defineField()` kullanın — standart Twenty nesneleri (Person, Company, vb.) gibi. veya diğer uygulamalardaki nesneler. `defineObject()` içindeki satır içi alanların aksine, bağımsız alanlar hangi nesneyi genişlettiklerini belirtmek için bir `objectUniversalIdentifier` gerektirir: - -```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' }, - ], -}); -``` - -Önemli noktalar: -* `objectUniversalIdentifier` hedef nesneyi tanımlar. Standart nesneler için, `twenty-sdk`'den dışa aktarılan `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`'ı kullanın. -* Alanları `defineObject()` içinde satır içi tanımlarken, `objectUniversalIdentifier`'a ihtiyacınız yoktur — üst nesneden devralınır. -* `defineField()`, `defineObject()` ile oluşturmadığınız nesnelere alan eklemenin tek yoludur. - - - - -İlişkiler nesneleri birbirine bağlar. Twenty'de ilişkiler her zaman **çift yönlüdür** — her iki tarafı da tanımlarsınız ve her taraf diğerine başvurur. - -İki ilişki türü vardır: - -| İlişki türü | Açıklama | Yabancı anahtar var mı? | -| ------------- | --------------------------------------------------------- | ----------------------- | -| `MANY_TO_ONE` | Bu nesnenin birçok kaydı, hedefin bir kaydını işaret eder | Evet (`joinColumnName`) | -| `ONE_TO_MANY` | Bu nesnenin bir kaydı, hedefin birçok kaydına sahiptir | Hayır (ters taraf) | - -#### İlişkiler nasıl çalışır - -Her ilişki, birbirine referans veren iki alan gerektirir: - -1. **MANY_TO_ONE** tarafı — yabancı anahtarı tutan nesne üzerinde bulunur -2. **ONE_TO_MANY** tarafı — koleksiyona sahip olan nesne üzerinde bulunur - -Her iki alan da `FieldType.RELATION` kullanır ve `relationTargetFieldMetadataUniversalIdentifier` aracılığıyla birbirine karşılıklı referans verir. - -#### Örnek: Posta Kartı'nın birçok Alıcısı vardır - -Bir `PostCard`'ın birçok `PostCardRecipient` kaydına gönderilebildiğini varsayalım. Her alıcı tam olarak bir posta kartına aittir. - -**Adım 1: PostCard üzerinde ONE_TO_MANY tarafını tanımlayın** ("bir" taraf): - -```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, - }, -}); -``` - -**Adım 2: PostCardRecipient üzerinde MANY_TO_ONE tarafını tanımlayın** ("çok" taraf — yabancı anahtarı tutar): - -```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', - }, -}); -``` - - -**Döngüsel içe aktarmalar:** Her iki ilişki alanı da birbirlerinin `universalIdentifier` değerine referans verir. Döngüsel içe aktarma sorunlarından kaçınmak için, alan kimliklerinizi her dosyadan adlandırılmış sabitler olarak dışa aktarın ve diğer dosyada içe aktarın. Derleme sistemi bunları derleme zamanında çözer. - - -#### Standart nesnelerle ilişkilendirme - -Yerleşik bir Twenty nesnesiyle (Person, Company, vb.) ilişki oluşturmak için `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS` kullanın: - -```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', - }, -}); -``` - -#### İlişki alanı özellikleri - -| Özellik | Zorunlu | Açıklama | -| ------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------- | -| `type` | Evet | `FieldType.RELATION` olmalıdır | -| `relationTargetObjectMetadataUniversalIdentifier` | Evet | Hedef nesnenin `universalIdentifier` değeri | -| `relationTargetFieldMetadataUniversalIdentifier` | Evet | Hedef nesnedeki eşleşen alanın `universalIdentifier` değeri | -| `universalSettings.relationType` | Evet | `RelationType.MANY_TO_ONE` veya `RelationType.ONE_TO_MANY` | -| `universalSettings.onDelete` | Yalnızca MANY_TO_ONE | Başvurulan kayıt silindiğinde ne olacağı: `CASCADE`, `SET_NULL`, `RESTRICT` veya `NO_ACTION` | -| `universalSettings.joinColumnName` | Yalnızca MANY_TO_ONE | Yabancı anahtar için veritabanı sütun adı (örn. `postCardId`) | - -#### defineObject içinde satır içi ilişki alanları - -İlişki alanlarını doğrudan `defineObject()` içinde de tanımlayabilirsiniz. Bu durumda, `objectUniversalIdentifier`'ı atlayın — üst nesneden devralınır: - -```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 - ], -}); -``` - - - -## `yarn twenty add` ile varlıklar için iskelet oluşturma - -Varlık dosyalarını elle oluşturmak yerine etkileşimli iskelet oluşturucuyu kullanabilirsiniz: - -```bash filename="Terminal" -yarn twenty add -``` - -Bu, bir varlık türü seçmenizi ister ve gerekli alanlar boyunca size yol gösterir. Kararlı bir `universalIdentifier` ve doğru `defineEntity()` çağrısıyla kullanıma hazır bir dosya üretir. - -İlk istemi atlamak için varlık türünü doğrudan da geçebilirsiniz: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### Kullanılabilir varlık türleri - -| Varlık türü | Komut | Oluşturulan dosya | -| -------------------- | ------------------------------------ | ------------------------------------------------------- | -| Nesne | `yarn twenty add object` | `src/objects/\.ts` | -| Alan | `yarn twenty add field` | `src/fields/\.ts` | -| Mantık işlevi | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| Ön uç bileşeni | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| Rol | `yarn twenty add role` | `src/roles/\.ts` | -| Beceri | `yarn twenty add skill` | `src/skills/\.ts` | -| Temsilci | `yarn twenty add agent` | `src/agents/\.ts` | -| Görünüm | `yarn twenty add view` | `src/views/\.ts` | -| Gezinme menüsü öğesi | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| Sayfa düzeni | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### İskelet oluşturucunun ürettikleri - -Her varlık türünün kendi şablonu vardır. Örneğin, `yarn twenty add object` şunları sorar: - -1. **Ad (tekil)** — ör. `invoice` -2. **Ad (çoğul)** — ör. `invoices` -3. **Etiket (tekil)** — adından otomatik doldurulur (ör. `Invoice`) -4. **Etiket (çoğul)** — otomatik doldurulur (ör. `Invoices`) -5. **Bir görünüm ve gezinme öğesi oluşturulsun mu?** — evet derseniz, iskelet oluşturucu yeni nesne için eşleşen bir görünüm ve kenar çubuğu bağlantısı da üretir. - -Diğer varlık türlerinin istemleri daha basittir — çoğu yalnızca bir ad sorar. - -`field` varlık türü daha ayrıntılıdır: alan adını, etiketi, türü (`TEXT`, `NUMBER`, `SELECT`, `RELATION` vb. gibi mevcut tüm alan türlerinin listesinden) ve hedef nesnenin `universalIdentifier` değerini sorar. - -### Özel çıktı yolu - -`--path` bayrağını kullanarak oluşturulan dosyayı özel bir konuma yerleştirin: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/front-components.mdx deleted file mode 100644 index 7f09f963fa..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: Ön uç bileşenleri -description: Twenty'nin UI'si içinde, korumalı alan (sandbox) izolasyonu ile görüntülenen React bileşenleri oluşturun. -icon: window-maximize ---- - -Ön uç bileşenler, Twenty'nin UI'si içinde doğrudan görüntülenen React bileşenleridir. Remote DOM kullanan izole bir Web Worker içinde çalışırlar — kodunuz izole bir ortamda (sandbox) çalışır ancak bir iframe içinde değil, sayfada yerel olarak işlenir. - -## Ön uç bileşenlerinin kullanılabileceği yerler - -Ön uç bileşenler, Twenty içinde iki konumda işlenebilir: - -* **Yan panel** — Headless olmayan ön uç bileşenler, sağ taraftaki yan panelde açılır. Bir ön uç bileşeni komut menüsünden tetiklendiğinde varsayılan davranış budur. -* **Widget'lar (panolar ve kayıt sayfaları)** — Ön uç bileşenler, sayfa düzenlerine widget olarak gömülebilir. Bir pano veya kayıt sayfası düzeni yapılandırılırken kullanıcılar bir ön uç bileşen widget'ı ekleyebilir. - -## Basit örnek - -Bir ön uç bileşenini çalışırken görmenin en hızlı yolu, onu bir **komut menüsü öğesi** olarak kaydetmektir. Bileşenin sayfanın sağ üst köşesinde bir hızlı işlem düğmesi olarak görünmesini sağlamak için ayrı bir dosyada `defineCommandMenuItem` kullanın: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -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` ile senkronize ettikten sonra (veya tek seferlik bir `yarn twenty dev --once` çalıştırdıktan sonra), hızlı işlem sayfanın sağ üst köşesinde görünür: - -
- Sağ üst köşedeki hızlı işlem düğmesi -
- -Bileşeni satır içi işlemek için üzerine tıklayın. - -## Yapılandırma alanları - -| Alan | Zorunlu | Açıklama | -| --------------------- | ------- | ------------------------------------------------------------------------------------- | -| `universalIdentifier` | Evet | Bu bileşen için kalıcı benzersiz kimlik | -| `component` | Evet | Bir React bileşen fonksiyonu | -| `name` | Hayır | Görünen Ad | -| `description` | Hayır | Bileşenin ne yaptığına dair açıklama | -| `isHeadless` | Hayır | Bileşenin görünür bir kullanıcı arayüzü yoksa `true` olarak ayarlayın (aşağıya bakın) | - -## Bir ön uç bileşenini bir sayfaya yerleştirme - -Komutların ötesinde, bir ön uç bileşenini bir **sayfa düzeninde** widget olarak ekleyerek doğrudan bir kayıt sayfasına gömebilirsiniz. Ayrıntılar için [definePageLayout](/l/tr/developers/extend/apps/skills-and-agents#definepagelayout) bölümüne bakın. - -## Headless ve headless olmayan - -Ön uç bileşenler, `isHeadless` seçeneğiyle kontrol edilen iki işleme kipiyle gelir: - -**Headless olmayan (varsayılan)** — Bileşen görünür bir kullanıcı arayüzü (UI) oluşturur. Komut menüsünden tetiklendiğinde yan panelde açılır. `isHeadless` `false` olduğunda veya belirtilmediğinde bu varsayılan davranıştır. - -**Headless (`isHeadless: true`)** — Bileşen arka planda görünmez şekilde bağlanır. Yan paneli açmaz. Headless bileşenler, mantığı çalıştırıp ardından kendilerini kaldıran eylemler için tasarlanmıştır — örneğin, bir async görevi çalıştırma, bir sayfaya gitme veya bir onay modalı gösterme. Aşağıda açıklanan SDK Command bileşenleriyle doğal olarak eşleşirler. - -```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, -}); -``` - -Bileşen `null` döndürdüğü için, Twenty bunun için bir kapsayıcı oluşturmayı atlar — düzende boş alan görünmez. Bileşen yine de tüm hook'lara ve host iletişim API'sine erişime sahiptir. - -## SDK Command bileşenleri - -`twenty-sdk` paketi, headless ön bileşenler için tasarlanmış dört Command yardımcı bileşeni sağlar. Her bileşen bağlandığında bir eylem yürütür, hataları bir snackbar bildirimi göstererek ele alır ve tamamlandığında ön bileşeni otomatik olarak kaldırır. - -Bunları `twenty-sdk/command` içinden içe aktarın: - -* **`Command`** — `execute` prop'u aracılığıyla async bir geri çağrıyı çalıştırır. -* **`CommandLink`** — Bir uygulama yoluna gider. Props: `to`, `params`, `queryParams`, `options`. -* **`CommandModal`** — Bir onay modalı açar. Kullanıcı onaylarsa `execute` geri çağrısını yürütür. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. -* **`CommandOpenSidePanelPage`** — Belirli bir yan panel sayfasını açar. Props: `page`, `pageTitle`, `pageIcon`. - -`Command` kullanarak komut menüsünden bir eylem çalıştıran headless bir ön bileşenin tam örneği: - -```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 ; -}; - -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', -}); -``` - -Ve yürütmeden önce onay istemek için `CommandModal` kullanan bir örnek: - -```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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, -}); -``` - -## Çalışma zamanı bağlamına erişme - -Bileşeninizin içinde, geçerli kullanıcıya, kayda ve bileşen örneğine erişmek için SDK hook'larını kullanın: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -Kullanılabilir hook'lar: - -| Hook | Döndürür | Açıklama | -| --------------------------------------------- | -------------------- | -------------------------------------------------------------------------- | -| `useUserId()` | `string` veya `null` | Geçerli kullanıcının ID'si | -| `useSelectedRecordIds()` | `metin[]` | Tüm seçili kayıt kimlikleri (hiçbiri seçilmediyse boş dizi) | -| `useRecordId()` | `string` veya `null` | **Kullanımdan kaldırıldı.** Bunun yerine `useSelectedRecordIds()` kullanın | -| `useFrontComponentId()` | `string` | Bu bileşen örneğinin ID'si | -| `useFrontComponentExecutionContext(selector)` | değişir | Bir seçici işlevle tam yürütme bağlamına erişin | - -## Host iletişim API'si - -Ön uç bileşenleri, `twenty-sdk`'deki işlevleri kullanarak gezinmeyi, modalları ve bildirimleri tetikleyebilir: - -| Fonksiyon | Açıklama | -| ----------------------------------------------- | ---------------------------------- | -| `navigate(to, params?, queryParams?, options?)` | Uygulamada bir sayfaya git | -| `openSidePanelPage(params)` | Bir yan panel aç | -| `closeSidePanel()` | Yan paneli kapat | -| `openCommandConfirmationModal(params)` | Bir onay iletişim kutusu göster | -| `enqueueSnackbar(params)` | Bir toast bildirimi göster | -| `unmountFrontComponent()` | Bileşeni kaldır (unmount) | -| `updateProgress(progress)` | Bir ilerleme göstergesini güncelle | - -Bir eylem tamamlandıktan sonra bir snackbar göstermek ve yan paneli kapatmak için host API'sini kullanan bir örnek: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', - name: 'archive-record', - description: 'Archives the current record', - component: ArchiveRecord, -}); -``` - -### Birden çok kayıtla çalışma - -Birden çok seçili kaydı yönetmek için `useSelectedRecordIds()` kullanın. Bu, toplu işlemler için kullanışlıdır: - -```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 ( -
-

Export {selectedRecordIds.length} selected record(s)?

- -
- ); -}; - -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, - }, -}); -``` - -## defineCommandMenuItem - -Bir ön uç bileşenini komut menüsüne (Cmd+K) kaydetmek için `defineCommandMenuItem` kullanın. `isPinned` `true` ise, sayfanın sağ üst köşesinde bir hızlı işlem düğmesi olarak da görünür. - -```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', -}); -``` - -| Alan | Zorunlu | Açıklama | -| --------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `universalIdentifier` | Evet | Komut için kalıcı benzersiz kimlik | -| `label` | Evet | Komut menüsünde (Cmd+K) gösterilen tam etiket | -| `frontComponentUniversalIdentifier` | Evet | Bu komutun açtığı ön uç bileşeninin `universalIdentifier` değeri | -| `shortLabel` | Hayır | Sabitlenmiş hızlı işlem düğmesinde görüntülenen daha kısa etiket | -| `icon` | Hayır | Etiketin yanında görüntülenen simge adı (örn. `'IconBolt'`, `'IconSend'`) | -| `isPinned` | Hayır | `true` olduğunda, komutu sayfanın sağ üst köşesinde bir hızlı işlem düğmesi olarak gösterir | -| `availabilityType` | Hayır | Komutun nerede görüneceğini kontrol eder: `'GLOBAL'` (her zaman kullanılabilir), `'RECORD_SELECTION'` (yalnızca kayıtlar seçiliyken) veya `'FALLBACK'` (başka hiçbir komut eşleşmediğinde gösterilir) | -| `availabilityObjectUniversalIdentifier` | Hayır | Komutu belirli bir nesne türünün sayfalarıyla sınırlandırın (örn. yalnızca Company kayıtlarında) | -| `conditionalAvailabilityExpression` | Hayır | Komutun görünür olup olmadığını dinamik olarak kontrol eden bir boolean ifade (aşağıya bakın) | - -## Koşullu kullanılabilirlik ifadeleri - -`conditionalAvailabilityExpression` alanı, geçerli sayfa bağlamına göre bir komutun ne zaman görünür olacağını kontrol etmenizi sağlar. İfadeler oluşturmak için `twenty-sdk`'den türlendirilmiş değişkenleri ve operatörleri içe aktarın: - -```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, - ), -}); -``` - -**Bağlam değişkenleri** — bunlar sayfanın mevcut durumunu temsil eder: - -| Değişken | Tür | Açıklama | -| ------------------------------ | --------- | ----------------------------------------------------------------- | -| `pageType` | `string` | Geçerli sayfa türü (örn. `'RecordIndexPage'`, `'RecordShowPage'`) | -| `isInSidePanel` | `boolean` | Bileşenin bir yan panelde oluşturulup oluşturulmadığı | -| `numberOfSelectedRecords` | `number` | Şu anda seçili kayıt sayısı | -| `isSelectAll` | `boolean` | "tümünü seç" seçeneğinin etkin olup olmadığı | -| `selectedRecords` | `array` | Seçili kayıt nesneleri | -| `favoriteRecordIds` | `array` | Favorilere eklenen kayıtların ID'leri | -| `objectPermissions` | `object` | Geçerli nesne türü için izinler | -| `targetObjectReadPermissions` | `object` | Hedef nesne için okuma izinleri | -| `targetObjectWritePermissions` | `object` | Hedef nesne için yazma izinleri | -| `featureFlags` | `object` | Etkin özellik bayrakları | -| `objectMetadataItem` | `object` | Geçerli nesne türünün üst verileri | -| `hasAnySoftDeleteFilterOnView` | `boolean` | Geçerli görünümde soft-delete filtresi olup olmadığı | - -**Operatörler** — değişkenleri boolean ifadelere dönüştürmek için birleştirin: - -| Operatör | Açıklama | -| ----------------------------------- | --------------------------------------------------------- | -| `isDefined(value)` | Değer null/undefined değilse `true` | -| `isNonEmptyString(value)` | Değer boş olmayan bir string ise `true` | -| `includes(array, value)` | Dizi değeri içeriyorsa `true` | -| `includesEvery(array, prop, value)` | Her bir öğenin özelliği değeri içeriyorsa `true` | -| `every(array, prop)` | Özellik her öğede truthy ise `true` | -| `everyDefined(array, prop)` | Özellik her öğede tanımlıysa `true` | -| `everyEquals(array, prop, value)` | Özellik her öğede değere eşitse `true` | -| `some(array, prop)` | Özellik en az bir öğede truthy ise `true` | -| `someDefined(array, prop)` | Özellik en az bir öğede tanımlıysa `true` | -| `someEquals(array, prop, value)` | Özellik en az bir öğede değere eşitse `true` | -| `someNonEmptyString(array, prop)` | Özellik en az bir öğede boş olmayan bir string ise `true` | -| `none(array, prop)` | Özellik her öğede falsy ise `true` | -| `noneDefined(array, prop)` | Özellik her öğede tanımsızsa `true` | -| `noneEquals(array, prop, value)` | Özellik hiçbir öğede değere eşit değilse `true` | - -## Genel varlıklar - -Ön uç bileşenleri, `getPublicAssetUrl` kullanarak uygulamanın `public/` dizinindeki dosyalara erişebilir: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -Ayrıntılar için [genel varlıklar bölümüne](/l/tr/developers/extend/apps/cli-and-testing#public-assets-public-folder) bakın. - -## Stil - -Ön uç bileşenleri birden fazla biçimlendirme yaklaşımını destekler. Şunları kullanabilirsiniz: - -* **Satır içi stiller** — `style={{ color: 'red' }}` -* **Twenty UI bileşenleri** — `twenty-sdk/ui` içinden içe aktarın (Button, Tag, Status, Chip, Avatar ve daha fazlası) -* **Emotion** — `@emotion/react` ile CSS-in-JS -* **Styled-components** — `styled.div` kalıpları -* **Tailwind CSS** — yardımcı sınıflar -* **React ile uyumlu herhangi bir CSS-in-JS kitaplığı** - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/getting-started.mdx deleted file mode 100644 index 51c3644a54..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Başlarken -icon: rocket -description: İlk Twenty uygulamanızı dakikalar içinde oluşturun. ---- - -## Ön Gereksinimler - -* **Node.js 24+** — [Buradan indirin](https://nodejs.org/) -* **Yarn 4** — Corepack aracılığıyla Node ile birlikte gelir. Etkinleştirin: `corepack enable` -* **Docker** — [Buradan indirin](https://www.docker.com/products/docker-desktop/). Yerel bir Twenty sunucusunu çalıştırmak için gereklidir. Zaten başka bir yerde Twenty çalıştırıyorsanız atlayın. - -Bir Twenty uygulaması oluşturmanın üç aşaması vardır. İskelet oluşturucu bunları tek bir sorunsuz akış komutuna indirger, ancak her aşama ayrı bir kavramdır — bir şeyler başarısız olduğunda hangi aşamada olduğunuzu bilmek neyi düzeltmeniz gerektiğini söyler. - -| Aşama | Ne yaparsınız | Araç | Sonuç | -| ---------------------------- | ------------------------------------------------- | ----------------------------- | ---------------------------------- | -| **1. İskelet Oluşturma** | Uygulamanın kaynak kodunu oluşturun | `npx create-twenty-app` | Diskte bir TypeScript projesi | -| **2. Bir sunucu çalıştırma** | Eşitleme yapacağınız bir Twenty sunucusu başlatın | Docker + `yarn twenty server` | Çalışan bir Twenty örneği | -| **3. Eşitleme** | Kodunuzu sunucuya canlı olarak eşitleyin | `yarn twenty dev` | Değişiklikleriniz arayüzde görünür | - ---- - -## Aşama 1 — Projenizin iskeletini oluşturun - -Şablondan yeni bir uygulama oluşturun: - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -``` - -Sizden bir ad ve açıklama istenir — varsayılanlar için **Enter** tuşuna basın. Bu, `my-twenty-app/` içinde bir başlangıç `application-config.ts`, varsayılan bir rol, bir CI iş akışı ve bir entegrasyon testi ile bir TypeScript projesi oluşturur. - -**Bu aşamadan sonra:** makinenizde uygulamanın kaynak kodu bulunur. Henüz çalışmıyor — bu, 2. Aşama. - ---- - -## Aşama 2 — Yerel bir Twenty sunucusu çalıştırın - -Uygulamanızın eşitleme yapacağı bir Twenty sunucusuna ihtiyacı vardır. Sunucu, Docker içinde yerel olarak çalışan tam bir Twenty örneğidir — UI, GraphQL API, PostgreSQL. Yerel kodunuz tanımlarını bu sunucuya yükler; bu da onların arayüzde görünmesini sağlar. - -İskelet oluşturucu sizin için bir tane başlatmayı teklif eder: - -> **Yerel bir Twenty örneği kurmak ister misiniz?** - -* **Evet (önerilir)** — `twentycrm/twenty-app-dev` Docker imajını çeker ve `2020` portunda başlatır. Önce Docker'ın çalıştığından emin olun. -* **Hayır** — Zaten bağlanmak istediğiniz bir Twenty sunucunuz varsa bunu seçin. Bunu daha sonra `yarn twenty remote add` ile bağlayabilirsiniz. - -
- Yerel örnek başlatılsın mı? -
- -Sunucu çalışır duruma geldiğinde, oturum açmak için bir tarayıcı açılır. Önceden eklenmiş demo hesabını kullanın: - -* **E-posta:** `tim@apple.dev` -* **Parola:** `tim@apple.dev` - -
- Twenty oturum açma ekranı -
- -Sonraki ekranda **Authorize**'a tıklayın — bu işlem CLI'nin çalışma alanınıza erişmesine izin verir. - -
- Twenty CLI yetkilendirme ekranı -
- -Terminaliniz her şeyin kurulduğunu onaylayacaktır. - -
- Uygulama iskeleti başarıyla oluşturuldu -
- -**Bu aşamadan sonra:** [http://localhost:2020](http://localhost:2020) adresinde çalışan bir Twenty sunucunuz ve ona eşitleme yapmak için yetkilendirilmiş bir CLI'ınız olur. - - -Docker yüklü değilse veya çalışmıyorsa, iskelet oluşturucu işletim sisteminiz için doğru başlatma komutunu söyleyecektir. Docker çalışır duruma geldiğinde, `yarn twenty server start` ile devam edebilirsiniz — yeniden iskelet oluşturmaya gerek yok. - - ---- - -## Aşama 3 — Değişikliklerinizi eşitleyin - -Zamanınızın çoğunu geçireceğiniz iç döngü budur. - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -Bu, `src/` dizinini izler, her değişiklikte yeniden oluşturur ve sonucu sunucuya eşitler. Bir dosyayı düzenleyin, kaydedin; bir saniye içinde sunucu değişikliği yansıtır. Terminalinizde canlı bir durum paneli göreceksiniz. - -Daha ayrıntılı çıktı (derleme günlükleri, eşitleme istekleri, hata izleri) için `--verbose` ekleyin. - -
- Geliştirme modu terminal çıktısı -
- -[http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer) adresini açın. Uygulamanızı **Your Apps** altında görmelisiniz. - -
- Your Apps listesinde My twenty app gösteriliyor -
- -**My twenty app**'e tıklayarak **application registration**'ı görüntüleyin — uygulamanızı tanımlayan (ad, tanımlayıcı, OAuth kimlik bilgileri, kaynak) sunucu düzeyinde bir kayıttır. Tek bir kayıt, aynı sunucudaki birden çok çalışma alanına kurulabilir. - -
- Uygulama kaydı ayrıntıları -
- -Çalışma alanındaki kurulumu görmek için **View installed app**'e tıklayın. **About** sekmesi sürümü ve yönetim seçeneklerini gösterir. - -
- Yüklü uygulama -
- -**Bu aşamadan sonra:** canlı bir geliştirme döngünüz olur. `src/` içindeki herhangi bir dosyayı düzenleyin; arayüzde görünür. - -### CI ve betikler için tek seferlik eşitleme - -Tek bir derleme + eşitleme çalıştırıp çıkmak için `--once` parametresini geçin — aynı ardışık düzen, izleyici yok: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| Komut | Davranış | Ne zaman kullanılmalı | -| ------------------------ | ----------------------------------------------------------------------- | --------------------------------------------------------------- | -| `yarn twenty dev` | Her değişiklikte izler ve yeniden eşitler. Siz durdurana kadar çalışır. | Etkileşimli yerel geliştirme. | -| `yarn twenty dev --once` | Tek derleme + eşitleme; başarıda `0`, başarısızlıkta `1` ile çıkar. | CI, pre-commit kancaları, AI ajanları, betiklenmiş iş akışları. | - -Her iki mod da geliştirme modunda bir sunucuya ve kimliği doğrulanmış bir uzak uca ihtiyaç duyar. - - -Geliştirme modu yalnızca geliştirme ortamında (`NODE_ENV=development`) çalışan Twenty örneklerinde kullanılabilir. Üretim örnekleri, geliştirme eşitleme isteklerini reddeder — üretim sunucularına dağıtmak için `yarn twenty deploy` kullanın. Bkz. [Uygulamaları Yayımlama](/l/tr/developers/extend/apps/publishing). - - ---- - -## Oluşturabilecekleriniz - -Uygulamalar **varlıklardan** oluşur — her biri tek bir `export default` içeren bir TypeScript dosyası olarak tanımlanır: - -| Varlık | Ne yapar | -| ------------------------- | --------------------------------------------------------------------------------------------------------- | -| **Nesneler ve Alanlar** | Özel veri modelleri (Post Card, Invoice vb.) tipli alanlarla | -| **Mantıksal işlevler** | HTTP rotaları, cron zamanlamaları veya veritabanı olayları tarafından tetiklenen sunucu tarafı TypeScript | -| **Ön uç bileşenleri** | Twenty'nin arayüzünde görüntülenen React bileşenleri (yan panel, widget'lar, komut menüsü) | -| **Beceriler ve Aracılar** | Yapay zeka yetenekleri — yeniden kullanılabilir yönergeler ve otonom asistanlar | -| **Görünümler ve Gezinme** | Önceden yapılandırılmış liste görünümleri ve kenar çubuğu menü öğeleri | -| **Sayfa düzenleri** | Sekmeler ve widget'lar içeren özel kayıt ayrıntı sayfaları | - -Tam başvuru: [Uygulama Oluşturma](/l/tr/developers/extend/apps/building). - -## Proje yapısı - -```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 -``` - -| Dosya / Klasör | Amaç | -| ---------------------------------------- | --------------------------------------------------------------------------- | -| `src/application-config.ts` | **Gerekli.** Uygulamanızın ana yapılandırma dosyası. | -| `src/default-role.ts` | Mantık işlevlerinizin neye erişebileceğini denetleyen varsayılan rol. | -| `src/constants/universal-identifiers.ts` | Otomatik oluşturulan UUID'ler ve meta veriler (görünen ad, açıklama). | -| `src/__tests__/` | Entegrasyon testleri (kurulum + örnek test). | -| `public/` | Uygulamanızla birlikte sunulan statik varlıklar (görüntüler, yazı tipleri). | - -### Bir örnekten başlayın - -Daha kapsamlı bir projeyle başlamak için `--example` kullanın (özel nesneler, alanlar, mantık işlevleri, ön uç bileşenleri): - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -Örnekler [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples) dizininde yer alır. Ayrıca `yarn twenty add` ile mevcut bir projeye tek tek varlıklar için iskelet oluşturabilirsiniz — bkz. [Uygulama Oluşturma](/l/tr/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add). - ---- - -## Yerel sunucuyu yönetme - -Yerel Twenty konteynerini kontrol etmek için `yarn twenty server` kullanın: - -| Komut | Ne yapar | -| -------------------------------------- | --------------------------------------------------------- | -| `yarn twenty server start` | Sunucuyu başlatır (gerekirse imajı çeker) | -| `yarn twenty server start --port 3030` | Özel bir portta başlatır | -| `yarn twenty server stop` | Sunucuyu durdurur (verileri korur) | -| `yarn twenty server status` | URL'yi, sürümü ve oturum açma kimlik bilgilerini gösterir | -| `yarn twenty server logs` | Sunucu günlüklerini akış olarak iletir | -| `yarn twenty server reset` | Verileri siler ve sıfırdan başlatır | -| `yarn twenty server upgrade` | En güncel `twenty-app-dev` imajını çeker | -| `yarn twenty server upgrade 2.2.0` | Belirli bir sürüme yükseltin | - -Veriler, yeniden başlatmalar arasında iki Docker biriminde kalıcıdır (PostgreSQL için `twenty-app-dev-data`, dosyalar için `twenty-app-dev-storage`). Her şeyi silmek için `reset` kullanın. - -### Sunucu imajını yükseltme - -`yarn twenty server upgrade`, en güncel imajı çeker, özetleri karşılaştırır ve yalnızca gerçekten bir şey değiştiyse konteyneri yeniden oluşturur. Birimler korunur — yalnızca konteyner değiştirilir. Yeni bir imaj çekildiyse ve konteyner çalışıyorsa, yükseltme otomatik olarak yeni bir konteyner başlatır; sağlıklı hale gelmesini beklemek için ardından `yarn twenty server start` çalıştırın. - -```bash filename="Terminal" -yarn twenty server upgrade # Latest -yarn twenty server upgrade 2.2.0 # Specific version -``` - -`yarn twenty server status` ile çalışan sürümü doğrulayabilirsiniz (konteynere gömülü `APP_VERSION` değerini gösterir). - -### Paralel bir test örneği çalıştırma - -`server` komutlarının herhangi birine `--test` parametresini vererek ikinci, tamamen yalıtılmış bir örneği yönetin — entegrasyon testlerini çalıştırmak veya ana geliştirme verilerinize dokunmadan denemeler yapmak için kullanışlıdır: - -| Komut | Ne yapar | -| ----------------------------------- | ------------------------------------------------------------- | -| `yarn twenty server start --test` | Test örneğini başlatır (varsayılan bağlantı noktası 2021'dir) | -| `yarn twenty server stop --test` | Durdurun | -| `yarn twenty server status --test` | Durumunu gösterin | -| `yarn twenty server logs --test` | Günlüklerini akış olarak izleyin | -| `yarn twenty server reset --test` | Verilerini silin | -| `yarn twenty server upgrade --test` | İmajını yükseltin | - -Test örneği, kendine ait bir Docker konteynerinde (`twenty-app-dev-test`), ayrılmış birimlerle (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) ve yapılandırmayla çalışır; böylece ana örneğinizle çakışma olmadan paralel olarak çalışabilir. Varsayılan 2021'i geçersiz kılmak için `--test` ile `--port`'u birlikte kullanın. - ---- - -## Manuel kurulum (iskelet oluşturucu olmadan) - -SDK'yı mevcut bir projeye ekliyorsanız iskelet oluşturma adımını atlayın: - -```bash filename="Terminal" -yarn add twenty-sdk twenty-client-sdk -``` - -`package.json` dosyasına betiği ekleyin: - -```json filename="package.json" -{ - "scripts": { - "twenty": "twenty" - } -} -``` - -Artık `yarn twenty dev`, `yarn twenty server start` ve diğerlerini çalıştırabilirsiniz. - - -`twenty-sdk`'yi global olarak kurmayın — her uygulama kendi sürümünü kullansın diye proje bazında sabitleyin. - - ---- - -## Sorun Giderme - -* **Docker hataları** — `yarn twenty server start` öncesinde Docker Desktop'ın (veya daemon'ın) çalıştığından emin olun. Hata iletisi, işletim sisteminiz için doğru başlatma komutunu gösterecektir. -* **Yanlış Node sürümü** — 24+ gerekir. `node -v` ile kontrol edin. -* **Yarn 4 eksik** — `corepack enable` çalıştırın. -* **Bağımlılıklar bozuk** — `rm -rf node_modules && yarn install`. - -Takıldınız mı? [Twenty Discord](https://discord.com/channels/1130383047699738754/1130386664812982322) üzerinde yardım isteyin. diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/layout.mdx deleted file mode 100644 index 3d58e1acac..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Düzen -description: Uygulamanızın Twenty içinde nasıl görüneceğini şekillendirmek için görünümleri, gezinme menüsü öğelerini ve sayfa düzenlerini tanımlayın. -icon: table-columns ---- - -Düzen varlıkları, uygulamanızın Twenty arayüzünde nasıl göründüğünü kontrol eder — kenar çubuğunda nelerin yer aldığı, uygulamayla birlikte hangi kayıtlı görünümlerin geldiği ve bir kayıt ayrıntı sayfasının nasıl düzenlendiği. - -## Düzen kavramları - -| Kavram | Neyi kontrol eder | Varlık | -| ------------------------ | ----------------------------------------------------------------------------------------------- | -------------------------- | -| **Görünüm** | Bir nesne için kaydedilmiş liste yapılandırması — görünür alanlar, sıralama, filtreler, gruplar | `defineView` | -| **Gezinme Menüsü Öğesi** | Sol kenar çubuğunda, bir görünüme veya harici bir URL'ye bağlanan bir öğe | `defineNavigationMenuItem` | -| **Sayfa Düzeni** | Bir kaydın ayrıntı sayfasını oluşturan sekmeler ve widget'lar | `definePageLayout` | -| **Sayfa düzeni sekmesi** | Mevcut bir sayfa düzenine (standart veya kendi uygulamanıza ait) eklenen bağımsız bir sekme | `definePageLayoutTab` | - -Görünümler, gezinme menüsü öğeleri ve sayfa düzenleri birbirlerine `universalIdentifier` ile başvurur: - -* Türü `VIEW` olan bir **gezinme menüsü öğesi**, bir `defineView` tanımlayıcısını işaret eder; böylece kenar çubuğu bağlantısı o kayıtlı görünümü açar. -* Türü `RECORD_PAGE` olan bir **sayfa düzeni**, bir nesneyi hedefler ve sekmelerinin içine widget'lar olarak [ön uç bileşenleri](/l/tr/developers/extend/apps/front-components) gömebilir. - - - - -Görünümler, bir nesnenin kayıtlarının nasıl görüntüleneceğine ilişkin kaydedilmiş yapılandırmalardır — hangi alanların görünür olacağını, sıralarını ve uygulanan filtreleri veya grupları içerir. Uygulamanızla önceden yapılandırılmış görünümler sunmak için `defineView()` kullanın: - -```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, - }, - ], -}); -``` - -Önemli noktalar: -* `objectUniversalIdentifier`, bu görünümün hangi nesneye uygulanacağını belirtir. -* `key`, görünüm türünü belirler (ör. ana liste görünümü için `ViewKey.INDEX`). -* `fields`, hangi sütunların görüneceğini ve sıralarını kontrol eder. Her alan bir `fieldMetadataUniversalIdentifier` öğesine referans verir. -* Daha gelişmiş yapılandırmalar için `filters`, `filterGroups`, `groups` ve `fieldGroups` de tanımlayabilirsiniz. -* `position`, aynı nesne için birden fazla görünüm olduğunda sıralamayı kontrol eder. - - - - -Gezinme menüsü öğeleri, çalışma alanı kenar çubuğuna özel girişler ekler. Görünümlere, harici URL'lere veya nesnelere bağlanmak için `defineNavigationMenuItem()` kullanın: - -```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, -}); -``` - -Önemli noktalar: -* `type`, menü öğesinin neye bağlanacağını belirler: kaydedilmiş bir görünüm için `NavigationMenuItemType.VIEW` veya harici bir URL için `NavigationMenuItemType.LINK`. -* Görünüm bağlantıları için `viewUniversalIdentifier` ayarlayın. Harici bağlantılar için `link` ayarlayın. -* `position`, kenar çubuğundaki sıralamayı kontrol eder. -* `icon` ve `color` (isteğe bağlı) görünümü özelleştirir. - - - - -Sayfa düzenleri, bir kayıt ayrıntı sayfasının nasıl görüneceğini özelleştirmenizi sağlar — hangi sekmelerin görüneceği, her sekmenin içinde hangi widget'ların olacağı ve bunların nasıl düzenleneceği. Uygulamanızla özel düzenler sunmak için `definePageLayout()` kullanın: - -```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, - }, - }, - ], - }, - ], -}); -``` - -Önemli noktalar: -* `type` genellikle belirli bir nesnenin ayrıntı görünümünü özelleştirmek için `'RECORD_PAGE'` olur. -* `objectUniversalIdentifier`, bu düzenin hangi nesneye uygulanacağını belirtir. -* Her `tab`, bir `title`, `position` ve `layoutMode` ile sayfanın bir bölümünü tanımlar (serbest biçimli düzen için `CANVAS`). -* Bir sekmenin içindeki her `widget`, bir ön uç bileşeni, bir ilişki listesi veya diğer yerleşik widget türlerini oluşturabilir. -* Sekmelerdeki `position`, sıralarını kontrol eder. Özel sekmeleri yerleşik olanların sonrasına yerleştirmek için daha yüksek değerler kullanın (ör. 50). - - - - -`definePageLayoutTab` uygulamanızın tek bir sekmeyi — isteğe bağlı widget'larla — **mevcut** bir sayfa düzenine eklemesine olanak tanır. En yaygın kullanım örneği, Twenty'nin yerleşik kayıt sayfalarından birine (örneğin, bir analitik veya yapay zekâ özet sekmesi) özel bir sekme eklemek ya da kendi uygulamanızın zaten sunduğu bir sayfa düzenine eklemektir. - -Hedeflenen sayfa düzeni ya **standart** bir Twenty sayfa düzeni ya da **kendi uygulamanız** tarafından tanımlanan bir düzen olmalıdır; yüklü başka bir uygulamaya ait sayfa düzenlerine uygulamalar arası referanslar şu anda desteklenmemektedir. - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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, - }, - }, - ], -}); -``` - -Önemli noktalar: -* `pageLayoutUniversalIdentifier`, `definePageLayoutTab` kullanılırken **zorunludur** ve kurulum sırasında (standart veya uygulamanızınki) zaten var olan bir sayfa düzenini işaret etmelidir. Üst sayfa düzeni eksikse, kurulum belirgin bir doğrulama hatasıyla başarısız olur. -* `widgets` yalnızca bu sekmeyle sınırlıdır — satır içi olarak `definePageLayout` içinde tanımlanan widget'larda olduğu gibi, ön uç bileşenlerine, görünümlere vb. tam olarak aynı şekilde referans verirler. -* `position`, hedeflenen düzende mevcut sekmelere göre sıralamayı kontrol eder. Yerleşik sekmelere göre sekmenizi istediğiniz konuma yerleştirecek bir değer seçin. -* Yalnızca mevcut bir düzene **ekleme** yapmak istediğinizde `definePageLayout` yerine bunu kullanın. Tüm düzen size ait olduğunda `definePageLayout` kullanın (genellikle uygulamanızda sunduğunuz bir nesne için bir `RECORD_PAGE` veya bir `STANDALONE_PAGE`). - - - diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index 64a0f33448..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,566 +0,0 @@ ---- -title: Mantıksal işlevler -description: Sunucu tarafı TypeScript işlevlerini HTTP, cron ve veritabanı olay tetikleyicileriyle tanımlayın. -icon: bolt ---- - -Mantık işlevleri, Twenty platformunda çalışan sunucu tarafı TypeScript işlevleridir. HTTP istekleri, cron zamanlamaları veya veritabanı olayları tarafından tetiklenebilirler — ve ayrıca yapay zekâ ajanları için araçlar olarak sunulabilirler. - - - - -Her fonksiyon dosyası, bir işleyici ve isteğe bağlı tetikleyiciler içeren bir yapılandırmayı dışa aktarmak için `defineLogicFunction()` kullanır. - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - isAuthRequired: true, - }, - /*databaseEventTriggerSettings: { - eventName: 'people.created', - },*/ - /*cronTriggerSettings: { - pattern: '0 0 1 1 *', - },*/ -}); -``` - -Kullanılabilir tetikleyici türleri: -* **httpRoute**: Fonksiyonunuzu bir HTTP yolu ve yöntemiyle **`/s/` uç noktasının altında** kullanıma sunar: -> örn. `path: '/post-card/create'` `https://your-twenty-server.com/s/post-card/create` adresinden çağrılabilir -* **cron**: Bir CRON ifadesi kullanarak fonksiyonunuzu bir zamanlamayla çalıştırır. -* **databaseEvent**: Çalışma alanı nesnesi yaşam döngüsü olaylarında çalışır. Olay işlemi `updated` olduğunda, dinlenecek belirli alanlar `updatedFields` dizisinde belirtilebilir. Tanımsız veya boş bırakılırsa, herhangi bir güncelleme fonksiyonu tetikler. -> örn. `person.updated`, `*.created`, `company.*` - - -Bir fonksiyonu CLI kullanarak manuel olarak da çalıştırabilirsiniz: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -Günlükleri şu şekilde izleyebilirsiniz: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### Rota tetikleyicisi yükü - -Bir rota tetikleyicisi mantık fonksiyonunuzu çağırdığında, -[AWS HTTP API v2 formatını](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) izleyen bir `RoutePayload` nesnesi alır. -`RoutePayload` türünü `twenty-sdk` içinden içe aktarın: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -`RoutePayload` türünün yapısı şu şekildedir: - - | Özellik | Tür | Açıklama | Örnek | - | ---------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | HTTP başlıkları (`forwardedRequestHeaders` içinde listelenenlerle sınırlı) | aşağıdaki bölüme bakın | - | `queryStringParameters` | `Record\` | Sorgu dizesi parametreleri (birden çok değer virgülle birleştirilir) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | Rota deseninden çıkarılan yol parametreleri | `/users/:id`, `/users/123` -> `{ id: '123' }` | - | `body` | `object \| null` | Ayrıştırılmış istek gövdesi (JSON) | `{ id: 1 }` -> `{ id: 1 }` | - | `rawBody` | `string \| undefined` | JSON ayrıştırılmadan önceki özgün UTF-8 istek gövdesi. HMAC tarzı webhook imzalarını doğrulamak için kullanışlıdır (ör. GitHub'ın `X-Hub-Signature-256`, Stripe). Çalışma zamanı onu korumadığında `undefined` olur. | | - | `isBase64Encoded` | `boolean` | Gövdenin base64 ile kodlanıp kodlanmadığı | | - | `requestContext.http.method` | `string` | HTTP yöntemi (GET, POST, PUT, PATCH, DELETE) | | - | `requestContext.http.path` | `string` | Ham istek yolu | | - - -#### forwardedRequestHeaders - -Varsayılan olarak, güvenlik nedenleriyle gelen isteklerden HTTP başlıkları mantık fonksiyonunuza **aktarılmaz**. -Belirli başlıklara erişmek için bunları `forwardedRequestHeaders` dizisinde listeleyin: - -```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'], - }, -}); -``` - -İşleyicinizde, iletilen başlıklara şu şekilde erişin: - -```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 }; -}; -``` - - -Başlık adları küçük harfe normalize edilir. Onlara küçük harfli anahtarlarla erişin (örneğin, `event.headers['content-type']`). - - -#### Bir işlevi bir yapay zekâ aracı veya iş akışı eylemi olarak kullanıma sunma - -Mantık işlevleri, her birinin kendi tetikleyicisi olacak şekilde iki yerde kullanılabilir hâle getirilebilir: - -* **`toolTriggerSettings`** — işlevi Twenty'nin yapay zekâ özellikleri (sohbet, MCP, işlev çağırma) tarafından bulunabilir hâle getirir. Standart JSON Şeması'nı kullanır; LLM'lerin doğal olarak anladığı biçimdir. -* **`workflowActionTriggerSettings`** — işlevin görsel iş akışı oluşturucusunda bir adım olarak görünmesini sağlar. Oluşturucunun uygun alan düzenleyicilerini, değişken seçicilerini ve etiketleri oluşturabilmesi için Twenty'nin zengin `InputSchema`'sını kullanır. - -Bir işlev bunlardan birini, diğerini veya her ikisini de tercih edebilir. Bunlar, `cronTriggerSettings`, `databaseEventTriggerSettings` ve `httpRouteTriggerSettings` ile birlikte yer alır — aynı desen, aynı biçim. - -```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: {}, -}); -``` - -Önemli noktalar: - -* Bir işlev yüzeyleri karıştırabilir — onu sohbette VE iş akışı oluşturucusunda kullanıma sunmak için hem `toolTriggerSettings` hem de `workflowActionTriggerSettings` bildirin. -* `toolTriggerSettings.inputSchema` ve `workflowActionTriggerSettings.inputSchema` ikisi de isteğe bağlıdır. Atlandığında, manifest oluşturucu bunları işleyici kaynak kodundan çıkarır (yapay zekâ aracı için JSON Şeması, iş akışı eylemi için Twenty'nin `InputSchema`'sı). Daha zengin tipleme istediğinizde birini açıkça belirtin — örneğin, iş akışı oluşturucu için `FieldMetadataType`'ı bilen `CURRENCY` veya `RELATION` gibi alanlarla ya da yapay zekâ aracısının okuyabileceği `description` alanlarıyla: - -```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'], - }, - }, -}); -``` - - -**İyi bir `description` yazın.** AI ajanları, aracı ne zaman kullanacaklarına karar vermek için işlevin `description` alanına güvenir. Aracın ne yaptığını ve ne zaman çağrılması gerektiğini açıkça belirtin. - - - - - -Kurulum sonrası işlev, uygulamanız bir çalışma alanına yüklendikten sonra otomatik olarak çalışan bir mantık işlevidir. Sunucu, uygulamanın meta verileri senkronize edildikten ve SDK istemcisi oluşturulduktan **sonra** bunu yürütür; böylece çalışma alanı tamamen kullanıma hazırdır ve yeni şema kullanıma alınmıştır. Tipik kullanım örnekleri arasında varsayılan verilerin tohumlanması, başlangıç kayıtlarının oluşturulması, çalışma alanı ayarlarının yapılandırılması veya üçüncü taraf hizmetlerde kaynak sağlanması yer alır. - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Ayrıca kurulum sonrası işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz: - -```bash filename="Terminal" -yarn twenty exec --postInstall -``` - -Önemli noktalar: -* Kurulum sonrası işlevler `definePostInstallLogicFunction()` kullanır — tetikleyici ayarlarını atlayan (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`) özel bir varyanttır. -* İşleyici, `{ previousVersion?: string; newVersion: string }` içeren bir `InstallPayload` alır — `newVersion`, yüklenen sürümdür; `previousVersion` ise daha önce yüklü olan sürümdür (veya ilk kurulumda `undefined`). Bu değerleri ilk kurulumları yükseltmelerden ayırt etmek ve sürüme özgü geçiş (migration) mantığını çalıştırmak için kullanın. -* **Kanca ne zaman çalışır**: varsayılan olarak yalnızca ilk kurulumlarda. Uygulama önceki bir sürümden yükseltildiğinde de çalışmasını istiyorsanız `shouldRunOnVersionUpgrade: true` geçin. Belirtilmediğinde, bayrak varsayılan olarak `false` olur ve yükseltmeler kancayı atlar. -* **Yürütme modeli — varsayılan olarak eşzamansız, isteğe bağlı senkron**: `shouldRunSynchronously` bayrağı kurulum sonrası işlemin *nasıl* yürütüldüğünü kontrol eder. - * `shouldRunSynchronously: false` *(varsayılan)* — kanca, `retryLimit: 3` ile **mesaj kuyruğuna alınır** ve bir worker içinde eşzamansız çalışır. İş kuyruğa alınır alınmaz kurulum yanıtı döner; dolayısıyla yavaşlayan veya hata veren bir işleyici çağıranı engellemez. Worker en fazla üç kez yeniden deneyecektir. **Bunu uzun süre çalışan işler için kullanın** — büyük veri kümelerini tohumlama, yavaş üçüncü taraf API'lerini çağırma, harici kaynakları sağlama; makul bir HTTP yanıt süresini aşabilecek her şey. - * `shouldRunSynchronously: true` — kanca **kurulum akışı sırasında satır içi** olarak yürütülür (kurulum öncesi ile aynı yürütücü). İşleyici bitene kadar kurulum isteği engellenir; hata fırlatırsa, kurulum çağıranı bir `POST_INSTALL_ERROR` alır. Otomatik yeniden deneme yok. **Bunu, yanıt dönmeden mutlaka tamamlanması gereken hızlı işler için kullanın** — örneğin, kullanıcıya bir doğrulama hatası iletmek veya kurulum çağrısı döner dönmez istemcinin ihtiyaç duyacağı hızlı bir kurulum yapmak. Kurulum sonrası çalıştığında, üstveri (metadata) geçişinin zaten uygulanmış olduğunu unutmayın; bu nedenle, senkron moddaki bir hata şema değişikliklerini **geri almaz** — yalnızca hatayı görünür kılar. -* İşleyicinizin idempotent olduğundan emin olun. Eşzamansız modda kuyruk en fazla üç kez yeniden deneyebilir; her iki modda da `shouldRunOnVersionUpgrade: true` iken yükseltmelerde kanca tekrar çalışabilir. -* Ortam değişkenleri `APPLICATION_ID`, `APP_ACCESS_TOKEN` ve `API_URL` işleyici içinde kullanılabilir (diğer mantık işlevlerinde olduğu gibi), böylece uygulamanıza özel kapsamda bir uygulama erişim belirteciyle Twenty API'sini çağırabilirsiniz. -* Uygulama başına yalnızca bir kurulum sonrası işlevine izin verilir. Birden fazla tespit edilirse manifest oluşturma hataya düşer. -* İşlevin `universalIdentifier`, `shouldRunOnVersionUpgrade` ve `shouldRunSynchronously` değerleri, derleme sırasında uygulama manifestine `postInstallLogicFunction` alanı altında otomatik olarak eklenir — bunlara `defineApplication()` içinde atıfta bulunmanıza gerek yoktur. -* Varsayılan zaman aşımı, veri tohumlama gibi daha uzun kurulum görevlerine izin vermek için 300 saniye (5 dakika) olarak ayarlanmıştır. -* **Geliştirme modunda çalıştırılmaz**: bir uygulama yerel olarak kaydedildiğinde (`yarn twenty dev` aracılığıyla), sunucu kurulum akışını tamamen atlar ve dosyaları doğrudan CLI watcher üzerinden eşitler — bu nedenle, `shouldRunSynchronously` ne olursa olsun, kurulum sonrası geliştirme modunda hiç çalışmaz. Çalışan bir çalışma alanında bunu elle tetiklemek için `yarn twenty exec --postInstall` kullanın. - - - - -Kurulum öncesi işlev, kurulum sırasında otomatik olarak çalışan ve **çalışma alanı üstveri (metadata) geçişi uygulanmadan önce** yürütülen bir mantık işlevidir. Kurulum sonrası ile (`InstallPayload`) aynı yük (payload) biçimini paylaşır, ancak kurulum akışında daha erken konumlandığından yaklaşan geçişin bağlı olduğu durumu hazırlayabilir — tipik kullanımlar arasında verileri yedeklemek, yeni şemayla uyumluluğu doğrulamak veya yeniden yapılandırılacak ya da kaldırılacak kayıtları arşivlemek yer alır. - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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, -}); -``` - -Ayrıca kurulum öncesi işlevi istediğiniz zaman CLI kullanarak manuel olarak çalıştırabilirsiniz: - -```bash filename="Terminal" -yarn twenty exec --preInstall -``` - -Önemli noktalar: -* Kurulum öncesi işlevler `definePreInstallLogicFunction()` kullanır — kurulum sonrasıyla aynı özel yapılandırma, sadece yaşam döngüsünde farklı bir yuvaya eklenir. -* Hem kurulum öncesi hem de kurulum sonrası işleyiciler aynı `InstallPayload` türünü alır: `{ previousVersion?: string; newVersion: string }`. Bunu bir kez içe aktarın ve her iki kanca için yeniden kullanın. -* **Kanca ne zaman çalışır**: çalışma alanı üstveri (metadata) geçişinden hemen önce konumlandırılır (`synchronizeFromManifest`). Çalıştırmadan önce, sunucu yalnızca ekleyici bir "indirgenmiş eşitleme" yürütür; bu, çalışma alanı üstverisinde **yeni** sürümün kurulum öncesi işlevini kaydeder — başka hiçbir şeye dokunulmaz — ve ardından bunu yürütür. Bu eşitleme yalnızca ekleyici olduğundan, işleyiciniz çalıştığında önceki sürümün nesneleri, alanları ve verileri hâlâ sağlamdır: geçiş öncesi durumu güvenle okuyabilir ve yedekleyebilirsiniz. -* **Yürütme modeli**: kurulum öncesi **senkron** olarak yürütülür ve **kurulumu bloklar**. İşleyici bir hata fırlatırsa, herhangi bir şema değişikliği uygulanmadan önce kurulum iptal edilir — çalışma alanı, tutarlı bir durumda önceki sürümde kalır. Bu kasıtlıdır: kurulum öncesi, riskli bir yükseltmeyi reddetmek için son şansınızdır. -* Kurulum sonrası ile aynı şekilde, uygulama başına yalnızca bir kurulum öncesi işlevine izin verilir. Derleme sırasında uygulama manifestine `preInstallLogicFunction` altında otomatik olarak eklenir. -* **Geliştirme modunda çalıştırılmaz**: kurulum sonrasında olduğu gibi — yerel olarak kaydedilen uygulamalarda kurulum akışı tamamen atlanır, bu nedenle `yarn twenty dev` altında kurulum öncesi hiç çalışmaz. Bunu elle tetiklemek için `yarn twenty exec --preInstall` kullanın. - - - - -Her iki kanca da aynı kurulum akışının parçasıdır ve aynı `InstallPayload`'ı alır. Fark, çalışma alanı üstveri (metadata) geçişine göre **ne zaman** çalıştıklarıdır ve bu, güvenle erişebilecekleri verileri değiştirir. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -Kurulum öncesi her zaman **senkron**dur (kurulumu bloke eder ve iptal edebilir). Kurulum sonrası **varsayılan olarak asenkron**dur — otomatik yeniden denemelerle bir worker üzerinde kuyruğa alınır — ancak `shouldRunSynchronously: true` ile senkron yürütmeye geçebilir. Her modun ne zaman kullanılacağı için yukarıdaki `definePostInstallLogicFunction` akordeonuna bakın. - -**Yeni şemanın mevcut olmasını gerektiren her şey için `post-install` kullanın.** Bu yaygın durumdur: - -* Yeni eklenen nesne ve alanlara karşı varsayılan verileri tohumlama (ilk kayıtları, varsayılan görünümleri, demo içeriği oluşturma). -* Uygulamanın kimlik bilgileri artık mevcut olduğuna göre, üçüncü taraf hizmetlerle webhook'ları kaydetmek. -* Eşitlenmiş üstveriye (metadata) bağlı kurulumu tamamlamak için kendi API'nizi çağırmak. -* Her yükseltmede durumu uzlaştırması gereken idempotent "bu mevcut olsun" mantığı — `shouldRunOnVersionUpgrade: true` ile birleştirin. - -Örnek — kurulumdan sonra varsayılan bir `PostCard` kaydı tohumlama: - -```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 => { - 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, -}); -``` - -**Bir geçiş mevcut verileri aksi takdirde silecek veya bozacaksa `pre-install` kullanın.** Kurulum öncesi *önceki* şemaya karşı çalıştığı ve hatalandığında yükseltmeyi geri aldığı için, riskli olan her şey için doğru yerdir: - -* **Kaldırılmak veya yeniden yapılandırılmak üzere olan verileri yedekleme** — örn. v2'de bir alanı kaldırıyorsunuz ve geçiş çalışmadan önce değerlerini başka bir alana kopyalamanız veya depolamaya aktarmanız gerekiyor. -* **Yeni bir kısıtın geçersiz kılacağı kayıtları arşivleme** — örn. bir alan `NOT NULL` oluyor ve önce null değerli satırları silmeniz veya düzeltmeniz gerekiyor. -* **Uyumluluğu doğrulama ve mevcut veriler temiz bir şekilde geçirilemiyorsa yükseltmeyi reddetme** — işleyiciden hata fırlatın ve kurulum, herhangi bir değişiklik uygulanmadan iptal edilir. Bu, uyumsuzluğu geçişin ortasında keşfetmekten daha güvenlidir. -* İlişkilendirmeyi kaybettirecek bir şema değişikliğinden önce **verileri yeniden adlandırma veya yeniden anahtarlama**. - -Örnek — yıkıcı bir geçişten önce kayıtları arşivleme: - -```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 => { - // 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, -}); -``` - -**Kural olarak:** - -| Şunu yapmak istiyorsunuz... | Kullan | -| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | -| Varsayılan verileri tohumlamak, çalışma alanını yapılandırmak, harici kaynakları kaydetmek | `post-install` | -| Kurulum yanıtını engellememesi gereken uzun süreli tohumlama veya üçüncü taraf çağrılarını çalıştırmak | `post-install` (varsayılan — `shouldRunSynchronously: false`, worker yeniden denemeleriyle) | -| Kurulum çağrısı döner dönmez çağıranın güveneceği hızlı kurulumu çalıştırmak | `post-install` ile `shouldRunSynchronously: true` | -| Yaklaşan geçişin kaybedeceği verileri okumak veya yedeklemek | `pre-install` | -| Mevcut verileri bozacak bir yükseltmeyi reddetmek | `pre-install` (işleyiciden hata fırlatmak) | -| Her yükseltmede uzlaştırma çalıştırmak | `post-install` ile `shouldRunOnVersionUpgrade: true` | -| Yalnızca ilk kurulumda tek seferlik kurulum yapmak | `post-install` ile `shouldRunOnVersionUpgrade: false` (varsayılan) | - - -Emin değilseniz, varsayılan olarak **kurulum sonrası**nı tercih edin. Yalnızca geçişin kendisi yıkıcıysa ve önceki durum yok olmadan önce onu yakalamanız gerekiyorsa kurulum öncesine başvurun. - - - - - -## Tipli API istemcileri (twenty-client-sdk) - -`twenty-client-sdk` paketi, mantık fonksiyonlarınızdan ve ön uç bileşenlerinizden Twenty API ile etkileşim kurmak için tip tanımlı iki GraphQL istemcisi sağlar. - -| İstemci | İçe Aktar | Uç nokta | Oluşturuldu mu? | -| ------------------- | ---------------------------- | ------------------------------------------------------------- | --------------------------------------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — çalışma alanı verileri (kayıtlar, nesneler) | Evet, geliştirme/derleme zamanında | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — çalışma alanı yapılandırması, dosya yüklemeleri | Hayır, önceden hazırlanmış olarak gelir | - - - - -`CoreApiClient`, çalışma alanı verilerini sorgulamak ve değiştirmek için ana istemcidir. `yarn twenty dev` veya `yarn twenty build` sırasında **çalışma alanı şemanızdan oluşturulur**, bu nedenle nesnelerinize ve alanlarınıza uyacak şekilde tamamen tiplenmiştir. - -```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, - }, -}); -``` - -İstemci bir seçim kümesi sözdizimi kullanır: Bir alanı dahil etmek için `true` geçin, bağımsız değişkenler için `__args` kullanın ve ilişkiler için nesneleri iç içe yerleştirin. Çalışma alanı şemanıza göre tam otomatik tamamlama ve tip denetimi elde edersiniz. - - -**CoreApiClient geliştirme/derleme zamanında oluşturulur.** Bunu önce `yarn twenty dev` veya `yarn twenty build` çalıştırmadan kullanırsanız, bir hata verir. Oluşturma otomatik olarak gerçekleşir — CLI, çalışma alanınızın GraphQL şemasını inceler ve `@genql/cli` kullanarak tiplenmiş bir istemci üretir. - - -#### Tür açıklamaları için CoreSchema'yı kullanma - -`CoreSchema`, çalışma alanı nesnelerinize uyan TypeScript türleri sağlar — bileşen durumunu veya işlev parametrelerini tiplemek için kullanışlıdır: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`MetadataApiClient`, SDK ile birlikte önceden hazırlanmış olarak gelir (oluşturma gerektirmez). Çalışma alanı yapılandırması, uygulamalar ve dosya yüklemeleri için `/metadata` uç noktasını sorgular. - -```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 }, - }, - }, -}); -``` - -#### Dosya yükleme - -`MetadataApiClient`, dosya türündeki alanlara dosya eklemek için bir `uploadFile` yöntemi içerir: - -```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://...' } -``` - -| Parametre | Tür | Açıklama | -| ---------------------------------- | -------- | --------------------------------------------------------------------------------- | -| `fileBuffer` | `Buffer` | Dosyanın ham içeriği | -| `filename` | `string` | Dosyanın adı (depolama ve görüntüleme için kullanılır) | -| `contentType` | `string` | MIME türü (belirtilmezse varsayılan olarak `application/octet-stream` kullanılır) | -| `fieldMetadataUniversalIdentifier` | `string` | Nesnenizdeki dosya türü alanının `universalIdentifier` değeri | - -Önemli noktalar: -* Alan için `universalIdentifier` kullanır (çalışma alanına özgü kimliği değil), böylece yükleme kodunuz uygulamanızın yüklü olduğu herhangi bir çalışma alanında çalışır. -* Döndürülen `url`, yüklenen dosyaya erişmek için kullanabileceğiniz imzalı bir URL'dir. - - - - - - Kodunuz Twenty üzerinde çalıştığında (mantık işlevleri veya ön uç bileşenleri), platform kimlik bilgilerini ortam değişkenleri olarak enjekte eder: - - * `TWENTY_API_URL` — Twenty API'nin temel URL'si - * `TWENTY_APP_ACCESS_TOKEN` — Uygulamanızın varsayılan işlev rolü kapsamında kısa ömürlü bir anahtar - - Bunları istemcilere iletmeniz gerekmez — otomatik olarak `process.env`'den okurlar. API anahtarının izinleri, `application-config.ts` içinde `defaultRoleUniversalIdentifier` ile referans verilen role göre belirlenir. - diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/publishing.mdx deleted file mode 100644 index 487f85395f..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Yayımlama -icon: yükle -description: Twenty uygulamanızı pazaryerine sunun ya da dahili olarak dağıtın. ---- - -## Genel Bakış - -Uygulamanız [yerelde derlenip test edildikten sonra](/l/tr/developers/extend/apps/building), dağıtım için iki yolunuz vardır: - -* **Bir tar arşivi dağıtın** — uygulamanızı dahili veya özel kullanım için doğrudan belirli bir Twenty sunucusuna yükleyin. -* **npm’ye yayımlama** — uygulamanızı Twenty pazaryerinde listeleyin; böylece herhangi bir çalışma alanı keşfedip yükleyebilir. - -Her iki yol da aynı **build** adımından başlar. - -## Uygulamanızı derleme - -Uygulamanızı derlemek ve dağıtıma hazır bir `manifest.json` oluşturmak için build komutunu çalıştırın: - -```bash filename="Terminal" -yarn twenty build -``` - -Bu işlem TypeScript kaynaklarını derler, mantık işlevlerini ve ön uç bileşenlerini transpile eder ve her şeyi `.twenty/output/` konumuna yazar. El ile dağıtım veya deploy komutu için bir `.tgz` paketini de üretmek amacıyla `--tarball` ekleyin. - -## Sunucuya dağıtım (tarball) - -Genel kullanıma açık olmasını istemediğiniz uygulamalar — sahipli araçlar, yalnızca kurumsal entegrasyonlar veya deneysel derlemeler — için bir tarball’ı doğrudan bir Twenty sunucusuna dağıtabilirsiniz. - -### Ön Gereksinimler - -Dağıtmadan önce, hedef sunucuyu işaret eden yapılandırılmış bir remote’a ihtiyacınız vardır. Remote’lar sunucu URL’sini ve kimlik doğrulama bilgilerini yerel olarak `~/.twenty/config.json` içinde saklar. - -Bir remote ekleyin: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### Dağıtım - -Uygulamanızı tek adımda derleyip sunucuya yükleyin: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### Dağıtılmış bir uygulamayı paylaşma - - -Özel (tarball) uygulamaları çalışma alanları arasında paylaşma bir **Kurumsal** özelliktir. **Dağıtım** sekmesi, çalışma alanınız geçerli bir Kurumsal anahtara sahip olana kadar paylaşım kontrolleri yerine bir yükseltme istemi gösterecektir. Etkinleştirmek için [Ayarlar > Yönetim Paneli > Kurumsal](/settings/admin-panel#enterprise) bölümüne gidin. - - -Tarball uygulamaları genel pazar yerinde listelenmez; bu nedenle aynı sunucudaki diğer çalışma alanları gezinerek onları keşfedemez. Çalışma alanınız Kurumsal planda olduğunda, yayınlanmış bir uygulamayı şu şekilde paylaşabilirsiniz: - -1. **Ayarlar > Uygulamalar > Kayıtlar** bölümüne gidin ve uygulamanızı açın -2. **Dağıtım** sekmesinde, **Paylaşım bağlantısını kopyala**’ya tıklayın -3. Bu bağlantıyı diğer çalışma alanlarındaki kullanıcılarla paylaşın — onları doğrudan uygulamanın yükleme sayfasına götürür - -Paylaşım bağlantısı, sunucunun temel URL’sini (herhangi bir çalışma alanı alt alan adı olmadan) kullanır; böylece sunucudaki herhangi bir çalışma alanı için çalışır. - -### Sürüm yönetimi - -Halihazırda dağıtılmış bir tarball uygulamasını güncellerken, sunucu `package.json` içindeki `version` değerinin, şu anda dağıtılmış sürümden ([semver](https://semver.org) sıralamasına göre) **kesinlikle daha yüksek** olmasını gerektirir. Aynı sürümü yeniden dağıtmak veya daha düşük bir sürümü göndermek, tarball depolanmadan önce reddedilir — CLI'de `VERSION_ALREADY_EXISTS` hatasını görürsünüz. - -Bir güncelleme yayımlamak için: - -1. `package.json` içindeki `version` alanını artırın (ör. `1.2.3` → `1.2.4`, `1.3.0` veya `2.0.0`) -2. `yarn twenty deploy` (veya `yarn twenty deploy --remote production`) komutunu çalıştırın -3. Uygulamayı kurmuş olan çalışma alanları, ayarlarında mevcut güncellemeyi görecektir - - -Ön sürüm etiketleri beklendiği gibi çalışır: `1.0.0-rc.1` → `1.0.0-rc.2` sürümünü artırmak mümkündür ve `1.0.0` gibi nihai bir sürüm, `1.0.0-rc.5` sürümünden daha yüksek olarak doğru şekilde tanınır. `package.json` içindeki sürümün kendisi geçerli bir semver dizesi olmalıdır. - - -{/* TODO: add screenshot of the Upgrade button */} - -### Sunucu sürümü uyumluluğu - -Uygulamanız belirli bir Twenty sunucu sürümünde sunulan bir özelliği kullanıyorsa (örneğin, v2.3.0'da eklenen OAuth sağlayıcıları), uygulamanızın gerektirdiği en düşük sunucu sürümünü `package.json` içindeki `engines.twenty` alanını kullanarak belirtmelisiniz: - -```json filename="package.json" -{ - "name": "twenty-my-app", - "version": "1.0.0", - "engines": { - "node": "^24.5.0", - "twenty": ">=2.3.0" - } -} -``` - -Değer, standart bir [semver aralığı](https://github.com/npm/node-semver#ranges)dır. Yaygın Kalıplar: - -| Aralık | Anlam | -| ---------------------------------- | --------------------------------------------------------- | -| `>=2.3.0` | 2.3.0'dan itibaren herhangi bir sunucu | -| `>=2.3.0 \<3.0.0` | 2.3.0 veya sonrası, ancak bir sonraki ana sürümün altında | -| `^2.3.0` | `>=2.3.0 \<3.0.0` ile aynı | - -**Dağıtım ve yükleme sırasında ne olur:** - -* `engines.twenty` ayarlanmışsa ve hedef sunucunun sürümü aralığı karşılamıyorsa, dağıtım (tarball yüklemesi) veya yükleme, gerekli aralığı ve gerçek sunucu sürümünü belirten bir mesajla birlikte `SERVER_VERSION_INCOMPATIBLE` hatasıyla reddedilir. -* `engines.twenty` **ayarlı değilse**, uygulama herhangi bir sunucu sürümünde kabul edilir (mevcut uygulamalarla geriye dönük uyumludur). -* Sunucuda `APP_VERSION` yapılandırılmamışsa, denetim atlanır. - - -Nihai denetim sunucudadır — hem tarball yüklemesinde hem de çalışma alanı (workspace) kurulumunda `engines.twenty`'yi doğrular. Bir tarball'ı bant dışı dağıtırsanız veya marketplace'ten kurarsanız, sunucu yine de uyumluluğu zorunlu kılar. - - -## Otomatik CI/CD (hazır şablonlu iş akışları) - -`create-twenty-app` ile oluşturulan uygulamalar, kutudan çıktığı gibi `.github/workflows/` altında iki GitHub Actions iş akışıyla gelir. Depoyu GitHub’a iter itmez çalışmaya hazırdır — CI için ek bir kurulum gerekmez ve CD yalnızca tek bir gizli anahtar gerektirir. - -### CI — `ci.yml` - -Entegrasyon testlerini `main` dalına yapılan her itmede ve her çekme isteğinde otomatik olarak çalıştırır. - -**Ne yapar:** - -1. Uygulamanızın kaynak kodunu alır. -2. `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` bileşik eylemini kullanarak yalıtılmış bir Twenty test örneği başlatır (CI'daki `yarn twenty server start --test` eşdeğeri). -3. Corepack’i etkinleştirir, `.nvmrc` dosyanızdan Node.js'i kurar ve bağımlılıkları `yarn install --immutable` ile yükler. -4. Oluşturulan örnekten `TWENTY_API_URL` ve `TWENTY_API_KEY` değerlerini aktararak `yarn test`i çalıştırır; böylece testleriniz gerçek bir sunucuyla haberleşebilir. - -**Yapılandırma seçenekleri:** - -* `TWENTY_VERSION` (ortam, varsayılanı `latest`) — CI’da kullanılan Twenty sunucu sürümünü `ci.yml` içinde bunu düzenleyerek sabitleyin. -* Eşzamanlılık `github.ref` bazında gruplanır ve yeni itmelerde devam eden çalışmaları iptal eder. - -Gizli anahtar gerekmez — test örneği geçicidir ve yalnızca iş süresi boyunca çalışır. - -### CD — `cd.yml` - -`main` dalına yapılan her itmede uygulamanızı yapılandırılmış bir Twenty sunucusuna dağıtır ve isteğe bağlı olarak `deploy` etiketi uygulandığında bir çekme isteğinden de dağıtım yapar. - -**Ne yapar:** - -1. Etiketli PR'ler için PR'in head commit'ini ya da itilen commit'i alır. -2. `twentyhq/twenty/.github/actions/deploy-twenty-app@main` çalıştırır — `yarn twenty deploy` komutunun CI eşdeğeridir. -3. `twentyhq/twenty/.github/actions/install-twenty-app@main` eylemini çalıştırır; böylece yeni dağıtılan sürüm hedef çalışma alanına kurulur. - -**Gerekli yapılandırma:** - -| Ayar | Koşul | Amaç | -| ----------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | -| `TWENTY_DEPLOY_URL` | `cd.yml` içinde `env` (varsayılan: `http://localhost:3000`) | Dağıtımın yapılacağı Twenty sunucusu. İlk kullanımdan önce bunu gerçek sunucu URL'nizle değiştirin. | -| `TWENTY_DEPLOY_API_KEY` | GitHub deposu **Settings → Secrets and variables → Actions** | Hedef sunucuda dağıtım iznine sahip API anahtarı. | - - -Varsayılan `TWENTY_DEPLOY_URL` olan `http://localhost:3000` bir yer tutucudur — GitHub barındırmalı bir çalıştırıcıdan hiçbir yere erişemez. CD'yi etkinleştirmeden önce bunu sunucunuzun genel URL'siyle güncelleyin (veya ağ erişimi olan öz barındırılan bir çalıştırıcı kullanın). - - -**Bir PR'den bir önizleme dağıtımını tetikleme:** - -Bir çekme isteğine `deploy` etiketini ekleyin. `cd.yml` içindeki `if:` koruması, ilgili PR için işi PR'in head commit'ini kullanarak çalıştırır; böylece birleştirmeden önce hedef sunucuda değişikliği doğrulayabilirsiniz. - -### Yeniden kullanılabilir eylemleri sabitleme - -Her iki iş akışı da `@main` üzerindeki yeniden kullanılabilir eylemlere başvurur; bu nedenle `twentyhq/twenty` deposundaki eylem güncellemeleri otomatik olarak alınır. Deterministik derlemeler istiyorsanız, her `uses:` satırında `@main` ifadesini bir commit SHA'sı veya sürüm etiketiyle değiştirin. - -## npm’ye yayımlama - -npm’ye yayımlamak, uygulamanızın Twenty pazaryerinde keşfedilebilir olmasını sağlar. Herhangi bir Twenty çalışma alanı, pazaryeri uygulamalarına doğrudan arayüzden göz atabilir, yükleyebilir ve güncelleyebilir. - -### Gereksinimler - -* Bir [npm](https://www.npmjs.com) hesabı -* `package.json` içindeki `keywords` dizinizdeki `twenty-app` anahtar sözcüğü (elle ekleyin — varsayılan olarak `create-twenty-app` şablonunda yer almaz) - -```json filename="package.json" -{ - "name": "twenty-app-postcard-sender", - "version": "1.0.0", - "keywords": ["twenty-app"] -} -``` - -### Pazaryeri meta verileri - -`defineApplication()` yapılandırması, uygulamanızın pazar yerinde nasıl görüneceğini kontrol eden isteğe bağlı alanları destekler. `public/` klasöründeki görsellere başvurmak için `logoUrl` ve `screenshots` kullanın: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -Pazar yeri alanlarının tam listesi için Uygulama Oluşturma sayfasındaki [defineApplication akordeonu](/l/tr/developers/extend/apps/building#defineentity-functions)'na bakın (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, vb.). - -#### Önerilen ekran görüntüsü boyutları - -Marketplace, `screenshots`'ı sabit `8:5` oranlı bir kapsayıcıda görüntüler (örneğin, `1600×1000 px`). - - -Herhangi bir en-boy oranındaki ekran görüntüleri eksiksiz görüntülenir ve asla kırpılmaz, ancak `8:5`'ten belirgin ölçüde daha uzun veya daha dar olanlarda yanlarda boş bantlar görünür. - - -### Yayımla - -```bash filename="Terminal" -yarn twenty publish -``` - -Belirli bir dist-tag altında yayımlamak için (ör. `beta` veya `next`): - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### Pazar yerinde keşif nasıl çalışır - -Twenty sunucusu pazar yeri kataloğunu npm kayıt defterinden **her saat** eşitler. - -Beklemek yerine eşitlemeyi hemen tetikleyebilirsiniz: - -```bash filename="Terminal" -yarn twenty server catalog-sync -# To target a specific remote: -# yarn twenty server catalog-sync --remote production -``` - -Pazar yerinde gösterilen meta veriler, `defineApplication()` yapılandırmanızdan gelir — `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` ve `termsUrl` gibi alanlar. - - -Uygulamanız `defineApplication()` içinde bir `aboutDescription` tanımlamıyorsa, pazaryeri, hakkında sayfasının içeriği olarak paketinizin npm'deki `README.md` dosyasını otomatik olarak kullanır. Bu, hem npm hem de Twenty pazaryeri için tek bir README dosyası kullanabileceğiniz anlamına gelir. Pazaryerinde farklı bir açıklama istiyorsanız, `aboutDescription` değerini açıkça ayarlayın. - - -### CI üzerinden yayımlama - -Her sürümde otomatik olarak yayımlamak için bu GitHub Actions iş akışını kullanın ([OIDC](https://docs.npmjs.com/trusted-publishers) kullanır): - -```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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -Diğer CI sistemleri (GitLab CI, CircleCI, vb.) için de aynı üç komut geçerlidir: `yarn install`, `yarn twenty build` ve ardından `.twenty/output` dizininden `npm publish`. - - -**npm provenance** isteğe bağlıdır ancak önerilir. `--provenance` ile yayımlamak, npm listenize bir güven rozeti ekler ve kullanıcıların paketin herkese açık bir CI ardışık düzenindeki belirli bir commit’ten oluşturulduğunu doğrulamasını sağlar. Kurulum talimatları için [npm provenance belgelerine](https://docs.npmjs.com/generating-provenance-statements) bakın. - - -## Uygulamaları yükleme - -Bir uygulama yayımlandığında (npm) veya dağıtıldığında (tarball), çalışma alanları onu kullanıcı arayüzü (UI) üzerinden yükleyebilir. - -Twenty içinde **Ayarlar > Uygulamalar** sayfasına gidin; burada hem pazar yerindeki hem de tarball ile dağıtılmış uygulamalar görüntülenip yüklenebilir. - -{/* TODO: add screenshot of the UI when the app is registered */} - -Uygulamaları komut satırından da yükleyebilirsiniz: - -```bash filename="Terminal" -yarn twenty install -``` - - -Sunucu, kurulum sırasında semver sürümlemesini zorunlu kılar ve dağıtımdaki kuralları yansıtır: - -* Çalışma alanınızda zaten yüklü olanla aynı sürümün kurulumu, `APP_ALREADY_INSTALLED` hatasıyla reddedilir. -* Halihazırda yüklü olandan daha düşük bir sürümü kurmak, `CANNOT_DOWNGRADE_APPLICATION` hatasıyla reddedilir. - -Daha yeni bir sürümü kurmak için önce onu dağıtın veya yayımlayın, ardından `yarn twenty install` komutunu yeniden çalıştırın. - diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index a4141ce8d2..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Beceriler ve Ajanlar -description: Uygulamanız için yapay zekâ yetenekleri ve ajanları tanımlayın. -icon: robot ---- - - - Beceriler ve ajanlar şu anda alfa aşamasında. Özellik işlevsel ancak hâlâ gelişmekte. - - -Uygulamalar, çalışma alanı içinde yer alan yapay zekâ yeteneklerini — yeniden kullanılabilir yetenek yönergeleri ve özel sistem istemlerine sahip ajanları — tanımlayabilir. - - - - -Yetenekler, yapay zekâ ajanlarının çalışma alanınızda kullanabileceği yeniden kullanılabilir yönergeleri ve kabiliyetleri tanımlar. Yerleşik doğrulamayla yetenekleri tanımlamak için `defineSkill()` kullanın: - -```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`, -}); -``` - -Önemli noktalar: -* `name`, yetenek için benzersiz bir tanımlayıcı dizedir (kebab-case önerilir). -* `label`, UI'de gösterilen, insan tarafından okunabilir addır. -* `content`, yetenek yönergelerini içerir — bu, yapay zekâ ajanının kullandığı metindir. -* `icon` (isteğe bağlı), UI'de gösterilen simgeyi ayarlar. -* `description` (isteğe bağlı), yeteneğin amacı hakkında ek bağlam sağlar. - - - - -Ajanlar, çalışma alanınız içinde bulunan yapay zekâ asistanlarıdır. Özel bir sistem istemiyle ajanlar oluşturmak için `defineAgent()` kullanın: - -```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.', -}); -``` - -Önemli noktalar: -* `name`, ajan için benzersiz bir tanımlayıcı dizedir (kebab-case önerilir). -* `label`, UI'de gösterilen görünen addır. -* `prompt`, ajanın davranışını tanımlayan sistem istemidir. -* `description` (isteğe bağlı), ajanın ne yaptığı hakkında bağlam sağlar. -* `icon` (isteğe bağlı), UI'de gösterilen simgeyi ayarlar. -* `modelId` (isteğe bağlı), ajanın kullandığı varsayılan yapay zekâ modelini geçersiz kılar. - - - diff --git a/packages/twenty-docs/l/tr/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/tr/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 6527770a97..0000000000 --- a/packages/twenty-docs/l/tr/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Twenty Uygulamaları -description: Twenty özelleştirmelerini kod olarak oluşturun ve yönetin. ---- - - -Uygulamalar şu anda alfa aşamasında. Özellik işlevsel ancak hâlâ gelişmekte. - - -## Uygulamalar Nedir? - -Uygulamalar, Twenty'yi özel nesneler, alanlar, mantık işlevleri, ön yüz bileşenleri, yapay zekâ yetenekleri ve daha fazlasıyla genişletmenizi sağlar — tümü kod olarak yönetilir. Her şeyi UI üzerinden yapılandırmak yerine, veri modelinizi ve mantığınızı TypeScript'te tanımlar ve bunu bir veya daha fazla çalışma alanına dağıtırsınız. - -**Oluşturabilecekleriniz:** - -* **Özel nesneler ve alanlar** — veri modelinizi yeni varlıklarla genişletin veya Şirket ya da Kişi gibi mevcut nesnelere alanlar ekleyin -* **Mantık işlevleri** — veritabanı olayları, cron zamanlamaları veya HTTP rotaları tarafından tetiklenen sunucu tarafı işlevler -* **Ön yüz bileşenleri** — Twenty'nin kullanıcı arayüzünde (kayıt sayfaları, komut menüsü, yan paneller) görüntülenen React bileşenleri -* **Yapay zekâ yetenekleri ve ajanları** — Twenty'nin yapay zekâsını özel yeteneklerle genişletin -* **Görünümler ve gezinme** — önceden yapılandırılmış kaydedilmiş görünümler ve kenar çubuğu bağlantıları - -## Hızlı Başlangıç - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -Bu, yeni bir uygulamanın iskeletini oluşturur, isteğe bağlı olarak yerel bir Twenty sunucusunu başlatır ve dosyalarınızdaki değişiklikleri izlemeye başlar. Tam adım adım anlatım için [Başlarken](/l/tr/developers/extend/apps/getting-started) kılavuzuna bakın. - -## Ayrıntılı kılavuzlar - -| Kılavuz | Açıklama | -| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | -| [Başlarken](/l/tr/developers/extend/apps/getting-started) | Bir uygulamanın iskeletini oluşturun, yerel bir sunucu kurun, proje yapısı, CI | -| [Uygulama Oluşturma](/l/tr/developers/extend/apps/building) | Varlık tanımları (`defineObject`, `defineLogicFunction`, `defineFrontComponent` vb.), API istemcileri, npm paketleri, genel varlıklar, test | -| [Yayınlama](/l/tr/developers/extend/apps/publishing) | Bir sunucuya dağıtın, npm'e ve pazaryerine yayınlayın | - -## Temel Kavramlar - -### Varlık algılama - -SDK, TypeScript dosyalarınızı `export default define({...})` çağrılarını tarayarak varlıkları algılar. Dosya adlandırması ve klasör yapısı esnektir — algılama AST tabanlıdır, yol tabanlı değildir. - -### Kullanılabilir varlık türleri - -| Fonksiyon | Amaç | -| ---------------------------------- | ---------------------------------------------------------- | -| `defineApplication()` | Uygulama meta verileri (zorunlu, uygulama başına bir adet) | -| `defineObject()` | Alanlara sahip özel nesneler | -| `defineField()` | Mevcut nesnelerde alanlar | -| `defineLogicFunction()` | Tetikleyicilerle sunucu tarafı mantık | -| `defineFrontComponent()` | Twenty'nin kullanıcı arayüzündeki React bileşenleri | -| `defineRole()` | İzin rolleri | -| `defineView()` | Kaydedilmiş görünüm yapılandırmaları | -| `defineNavigationMenuItem()` | Kenar çubuğu gezinme bağlantıları | -| `defineSkill()` | Yapay zekâ ajanı yetenekleri | -| `defineAgent()` | İstemlerle yapay zekâ ajanları | -| `definePageLayout()` | Özel kayıt sayfası düzenleri | -| `definePreInstallLogicFunction()` | Uygulama kurulmadan önce çalışır | -| `definePostInstallLogicFunction()` | Uygulama kurulduktan sonra çalışır | - -### Geliştirme iş akışı - -1. **`yarn twenty dev`** — kaynak dosyaları izler, değişiklikte yeniden derler, sunucuyla senkronize eder, tipli API istemcileri üretir -2. **`yarn twenty build`** — dağıtılabilir bir derleme üretir -3. **`yarn twenty deploy`** — uzak bir Twenty sunucusuna dağıtır -4. **`yarn twenty add`** — etkileşimli olarak yeni bir varlık iskeleti oluşturur - -### CLI başvurusu - -```bash filename="Terminal" -yarn twenty help # Tüm komutları listele -yarn twenty server start # Yerel geliştirme sunucusunu başlat -yarn twenty remote add # Bir Twenty sunucusuna bağlan -yarn twenty exec -n fn # Bir mantık fonksiyonunu çalıştır -yarn twenty logs -n fn # Fonksiyon günlüklerini izle -``` - -Tam CLI başvuru rehberi için [Başlarken](/l/tr/developers/extend/apps/getting-started) kılavuzuna bakın. diff --git a/packages/twenty-docs/l/tr/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/tr/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 74146c4eeb..0000000000 --- a/packages/twenty-docs/l/tr/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Sürüm Ayarları -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Özellikleri - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/building.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/building.mdx deleted file mode 100644 index c6d8f9f315..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/building.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: 架构 -description: Twenty 应用如何运作——沙盒化、生命周期与构建模块。 -icon: sitemap ---- - -Twenty 应用是 TypeScript 包,可通过自定义对象、逻辑、UI 组件和 AI 能力扩展你的工作区。 它们在 Twenty 平台上运行,具备完备的沙盒与权限控制。 - -## 应用如何运作 - -应用是由 `twenty-sdk` 包中的 `defineEntity()` 函数声明的**实体**集合。 SDK 在构建时通过 AST 分析检测到这些声明,并生成一份**清单**——完整描述你的应用为工作区新增的内容。 - -``` -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 -``` - - - **文件组织由你决定。** 实体检测基于 AST——无论文件位于何处,SDK 都能找到 `export default defineEntity(...)` 的调用。 上述文件夹结构是一种约定,而非强制要求。 - - -## 实体类型 - -| 实体 | 目的 | 文档 | -| --------- | ------------------------- | -------------------------------------------------- | -| **应用程序** | 应用标识、权限、变量 | [数据模型](/l/zh/developers/extend/apps/data-model) | -| **角色** | 对象和字段的权限集 | [数据模型](/l/zh/developers/extend/apps/data-model) | -| **对象** | 带字段的自定义数据表 | [数据模型](/l/zh/developers/extend/apps/data-model) | -| **字段** | 扩展现有对象,定义关系 | [数据模型](/l/zh/developers/extend/apps/data-model) | -| **逻辑函数** | 带触发器的服务端 TypeScript | [逻辑函数](/l/zh/developers/extend/apps/logic-functions) | -| **前端组件** | 在 Twenty 页面中的沙盒化 React UI | [前端组件](/l/zh/developers/extend/apps/front-components) | -| **技能** | 可复用的 AI 代理指令 | [技能与代理](/l/zh/developers/extend/apps/skills-and-agents) | -| **代理** | 具有自定义提示词的 AI 助手 | [技能与代理](/l/zh/developers/extend/apps/skills-and-agents) | -| **视图** | 预配置的记录列表视图 | [布局](/l/zh/developers/extend/apps/layout) | -| **导航菜单项** | 自定义侧边栏条目 | [布局](/l/zh/developers/extend/apps/layout) | -| **页面布局** | 自定义记录页面的选项卡和小部件 | [布局](/l/zh/developers/extend/apps/layout) | - -## 沙盒化 - -* **逻辑函数** 在服务器上的独立 Node.js 进程中运行。 它们只能通过类型化的 API 客户端访问数据,且范围受应用角色权限限制。 -* **前端组件** 在使用 Remote DOM 的 Web Worker 中运行——与主页面沙盒隔离,但渲染原生 DOM 元素(非 iframe)。 它们通过消息传递的宿主 API 与 Twenty 通信。 -* **权限** 在 API 层面强制执行。 运行时令牌(`TWENTY_APP_ACCESS_TOKEN`)源自 `defineApplication()` 中定义的角色。 - -## 应用生命周期 - -``` -┌─────────────────────────────────────────────────────────┐ -│ Development │ -│ npx create-twenty-app → yarn twenty dev (live sync) │ -├─────────────────────────────────────────────────────────┤ -│ Build & Deploy │ -│ yarn twenty build → yarn twenty deploy │ -├─────────────────────────────────────────────────────────┤ -│ 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 build`** — 编译 TypeScript,使用 esbuild 打包逻辑函数和前端组件,并生成清单。 -* **预/后安装钩子** — 在安装过程中运行的可选逻辑函数。 详情请参见[逻辑函数](/l/zh/developers/extend/apps/logic-functions)。 - -## 后续步骤 - - - - 定义对象、字段、角色和关系。 - - - 具有 HTTP、cron 和事件触发器的服务端函数。 - - - Twenty 的 UI 中的沙盒化 React 组件。 - - - 视图、导航菜单项和记录页面布局。 - - - 具有自定义提示词的 AI 技能与代理。 - - - CLI 命令、测试、资源、远程和 CI。 - - - 部署到服务器或发布到应用市场。 - - diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/cli-and-testing.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/cli-and-testing.mdx deleted file mode 100644 index 45218b20b9..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/cli-and-testing.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: CLI 与测试 -description: CLI 命令、测试设置、公共资源、npm 包、远程以及 CI 配置。 -icon: terminal ---- - -## 公共资源(`public/` 文件夹) - -应用根目录中的 `public/` 文件夹包含静态文件——图像、图标、字体,或应用在运行时所需的任何其他资源。 这些文件会在构建时自动包含、在开发模式下同步,并上传到服务器。 - -放置在 `public/` 中的文件: - -* **公开可访问**——同步到服务器后,资源将通过公共 URL 提供服务。 访问它们无需身份验证。 -* **在前端组件中可用**——使用资源 URL 在 React 组件中显示图像、图标或任何媒体。 -* **在逻辑函数中可用**——在电子邮件、API 响应或任何服务端逻辑中引用资源 URL。 -* **用于市场元数据**——`defineApplication()` 中的 `logoUrl` 和 `screenshots` 字段引用此文件夹中的文件(例如,`public/logo.png`)。 应用发布后,这些内容会显示在市场中。 -* **在开发模式下自动同步**——当在 `public/` 中添加、更新或删除文件时,会自动同步到服务器。 无需重启。 -* **包含在构建中**——`yarn twenty build` 会将所有公共资源打包到分发产物中。 - -### 使用 `getPublicAssetUrl` 访问公共资源 - -使用来自 `twenty-sdk` 的 `getPublicAssetUrl` 辅助函数获取 `public/` 目录中文件的完整 URL。 它可在 **逻辑函数** 和 **前端组件** 中使用。 - -**在逻辑函数中:** - -```ts src/logic-functions/send-invoice.ts -import { defineLogicFunction, getPublicAssetUrl } from 'twenty-sdk/define'; - -const handler = async (): Promise => { - 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, getPublicAssetUrl } from 'twenty-sdk/define'; - -export default defineFrontComponent(() => { - const logoUrl = getPublicAssetUrl('logo.png'); - - return App logo; -}); -``` - -`path` 参数是相对于应用的 `public/` 文件夹的。 `getPublicAssetUrl('logo.png')` 和 `getPublicAssetUrl('public/logo.png')` 均解析为相同的 URL——如果存在,`public/` 前缀会被自动移除。 - -## 使用 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 => { - 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

Today is {format(new Date(), 'MMMM do, yyyy')}

; -}; - -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` 作为预置模块提供 — 这些模块不会被打包,而是在运行时由服务器解析。 - -## 测试你的应用 - -该 SDK 提供可编程的 API,使你可以在测试代码中构建、部署、安装和卸载你的应用。 结合 [Vitest](https://vitest.dev/) 和类型化 API 客户端,你可以编写集成测试,在真实的 Twenty 服务器上验证你的应用端到端运行是否正常。 - -### 设置 - -脚手架生成的应用已包含 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` | 构建应用,并可选地打包为 tar 包 | -| `appDeploy` | 将 tar 包上传到服务器 | -| `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 typecheck -``` - -这会运行 `tsc --noEmit` 并报告所有类型错误。 - -## CLI 参考 - -除了 `dev`、`build`、`add` 和 `typecheck` 外,CLI 还提供了用于执行函数、查看日志和管理应用安装的命令。 - -### 执行函数(`yarn twenty exec`) - -手动运行逻辑函数,而无需通过 HTTP、定时任务或数据库事件来触发: - -```bash filename="Terminal" -# Execute by function name -yarn twenty exec -n create-new-post-card - -# Execute by universalIdentifier -yarn twenty exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf - -# Pass a JSON payload -yarn twenty exec -n create-new-post-card -p '{"name": "Hello"}' - -# Execute the post-install function -yarn twenty exec --postInstall -``` - -### 查看函数日志(`yarn twenty logs`) - -实时流式查看你的应用逻辑函数的执行日志: - -```bash filename="Terminal" -# Stream all function logs -yarn twenty logs - -# Filter by function name -yarn twenty logs -n create-new-post-card - -# Filter by universalIdentifier -yarn twenty logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - - -这与 `yarn twenty server logs` 不同,后者显示的是 Docker 容器日志。 `yarn twenty logs` 会显示来自 Twenty 服务器的应用函数执行日志。 - - -### 卸载应用(`yarn twenty uninstall`) - -将你的应用从活动工作区中移除: - -```bash filename="Terminal" -yarn twenty uninstall - -# Skip the confirmation prompt -yarn twenty uninstall --yes -``` - -## 管理远程 - -“远程”是指你的应用连接到的 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 --api-url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote - -# List all configured remotes -yarn twenty remote list - -# Switch the active remote -yarn twenty remote switch -``` - -你的凭据存储在 `~/.twenty/config.json` 中。 - -## 使用 GitHub Actions 进行 CI - -脚手架工具会在 `.github/workflows/ci.yml` 生成一个开箱即用的 GitHub Actions 工作流。 它会在每次向 `main` 推送以及拉取请求上自动运行你的集成测试。 - -工作流: - -1. 检出你的代码 -2. 使用 `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` 动作启动一个临时的 Twenty 服务器 -3. 使用 `yarn install --immutable` 安装依赖 -4. 运行 `yarn test`,并从该动作的输出中注入 `TWENTY_API_URL` 和 `TWENTY_API_KEY` - -```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 会自动提供 `GITHUB_TOKEN` 机密。 - -若要固定为特定的 Twenty 版本而不是 `latest`,请在工作流顶部修改 `TWENTY_VERSION` 环境变量。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/connections.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/connections.mdx deleted file mode 100644 index 114d1627b9..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/connections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: 连接 -description: Let your app act on a user's behalf in third-party services via OAuth. -icon: plug ---- - -连接是用户为外部服务(Linear、GitHub、Slack 等)持有的凭据。 你的应用声明**如何**获取这些凭据——即**连接提供程序**——并在运行时使用它们向第三方 API 发起认证调用。 - -目前仅支持 OAuth 2.0。 将来的凭据类型(个人访问令牌、API 密钥、基本身份验证)将接入相同的接口——已经使用 `defineConnectionProvider({ type: 'oauth', ... })` 的应用将无需迁移。 - - - - - -连接提供程序描述了你的应用所需的 OAuth 握手流程。 用户在你的应用设置中点击"添加连接",完成提供方的授权同意页面后,会在其工作区中创建一条 `ConnectedAccount` 行。 - -一个可用的配置需要**两个文件**——连接提供程序,以及在 `defineApplication` 上与之匹配、用于保存 OAuth 客户端凭据的 `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.', - defaultRoleUniversalIdentifier: '...', - // 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, - }, - }, -}); -``` - -关键点: - -* `name` 是在 `listConnections({ providerName })` 中使用的唯一标识符字符串(短横线命名(kebab-case),必须匹配 `^[a-z][a-z0-9-]*$`)。 -* `displayName` 会显示在每个应用的设置选项卡以及 AI 工具列表中。 -* `clientIdVariable` / `clientSecretVariable` 是**名称**,而不是值——它们必须与 `defineApplication.serverVariables` 中声明的键匹配。 实际的 `client_id` 和 `client_secret` 由服务器管理员通过应用注册 UI 输入,绝不会提交到你的仓库。 -* 请使用 `serverVariables`(而非 `applicationVariables`)——OAuth 凭据是服务器范围的,并且每个 Twenty 服务器只配置一个 OAuth 应用。 -* 在两个 `serverVariables` 都填写之前,每个应用的设置选项卡会显示"需要服务器管理员"的提示,并且"添加连接"按钮将被禁用。 -* `type: 'oauth'` 是目前唯一受支持的取值。 该判别器具备前向兼容性:未来的类型(`'pat'`、`'api-key'` 等) 将会与 `oauth` 并列新增子配置块。 - -你的提供方需要加入白名单的 OAuth 回调 URL 为: - -``` -https:///apps/oauth/callback -``` - - - - - -在逻辑函数处理器内,`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)` 以重新获取单个连接 | -| `可见性` | `'user'`(仅对单个工作区成员私有)或 `'workspace'`(与所有成员共享) | -| `范围` | 上游提供方授予的 OAuth 权限(不同于 `visibility`——两者不相关) | -| `userWorkspaceId` | 所有者的 userWorkspace id——在 HTTP 路由触发器中用于选择"请求用户的连接"很有用 | -| `accessToken` | 最新的 OAuth 访问令牌(若已过期会自动刷新) | -| `name` / `handle` | 连接的显示名称(在 OAuth 回调时自动生成,用户可重命名) | -| `authFailedAt` | 当最近一次刷新失败时会设置;用户必须重新连接 | - -关键点: - -* 传入 `{ providerName }` 以按提供方筛选;省略它则可获取此应用在所有提供方上的全部连接。 -* 服务器会在返回前透明地刷新访问令牌。 你的处理器始终会拿到可用的令牌(或已设置 `authFailedAt`)。 -* `getConnection(id)` 是获取单行记录的对应方法。 - - - - - -当用户点击"添加连接"时,系统会提示其选择可见性: - -* **仅限我**——该凭据仅对连接的用户私有。 代表其调用的任何逻辑函数(带有 `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'); -``` - -每个(用户、提供方)允许有多个连接,因此同一用户可以同时拥有"个人 Linear"和"工作 Linear"。 - - - - - -对于每个连接提供方,服务器管理员需要先在第三方注册一个 OAuth 应用。 - -1. 前往提供方的开发者设置(例如 https://linear.app/settings/api/applications/new)。 -2. 将**Redirect URI** 设置为 `\/apps/oauth/callback`。 -3. 复制生成的**Client ID**和**Client Secret**。 -4. 以服务器管理员身份在 Twenty 中打开已安装的应用 → 在相应的 `serverVariables` 上设置这些值。 -5. 之后,工作区成员可以在每个应用的**连接**部分添加连接。 - - - - diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/data-model.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/data-model.mdx deleted file mode 100644 index 73462fa16d..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/data-model.mdx +++ /dev/null @@ -1,493 +0,0 @@ ---- -title: 数据模型 -description: 使用 Twenty SDK 定义对象、字段、角色和应用程序元数据。 -icon: database ---- - -`twenty-sdk` 包提供 `defineEntity` 函数,用于声明应用的数据模型。 你必须使用 `export default defineEntity({...})`,这样 SDK 才能检测到你的实体。 这些函数会在构建时校验你的配置,并提供 IDE 自动补全和类型安全。 - - - **文件组织由你决定。** - 实体检测基于 AST——无论文件位于何处,SDK 都能找到 `export default defineEntity(...)` 的调用。 按类型对文件分组(例如 `logic-functions/`、`roles/`)只是代码组织的一种约定,并非必需。 - - - - - -角色封装了对你的工作空间对象与操作的权限。 - -```ts restricted-company-role.ts -import { - defineRole, - PermissionFlag, - STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, -} 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, - }, - ], - permissionFlags: [PermissionFlag.APPLICATIONS], -}); -``` - - - - -每个应用必须且只能有一个 `defineApplication` 调用,用于描述: - -* **应用的身份**:标识符、显示名称和描述。 -* **权限**:其函数和前端组件所使用的角色。 -* **(可选)变量**:以环境变量形式提供给函数的键值对。 -* **(可选)安装前/安装后函数**:在安装之前或之后运行的逻辑函数。 - -```ts src/application-config.ts -import { defineApplication } from 'twenty-sdk/define'; -import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role'; - -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, - }, - }, - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, -}); -``` - -备注: -* `universalIdentifier` 字段是你拥有的确定性 ID。 只需生成一次,并在多次同步过程中保持稳定不变。 -* `applicationVariables` 会变成你的函数和前端组件可用的环境变量(例如,`DEFAULT_RECIPIENT_NAME` 可作为 `process.env.DEFAULT_RECIPIENT_NAME` 使用)。 -* `defaultRoleUniversalIdentifier` 必须引用使用 `defineRole()` 定义的角色(见上文)。 -* 在构建清单时会自动检测安装前/安装后函数——无需在 `defineApplication()` 中引用它们。 - -#### 应用市场元数据 - -如果你计划[发布你的应用](/l/zh/developers/extend/apps/publishing),这些可选字段将控制你的应用在应用市场中的展示: - -| 字段 | 描述 | -| ------------------ | -------------------------------------------------------------- | -| `作者` | 作者或公司名称 | -| `类别` | 用于应用市场筛选的应用类别 | -| `logoUrl` | 应用徽标的路径(例如 `public/logo.png`) | -| `screenshots` | 截图路径数组(例如 `public/screenshot-1.png`) | -| `aboutDescription` | 用于“关于”选项卡的更长的 Markdown 描述。 如果省略,市场将使用该软件包在 npm 上的 `README.md`。 | -| `websiteUrl` | 你的网站链接 | -| `termsUrl` | 服务条款链接 | -| `emailSupport` | 支持电子邮件地址 | -| `issueReportUrl` | 问题跟踪器链接 | - -#### 角色和权限 - -`application-config.ts` 中的 `defaultRoleUniversalIdentifier` 字段指定你的应用的逻辑函数和前端组件所使用的默认角色。 详见上文的 `defineRole`。 - -* 作为 `TWENTY_APP_ACCESS_TOKEN` 注入的运行时令牌来源于该角色。 -* 类型化客户端将受限于该角色授予的权限。 -* 遵循最小权限原则:创建一个仅包含你的函数所需权限的专用角色。 - -##### 默认函数角色 - -当你使用脚手架创建新应用时,CLI 会创建一个默认角色文件: - -```ts src/roles/default-role.ts -import { defineRole, PermissionFlag } from 'twenty-sdk/define'; - -export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = - 'b648f87b-1d26-4961-b974-0908fd991061'; - -export default defineRole({ - 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: [], - permissionFlags: [], -}); -``` - -该角色的 `universalIdentifier` 会在 `application-config.ts` 中被引用为 `defaultRoleUniversalIdentifier`: - -* **\*.role.ts** 定义该角色可以执行的操作。 -* **application-config.ts** 指向该角色,使你的函数继承其权限。 - -备注: -* 从脚手架生成的角色开始,然后按照最小权限原则逐步收紧权限。 -* 将 `objectPermissions` 和 `fieldPermissions` 替换为你的函数所需的对象/字段。 -* `permissionFlags` 控制对平台级能力的访问。 尽量保持最小化。 -* 查看一个可运行示例:[`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts)。 - - - - -自定义对象同时描述工作空间中记录的架构与行为。 使用 `defineObject()` 以内置校验定义对象: - -```ts postCard.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, - }, - ], -}); -``` - -关键点: - -* 使用 `defineObject()` 以获得内置校验和更好的 IDE 支持。 -* `universalIdentifier` 必须在各次部署间保持唯一且稳定。 -* 每个字段都需要 `name`、`type`、`label` 以及其自身稳定的 `universalIdentifier`。 -* `fields` 数组是可选的——你可以定义没有自定义字段的对象。 -* 你可以使用 `yarn twenty add` 脚手架创建新对象,它会引导你完成命名、字段和关系。 - - -**基础字段会自动创建。** 当你定义自定义对象时,Twenty 会自动添加标准字段 -例如 `id`、`name`、`createdAt`、`updatedAt`、`createdBy`、`updatedBy` 和 `deletedAt`。 -你无需在 `fields` 数组中定义这些字段——只需添加你的自定义字段。 -你可以通过在你的 `fields` 数组中定义一个同名字段来覆盖默认字段, -但不建议这样做。 - - - - - -使用 `defineField()` 向你不拥有的对象添加字段——例如标准的 Twenty 对象(Person、Company 等)。 或来自其他应用的对象。 与在 `defineObject()` 中的内联字段不同,独立字段需要一个 `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-sdk` 导出的 `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`。 -* 在 `defineObject()` 中以内联方式定义字段时,你不需要 `objectUniversalIdentifier`——它会从父对象继承。 -* `defineField()` 是为非通过 `defineObject()` 创建的对象添加字段的唯一方式。 - - - - -关系用于将对象彼此连接。 在 Twenty 中,关系始终是双向的——你需要定义两侧,每一侧都引用另一侧。 - -关系有两种类型: - -| 关系类型 | 描述 | 是否有外键? | -| ------------- | ------------------- | ------------------- | -| `MANY_TO_ONE` | 该对象的多条记录指向目标对象的一条记录 | 是(`joinColumnName`) | -| `ONE_TO_MANY` | 该对象的一条记录拥有目标对象的多条记录 | 否(反向侧) | - -#### 关系如何工作 - -每个关系都需要两个相互引用的字段: - -1. **MANY_TO_ONE** 侧——位于持有外键的对象上 -2. **ONE_TO_MANY** 侧——位于拥有集合的对象上 - -两个字段都使用 `FieldType.RELATION`,并通过 `relationTargetFieldMetadataUniversalIdentifier` 相互交叉引用。 - -#### 示例:Post Card 拥有多个收件人 - -假设一个 `PostCard` 可以发送到多个 `PostCardRecipient` 记录。 每个收件人只隶属于一张 Post Card。 - -**步骤 1:在 PostCard 上定义 ONE_TO_MANY 侧**(“一”侧): - -```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', - }, -}); -``` - - -\*\*循环导入:\*\*两个关系字段相互引用彼此的 `universalIdentifier`。 为避免循环导入问题,请在各自文件中将字段 ID 作为具名常量导出,并在另一个文件中导入它们。 构建系统会在编译时解析这些引用。 - - -#### 与标准对象建立关系 - -要与内置的 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', - }, -}); -``` - -#### 关系字段属性 - -| 属性 | 必填 | 描述 | -| ------------------------------------------------- | ---------------- | -------------------------------------------------------------- | -| `类型` | 是 | 必须为 `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 中内联关系字段 - -你也可以直接在 `defineObject()` 内定义关系字段。 在这种情况下,省略 `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 - ], -}); -``` - - - -## 使用 `yarn twenty add` 脚手架生成实体 - -无需手动创建实体文件,你可以使用交互式脚手架: - -```bash filename="Terminal" -yarn twenty add -``` - -它会提示你选择实体类型,并引导你完成必填字段。 它会生成一个可直接使用的文件,包含稳定的 `universalIdentifier` 以及正确的 `defineEntity()` 调用。 - -你也可以直接传入实体类型以跳过第一个提示: - -```bash filename="Terminal" -yarn twenty add object -yarn twenty add logicFunction -yarn twenty add frontComponent -``` - -### 可用的实体类型 - -| 实体类型 | 命令 | 生成的文件 | -| ----- | ------------------------------------ | ------------------------------------------------------- | -| 对象 | `yarn twenty add object` | `src/objects/\.ts` | -| 字段 | `yarn twenty add field` | `src/fields/\.ts` | -| 逻辑函数 | `yarn twenty add logicFunction` | `src/logic-functions/\.ts` | -| 前端组件 | `yarn twenty add frontComponent` | `src/front-components/\.tsx` | -| 角色 | `yarn twenty add role` | `src/roles/\.ts` | -| 技能 | `yarn twenty add skill` | `src/skills/\.ts` | -| 代理 | `yarn twenty add agent` | `src/agents/\.ts` | -| 视图 | `yarn twenty add view` | `src/views/\.ts` | -| 导航菜单项 | `yarn twenty add navigationMenuItem` | `src/navigation-menu-items/\.ts` | -| 页面布局 | `yarn twenty add pageLayout` | `src/page-layouts/\.ts` | - -### 脚手架生成的内容 - -每种实体类型都有其自己的模板。 例如,`yarn twenty add object` 会询问: - -1. **名称(单数)**——例如,`invoice` -2. **名称(复数)**——例如,`invoices` -3. **标签(单数)**——根据名称自动填充(例如,`Invoice`) -4. **标签(复数)**——自动填充(例如,`Invoices`) -5. **创建视图和导航项?**——如果你选择是,脚手架还会为新对象生成相应的视图和侧边栏链接。 - -其他实体类型的提示更简单——大多只会询问名称。 - -`field` 实体类型更为详细:它会询问字段名称、标签、类型(从所有可用字段类型列表中选择,如 `TEXT`、`NUMBER`、`SELECT`、`RELATION` 等),以及目标对象的 `universalIdentifier`。 - -### 自定义输出路径 - -使用 `--path` 标志将生成的文件放置在自定义位置: - -```bash filename="Terminal" -yarn twenty add logicFunction --path src/custom-folder -``` diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/front-components.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/front-components.mdx deleted file mode 100644 index b03034e7a4..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/front-components.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: 前端组件 -description: 构建可在 Twenty 的 UI 中渲染并具备沙盒隔离的 React 组件。 -icon: window-maximize ---- - -前端组件是直接在 Twenty 的 UI 内渲染的 React 组件。 它们在使用 Remote DOM 的**隔离 Web Worker**中运行——你的代码在沙盒中执行,但会原生渲染到页面中,而非在 iframe 里。 - -## 前端组件可用位置 - -在 Twenty 中,前端组件可在两个位置进行渲染: - -* **侧边栏** — 非无头的前端组件会在右侧侧边栏中打开。 当前端组件从命令菜单触发时,这是默认行为。 -* **小部件(仪表盘和记录页面)** — 前端组件可以作为小部件嵌入页面布局中。 在配置仪表盘或记录页面布局时,用户可以添加前端组件小部件。 - -## 基础示例 - -最快查看前端组件实际运行效果的方法,是将其注册为一个**命令菜单项**。 在单独的文件中使用 `defineCommandMenuItem`,使该组件显示为页面右上角的快速操作按钮: - -```tsx src/front-components/hello-world.tsx -import { defineFrontComponent } from 'twenty-sdk/define'; - -const HelloWorld = () => { - return ( -
-

Hello from my app!

-

This component renders inside Twenty.

-
- ); -}; - -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` 同步后(或单次运行 `yarn twenty dev --once`),快速操作会出现在页面右上角: - -
- 右上角的快速操作按钮 -
- -点击它以内联方式渲染该组件。 - -## 配置字段 - -| 字段 | 必填 | 描述 | -| --------------------- | -- | ---------------------------- | -| `universalIdentifier` | 是 | 该组件的稳定唯一 ID | -| `component` | 是 | 一个 React 组件函数 | -| `name` | 否 | 显示名称 | -| `description` | 否 | 组件的功能描述 | -| `isHeadless` | 否 | 如果组件没有可见的 UI,则设为 `true`(见下文) | - -## 在页面上放置前端组件 - -除了命令之外,您还可以在**页面布局**中将其添加为小部件,从而将前端组件直接嵌入记录页面。 详情请参见[definePageLayout](/l/zh/developers/extend/apps/skills-and-agents#definepagelayout)部分。 - -## 无头与非无头 - -前端组件有两种由 `isHeadless` 选项控制的渲染模式: - -**非无头(默认)** — 该组件会渲染可见的 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 会跳过为其渲染容器——布局中不会出现空白区域。 该组件仍可访问所有 hooks 和宿主通信 API。 - -## SDK Command 组件 - -`twenty-sdk` 包提供了四个为无头前端组件设计的 Command 辅助组件。 每个组件都会在挂载时执行一个操作,通过显示 snackbar 通知来处理错误,并在完成后自动卸载该前端组件。 - -从 `twenty-sdk/command` 导入它们: - -* **`Command`** — 通过 `execute` 属性运行异步回调。 -* **`CommandLink`** — 导航到某个应用路径。 属性:`to`、`params`、`queryParams`、`options`。 -* **`CommandModal`** — 打开一个确认模态框。 如果用户确认,则执行 `execute` 回调。 属性:`title`、`subtitle`、`execute`、`confirmButtonText`、`confirmButtonAccent`。 -* **`CommandOpenSidePanelPage`** — 打开特定的侧边栏页面。 属性:`page`、`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 ; -}; - -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 ( - - ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', - name: 'delete-draft', - description: 'Deletes a draft with confirmation', - component: DeleteDraft, - isHeadless: true, -}); -``` - -## 访问运行时上下文 - -在组件内部,使用 SDK 的 hooks 获取当前用户、记录和组件实例: - -```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 ( -
-

User: {userId}

-

Record: {recordId ?? 'No record context'}

-

Component: {componentId}

-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', - name: 'record-info', - component: RecordInfo, -}); -``` - -可用的 hooks: - -| 钩子 | 返回值 | 描述 | -| --------------------------------------------- | ----------------- | ------------------------------------- | -| `useUserId()` | `string` 或 `null` | 当前用户的 ID | -| `useSelectedRecordIds()` | `字符串[]` | 所有已选择的记录 ID(如果未选择,则为空数组) | -| `useRecordId()` | `string` 或 `null` | **已弃用。** 请改用 `useSelectedRecordIds()` | -| `useFrontComponentId()` | `string` | 此组件实例的 ID | -| `useFrontComponentExecutionContext(selector)` | 因情况而异 | 使用选择器函数访问完整的执行上下文 | - -## 宿主通信 API - -前端组件可以使用来自 `twenty-sdk` 的函数触发导航、模态框和通知: - -| 函数 | 描述 | -| ----------------------------------------------- | ------------- | -| `navigate(to, params?, queryParams?, options?)` | 在应用中导航到某个页面 | -| `openSidePanelPage(params)` | 打开侧边栏 | -| `closeSidePanel()` | 关闭侧边栏 | -| `openCommandConfirmationModal(params)` | 显示确认对话框 | -| `enqueueSnackbar(params)` | 显示一条 Toast 通知 | -| `unmountFrontComponent()` | 卸载该组件 | -| `updateProgress(progress)` | 更新进度指示器 | - -下面是一个示例,使用宿主 API 在操作完成后显示一条 snackbar 并关闭侧边栏: - -```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 ( -
-

Archive this record?

- -
- ); -}; - -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 ( -
-

Export {selectedRecordIds.length} selected record(s)?

- -
- ); -}; - -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, - }, -}); -``` - -## defineCommandMenuItem - -使用 `defineCommandMenuItem` 在命令菜单(Cmd+K)中注册一个前端组件。 如果 `isPinned` 为 `true`,它还会显示为页面右上角的快速操作按钮。 - -```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` | 否 | 用于动态控制命令是否可见的布尔表达式(见下文) | - -## 条件可用性表达式 - -通过 `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, - ), -}); -``` - -**上下文变量** — 表示页面的当前状态: - -| 变量 | 类型 | 描述 | -| ------------------------------ | --------- | --------------------------------------------- | -| `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)` | 当至少一个条目上的该属性为 truthy 时为 `true` | -| `someDefined(array, prop)` | 当至少一个条目上的该属性已定义时为 `true` | -| `someEquals(array, prop, value)` | 当至少一个条目上的该属性等于该值时为 `true` | -| `someNonEmptyString(array, prop)` | 当至少一个条目上的该属性为非空字符串时为 `true` | -| `none(array, prop)` | 当该属性在每个条目上都为 falsy 时为 `true` | -| `noneDefined(array, prop)` | 当该属性在每个条目上都为 undefined 时为 `true` | -| `noneEquals(array, prop, value)` | 当该属性在任意条目上都不等于该值时为 `true` | - -## 公共资源 - -前端组件可以使用 `getPublicAssetUrl` 访问应用的 `public/` 目录中的文件: - -```tsx -import { defineFrontComponent, getPublicAssetUrl } from 'twenty-sdk/define'; - -const Logo = () => Logo; - -export default defineFrontComponent({ - universalIdentifier: '...', - name: 'logo', - component: Logo, -}); -``` - -详情请参见[公共资源部分](/l/zh/developers/extend/apps/cli-and-testing#public-assets-public-folder)。 - -## 样式 - -前端组件支持多种样式方案。 您可以使用: - -* **内联样式** — `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** — 工具类 -* **任何 CSS-in-JS 库**(与 React 兼容) - -```tsx -import { defineFrontComponent } from 'twenty-sdk/define'; -import { Button, Tag, Status } from 'twenty-sdk/ui'; - -const StyledWidget = () => { - return ( -
-
- ); -}; - -export default defineFrontComponent({ - universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', - name: 'styled-widget', - component: StyledWidget, -}); -``` diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/getting-started.mdx deleted file mode 100644 index e0d7bd402b..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/getting-started.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -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 应用包含三个阶段。 脚手架工具将它们合并为一个理想路径的命令,但每个阶段都是独立的概念——当出现问题时,知道自己处于哪个阶段可以指明需要修复什么。 - -| 阶段 | 你要做什么 | 工具 | 结果 | -| ------------ | -------------------- | ----------------------------- | -------------------- | -| **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/` 中生成一个 TypeScript 项目,包含一个入门版的 `application-config.ts`、一个默认角色、一个 CI 工作流,以及一个集成测试。 - -**完成此阶段后:** 你的机器上已有该应用的源代码。 它还未运行——那是第 2 阶段的内容。 - ---- - -## 阶段 2 — 运行本地 Twenty 服务器 - -你的应用需要一个 Twenty 服务器来进行同步。 该服务器是一个完整的 Twenty 实例——包含 UI、GraphQL API、PostgreSQL——在本地的 Docker 中运行。 你的本地代码会将其定义上传到该服务器,从而使其显示在 UI 中。 - -脚手架工具会为你提供启动它的选项: - -> **是否要设置本地 Twenty 实例?** - -* **是(推荐)** — 将拉取 `twentycrm/twenty-app-dev` Docker 镜像,并在端口 `2020` 上启动它。 请先确保 Docker 正在运行。 -* **否** — 如果你已经有一个想要连接的 Twenty 服务器,请选择此项。 你可以稍后通过 `yarn twenty remote add` 将其连接起来。 - -
- 是否启动本地实例? -
- -服务器启动后,浏览器会打开登录页面。 使用预置的演示账户: - -* **邮箱:** `tim@apple.dev` -* **密码:** `tim@apple.dev` - -
- Twenty 登录界面 -
- -在下一屏点击 **Authorize** —— 这将授予 CLI 访问你工作区的权限。 - -
- Twenty CLI 授权界面 -
- -你的终端会确认一切已就绪。 - -
- 应用脚手架创建成功 -
- -**完成此阶段后:** 你在 [http://localhost:2020](http://localhost:2020) 上拥有一个正在运行的 Twenty 服务器,且你的 CLI 已获授权可与其同步。 - - -如果未安装或未运行 Docker,脚手架工具会告诉你在所用操作系统上正确的启动命令。 Docker 启动后,你可以通过 `yarn twenty server start` 继续——无需重新生成脚手架。 - - ---- - -## 阶段 3 — 同步你的更改 - -这是你大部分时间所处的内循环。 - -```bash filename="Terminal" -cd my-twenty-app -yarn twenty dev -``` - -它会监视 `src/`,在每次更改时重新构建,并将结果同步到服务器。 编辑文件、保存,服务器会在一秒内反映出更改。 你会在终端中看到一个实时状态面板。 - -如需更详细的输出(构建日志、同步请求、错误跟踪),请添加 `--verbose`。 - -
- 开发模式终端输出 -
- -打开 [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer)。 你应当在 **你的应用** 下看到你的应用。 - -
- “你的应用”列表显示 My twenty app -
- -点击 **My twenty app** 查看其**应用注册**——一条用于描述你的应用(名称、标识符、OAuth 凭据、来源)的服务器级记录。 同一服务器上的多个工作区可以安装同一个注册项。 - -
- 应用注册详情 -
- -点击 **查看已安装的应用** 以查看工作区安装项。 **关于** 选项卡显示版本和管理选项。 - -
- 已安装的应用 -
- -**完成此阶段后:** 你拥有一个实时的开发循环。 编辑 `src/` 中的任意文件,更改会显示在 UI 中。 - -### 用于 CI 和脚本的一次性同步 - -传入 `--once` 以执行一次构建与同步后退出——相同的流水线,无文件监视器: - -```bash filename="Terminal" -yarn twenty dev --once -``` - -| 命令 | 行为 | 适用场景 | -| ------------------------ | -------------------------------- | ------------------------------ | -| `yarn twenty dev` | 监视并在每次更改时重新同步。 持续运行,直到你将其停止。 | 交互式本地开发。 | -| `yarn twenty dev --once` | 单次构建与同步,成功时以 `0` 退出,失败时以 `1` 退出。 | CI、pre-commit 钩子、AI 代理、脚本化工作流。 | - -两种模式都需要处于开发模式的服务器和已认证的远程。 - - -开发模式仅适用于以开发模式运行的 Twenty 实例(`NODE_ENV=development`)。 生产实例会拒绝开发同步请求——请使用 `yarn twenty deploy` 部署到生产服务器。 参见[发布应用](/l/zh/developers/extend/apps/publishing)。 - - ---- - -## 你可以构建的内容 - -应用由**实体**组成——每个实体定义为一个包含单一 `export default` 的 TypeScript 文件: - -| 实体 | 作用 | -| ---------- | ------------------------------------------ | -| **对象与字段** | 自定义数据模型(明信片、发票等) 带有类型化字段 | -| **逻辑函数** | 由 HTTP 路由、cron 调度或数据库事件触发的服务端 TypeScript | -| **前端组件** | 在 Twenty 的 UI 内渲染的 React 组件(侧边面板、小部件、命令菜单) | -| **技能与智能体** | AI 能力——可复用的指令和自主助手 | -| **视图与导航** | 预配置的列表视图和侧边栏菜单项 | -| **页面布局** | 带有选项卡和小部件的自定义记录详情页 | - -完整参考:[构建应用](/l/zh/developers/extend/apps/building)。 - -## 项目结构 - -```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/` | 随应用一起提供的静态资源(图像、字体)。 | - -### 从示例开始 - -使用 `--example` 从一个更完整的项目开始(自定义对象、字段、逻辑函数、前端组件): - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app --example postcard -``` - -示例位于 [twenty-apps/examples](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples)。 你也可以使用 `yarn twenty add` 为现有项目生成单个实体的脚手架——参见[构建应用](/l/zh/developers/extend/apps/building#scaffolding-entities-with-yarn-twenty-add)。 - ---- - -## 管理本地服务器 - -使用 `yarn twenty server` 控制本地的 Twenty 容器: - -| 命令 | 作用 | -| -------------------------------------- | ------------------------- | -| `yarn twenty server start` | 启动服务器(按需拉取镜像) | -| `yarn twenty server start --port 3030` | 在自定义端口启动 | -| `yarn twenty server stop` | 停止服务器(保留数据) | -| `yarn twenty server status` | 显示 URL、版本和登录凭据 | -| `yarn twenty server logs` | 流式输出服务器日志 | -| `yarn twenty server reset` | 清空数据并全新开始 | -| `yarn twenty server upgrade` | 拉取最新的 `twenty-app-dev` 镜像 | -| `yarn twenty server upgrade 2.2.0` | 升级到指定版本 | - -数据在重启后会保留,存储于两个 Docker 卷中(`twenty-app-dev-data` 用于 PostgreSQL,`twenty-app-dev-storage` 用于文件)。 使用 `reset` 清空所有内容。 - -### 升级服务器镜像 - -`yarn twenty server upgrade` 将拉取最新镜像、比较摘要,并且仅在确有变更时才重新创建容器。 数据卷将被保留——只会替换容器。 如果已拉取新镜像且容器正在运行,升级会自动启动一个新容器;之后运行 `yarn twenty server start` 以等待其变为健康状态。 - -```bash filename="Terminal" -yarn twenty server upgrade # Latest -yarn twenty server upgrade 2.2.0 # Specific version -``` - -使用 `yarn twenty server status` 验证正在运行的版本(它会显示写入容器的 `APP_VERSION`)。 - -### 运行并行测试实例 - -向任意 `server` 命令传递 `--test` 以管理第二个、完全隔离的实例——这有助于在不影响主开发数据的情况下进行集成测试或试验: - -| 命令 | 作用 | -| ----------------------------------- | ------------------- | -| `yarn twenty server start --test` | 启动测试实例 (默认端口为 2021) | -| `yarn twenty server stop --test` | 停止它 | -| `yarn twenty server status --test` | 显示其状态 | -| `yarn twenty server logs --test` | 流式输出其日志 | -| `yarn twenty server reset --test` | 清空其数据 | -| `yarn twenty server upgrade --test` | 升级其镜像 | - -测试实例有其自己的容器(`twenty-app-dev-test`)、卷(`twenty-app-dev-test-data`、`twenty-app-dev-test-storage`)和配置——它可与你的主实例并行运行且不会发生冲突。 将 `--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 server start`,以及其他命令。 - - -不要全局安装 `twenty-sdk` —— 在每个项目中固定其版本,使每个应用都使用各自的版本。 - - ---- - -## 故障排除 - -* **Docker 错误** — 在运行 `yarn twenty server start` 之前,请确保 Docker Desktop(或守护进程)已在运行。 错误消息会显示适用于你的操作系统的正确启动命令。 -* **Node 版本不正确** — 需要 24+。 使用 `node -v` 检查。 -* **缺少 Yarn 4** — 运行 `corepack enable`。 -* **依赖损坏** — `rm -rf node_modules && yarn install`。 - -卡住了吗? 在 [Twenty 的 Discord](https://discord.com/channels/1130383047699738754/1130386664812982322) 上提问。 diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/layout.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/layout.mdx deleted file mode 100644 index e9768a8714..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/layout.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: 布局 -description: 定义视图、导航菜单项和页面布局,以塑造你的应用在 Twenty 中的呈现方式。 -icon: table-columns ---- - -布局实体控制你的应用如何在 Twenty 的 UI 中呈现——侧边栏中有哪些内容、应用随附哪些已保存的视图,以及记录详情页如何排布。 - -## 布局概念 - -| 概念 | 控制内容 | 实体 | -| ----------- | ----------------------------------- | -------------------------- | -| **视图** | 对象的已保存列表配置——可见字段、顺序、筛选器、分组 | `defineView` | -| **导航菜单项** | 左侧侧边栏中的一项,链接到某个视图或外部 URL | `defineNavigationMenuItem` | -| **页面布局** | 构成记录详情页的选项卡和小部件 | `definePageLayout` | -| **页面布局选项卡** | 附加到现有页面布局(标准页面布局或你自己的应用的页面布局)的独立选项卡 | `definePageLayoutTab` | - -视图、导航菜单项和页面布局通过 `universalIdentifier` 相互引用: - -* 类型为 `VIEW` 的**导航菜单项**指向一个 `defineView` 标识符,因此侧边栏链接会打开该已保存视图。 -* 类型为 `RECORD_PAGE` 的**页面布局**面向某个对象,并可在其选项卡内嵌入[前端组件](/l/zh/developers/extend/apps/front-components)作为小部件。 - - - - -视图是关于对象记录如何显示的已保存配置——包括哪些字段可见、它们的顺序,以及应用的任何筛选器或分组。 使用 `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` 指定此视图适用于哪个对象。 -* `key` 决定视图类型(例如,主列表视图使用 `ViewKey.INDEX`)。 -* `fields` 控制显示哪些列及其顺序。 每个字段引用一个 `fieldMetadataUniversalIdentifier`。 -* 你还可以定义 `filters`、`filterGroups`、`groups` 和 `fieldGroups` 以进行更高级的配置。 -* `position` 在同一对象存在多个视图时控制其排序。 - - - - -导航菜单项会在工作区侧边栏中添加自定义条目。 使用 `defineNavigationMenuItem()` 链接到视图、外部 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` 决定菜单项链接到的目标:`NavigationMenuItemType.VIEW` 表示已保存视图,`NavigationMenuItemType.LINK` 表示外部 URL。 -* 对于视图链接,设置 `viewUniversalIdentifier`。 对于外部链接,设置 `link`。 -* `position` 控制在侧边栏中的排序。 -* `icon` 和 `color`(可选)用于自定义外观。 - - - - -页面布局使你可以自定义记录详情页的外观——显示哪些选项卡、每个选项卡内有哪些小部件,以及它们如何排列。 使用 `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` 可以渲染一个前端组件、关系列表或其他内置小部件类型。 -* 选项卡上的 `position` 控制其顺序。 使用更高的值(例如 50)可将自定义选项卡放在内置选项卡之后。 - - - - -`definePageLayoutTab` 允许你的应用将单个选项卡 — 可选小部件 — 附加到一个**现有**页面布局。 最常见的用例是向 Twenty 内置的某个记录页面添加自定义选项卡(例如,分析或 AI 摘要选项卡),或向你的应用已随附的页面布局添加该选项卡。 - -目标页面布局必须是 **标准** 的 Twenty 页面布局,或由 **你自己的应用** 定义的布局;目前不支持跨应用引用由其他已安装应用拥有的页面布局。 - -```ts src/page-layouts/example-extra-tab.ts -import { - definePageLayoutTab, - PageLayoutTabLayoutMode, -} from 'twenty-sdk/define'; -import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; - -const COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = - '20202020-ab01-4001-8001-c0aba11c0100'; - -export default definePageLayoutTab({ - universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', - pageLayoutUniversalIdentifier: - COMPANY_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, - 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` 在使用 `definePageLayoutTab` 时是**必需**的,并且必须指向在安装时已存在的页面布局(标准布局或你的应用的布局)。 当父页面布局缺失时,安装会失败,并给出清晰的验证错误。 -* `widgets` 仅作用于此选项卡 — 它们引用前端组件、视图等,其方式与在 `definePageLayout` 中内联定义的小部件完全相同。 -* `position` 控制目标布局中相对于现有选项卡的排序。 选择一个取值,使你的选项卡相对于内置选项卡位于你想要的位置。 -* 当你只想向现有布局进行**添加**时,请使用此功能,而不是 `definePageLayout`。 当你拥有整个布局时,请使用 `definePageLayout`(通常是你在应用中提供的对象的 `RECORD_PAGE`,或 `STANDALONE_PAGE`)。 - - - diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/logic-functions.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/logic-functions.mdx deleted file mode 100644 index 81e5c34cdf..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/logic-functions.mdx +++ /dev/null @@ -1,566 +0,0 @@ ---- -title: 逻辑函数 -description: 定义具有 HTTP、cron 和数据库事件触发器的服务端 TypeScript 函数。 -icon: bolt ---- - -逻辑函数是在 Twenty 平台上运行的服务端 TypeScript 函数。 它们可以由 HTTP 请求、cron 调度或数据库事件触发——也可以作为工具暴露给 AI 代理。 - - - - -每个函数文件都使用 `defineLogicFunction()` 导出包含处理程序和可选触发器的配置。 - -```ts src/logic-functions/createPostCard.logic-function.ts -import { defineLogicFunction } from 'twenty-sdk/define'; -import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk/define'; -import { CoreApiClient, type Person } from 'twenty-client-sdk/core'; - -const handler = async (params: RoutePayload) => { - const client = new CoreApiClient(); - const name = 'name' in params.queryStringParameters - ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world' - : '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: 'GET', - 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` 调用 -* **cron**:使用 CRON 表达式按计划运行你的函数。 -* **databaseEvent**:在工作空间对象生命周期事件上运行。 当事件操作为 `updated` 时,可以在 `updatedFields` 数组中指定要监听的特定字段。 如果未定义或为空,任何更新都会触发该函数。 -> 例如 `person.updated`、`*.created`、`company.*` - - -你也可以使用 CLI 手动执行函数: - -```bash filename="Terminal" -yarn twenty exec -n create-new-post-card -p '{"key": "value"}' -``` - -```bash filename="Terminal" -yarn twenty exec -y e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf -``` - -你可以通过以下方式查看日志: - -```bash filename="Terminal" -yarn twenty logs -``` - - -#### 路由触发器负载 - -当路由触发器调用你的逻辑函数时,它会接收一个遵循 -[AWS HTTP API v2 格式](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html)的 `RoutePayload` 对象。 -从 `twenty-sdk` 导入 `RoutePayload` 类型: - -```ts -import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define'; - -const handler = async (event: RoutePayload) => { - const { headers, queryStringParameters, pathParameters, body } = event; - const { method, path } = event.requestContext.http; - - return { message: 'Success' }; -}; -``` - -`RoutePayload` 类型具有以下结构: - - | 属性 | 类型 | 描述 | 示例 | - | ---------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | - | `headers` | `Record\` | HTTP 请求头(仅限 `forwardedRequestHeaders` 中列出的那些) | 见下文 | - | `queryStringParameters` | `Record\` | 查询字符串参数(多个值以逗号连接) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | - | `pathParameters` | `Record\` | 从路由模式中提取的路径参数 | `/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` | 请求体是否为 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 }; -}; -``` - - -请求头名称会被规范化为小写。 请使用小写键访问它们(例如,`event.headers['content-type']`)。 - - -#### 将函数公开为 AI 工具或工作流操作 - -逻辑函数可以在两个入口对外公开,每个入口都有各自的触发器: - -* **`toolTriggerSettings`** — 使该函数可被 Twenty 的 AI 功能(chat、MCP、function calling)发现。 使用标准 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`,即可在 chat 和工作流构建器中同时公开它。 -* `toolTriggerSettings.inputSchema` 和 `workflowActionTriggerSettings.inputSchema` 均为可选。 如果省略,清单构建器会根据处理器源代码进行推断(AI 工具使用 JSON Schema,工作流操作使用 Twenty 的 `InputSchema`)。 当你需要更丰富的类型时,可显式提供一个 — 例如,在工作流构建器中使用对 `FieldMetadataType` 友好的字段(如 `CURRENCY` 或 `RELATION`),或提供 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'], - }, - }, -}); -``` - - -**写一个好的 `description`。** AI 智能体会依赖该函数的 `description` 字段来决定何时使用该工具。 明确说明该工具的作用以及应在何时调用。 - - - - - -安装后函数是在你的应用安装到工作区后自动运行的逻辑函数。 服务器会在应用的元数据已同步并已生成 SDK 客户端**之后**执行它,因此工作区已完全可用,且新架构已就绪。 常见用例包括预置默认数据、创建初始记录、配置工作区设置,或在第三方服务上预配资源。 - -```ts src/logic-functions/post-install.ts -import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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 exec --postInstall -``` - -关键点: -* 安装后函数使用 `definePostInstallLogicFunction()` — 这是一个省略触发器设置(`cronTriggerSettings`、`databaseEventTriggerSettings`、`httpRouteTriggerSettings`、`toolTriggerSettings`、`workflowActionTriggerSettings`)的专用变体。 -* 处理程序会接收一个 `InstallPayload`,其为 `{ previousVersion?: string; newVersion: string }` —— `newVersion` 是正在安装的版本,而 `previousVersion` 是先前已安装的版本(在全新安装时为 `undefined`)。 使用这些值来区分全新安装与升级,并运行特定版本的迁移逻辑。 -* **钩子何时运行**:默认情况下,仅在全新安装时运行。 如果还希望在应用从旧版本升级时运行,请传入 `shouldRunOnVersionUpgrade: true`。 若省略,该标志默认为 `false`,升级将跳过该钩子。 -* **执行模型 — 默认异步,可选择同步**:`shouldRunSynchronously` 标志控制安装后*如何*执行。 - * `shouldRunSynchronously: false` *(默认)* — 该钩子会**加入消息队列**,设置 `retryLimit: 3`,并在工作线程中异步运行。 作业一入列,安装响应即返回,因此缓慢或失败的处理程序不会阻塞调用方。 工作线程最多会重试三次。 **将其用于长时间运行的作业**——预填充大型数据集、调用缓慢的第三方 API、预配外部资源,以及任何可能超出合理 HTTP 响应窗口的任务。 - * `shouldRunSynchronously: true` — 该钩子会在安装流程中**内联执行**(与安装前使用相同的执行器)。 安装请求将阻塞直至处理程序完成;若抛出异常,安装调用方将收到 `POST_INSTALL_ERROR`。 不进行自动重试。 **用于需要在响应前完成的快速工作**——例如向用户返回验证错误,或进行安装调用返回后客户端将立即依赖的快速设置。 请注意,运行安装后时,元数据迁移已应用完成,因此同步模式下的失败**不会**回滚架构更改——它只会暴露错误。 -* 确保你的处理程序是幂等的。 在异步模式下,队列最多可重试三次;在任一模式下,当 `shouldRunOnVersionUpgrade: true` 时,该钩子在升级时可能再次运行。 -* 在处理程序内可使用环境变量 `APPLICATION_ID`、`APP_ACCESS_TOKEN` 和 `API_URL`(与其他逻辑函数相同),因此你可以使用作用域限定到你应用的应用访问令牌调用 Twenty API。 -* 每个应用仅允许一个安装后函数。 如果检测到多个,清单构建将报错。 -* 构建期间,函数的 `universalIdentifier`、`shouldRunOnVersionUpgrade` 和 `shouldRunSynchronously` 会自动附加到应用清单的 `postInstallLogicFunction` 字段下——你无需在 `defineApplication()` 中引用它们。 -* 默认超时时间设置为 300 秒(5 分钟),以便支持更长的设置任务,如数据填充。 -* **开发模式下不执行**:当应用在本地注册(通过 `yarn twenty dev`)时,服务器会完全跳过安装流程,并通过 CLI 监视器直接同步文件——因此无论 `shouldRunSynchronously` 如何,安装后在开发模式下都不会运行。 使用 `yarn twenty exec --postInstall` 在运行中的工作区上手动触发它。 - - - - -安装前函数是在安装期间自动运行的逻辑函数,**在应用工作区元数据迁移之前**。 它与安装后共享相同的负载结构(`InstallPayload`),但在安装流程中位置更早,因此可以准备即将到来的迁移所依赖的状态——典型用例如备份数据、验证与新架构的兼容性,或归档即将被重构或删除的记录。 - -```ts src/logic-functions/pre-install.ts -import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; - -const handler = async (payload: InstallPayload): Promise => { - 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 exec --preInstall -``` - -关键点: -* 安装前函数使用 `definePreInstallLogicFunction()`——与安装后相同的专用配置,只是附加到不同的生命周期阶段。 -* 安装前和安装后处理程序接收相同的 `InstallPayload` 类型:`{ previousVersion?: string; newVersion: string }`。 导入一次,可在两个钩子中复用。 -* **钩子何时运行**:位于工作区元数据迁移(`synchronizeFromManifest`)之前。 在执行之前,服务器会运行一次纯增量的“精简同步”,将**新**版本的安装前函数注册到工作区元数据中——不会触及其他任何内容——然后再执行它。 由于此次同步仅为增量操作,当你的处理程序运行时,上一版本的对象、字段和数据仍完好无损:你可以安全地读取并备份迁移前的状态。 -* **执行模型**:安装前以**同步**方式执行,并且**会阻塞安装**。 如果处理程序抛出异常,安装会在任何架构更改应用之前被中止——工作区将保持在上一版本且处于一致状态。 这是有意为之:安装前是你拒绝高风险升级的最后机会。 -* 与安装后相同,每个应用仅允许一个安装前函数。 在构建期间,它会自动附加到应用清单的 `preInstallLogicFunction` 下。 -* **开发模式下不执行**:与安装后相同——对于本地注册的应用将完全跳过安装流程,因此在 `yarn twenty dev` 下不会运行安装前。 使用 `yarn twenty exec --preInstall` 手动触发它。 - - - - -两个钩子都属于同一安装流程,并接收相同的 `InstallPayload`。 区别在于它们相对于工作区元数据迁移**何时**运行,这会影响它们可以安全访问的数据范围。 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ install flow │ -│ │ -│ upload package → [pre-install] → metadata migration → │ -│ generate SDK → [post-install] │ -│ │ -│ old schema visible new schema visible │ -└─────────────────────────────────────────────────────────────┘ -``` - -安装前始终为**同步**(会阻塞安装并可中止它)。 安装后**默认异步**——在工作线程中入列并自动重试——但可通过 `shouldRunSynchronously: true` 选择同步执行。 关于各模式的使用场景,请参见上方的 `definePostInstallLogicFunction` 折叠面板。 - -**对于需要新架构已存在的任何事项,请使用 `post-install`。** 这是最常见的情况: - -* 针对新添加的对象和字段预填充默认数据(创建初始记录、默认视图、演示内容)。 -* 在应用已有凭据的前提下,向第三方服务注册 Webhook。 -* 调用你自己的 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 => { - 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`,你需要先删除或修正具有空值的行。 -* **验证兼容性;若当前数据无法干净迁移则拒绝升级**——从处理程序中抛出异常,安装将中止且不会应用任何更改。 这比在迁移中途才发现不兼容要更安全。 -* **在会导致关联丢失的架构更改之前**对数据进行重命名或重新设置键。 - -示例——在破坏性迁移之前归档记录: - -```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 => { - // 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`(默认) | - - -如有不确定,默认选择**安装后(post-install)**。 仅当迁移本身具有破坏性,且你需要在其丢失之前拦截先前状态时,才使用安装前。 - - - - - -## 类型化 API 客户端(`twenty-client-sdk`) - -`twenty-client-sdk` 包提供了两个类型化的 GraphQL 客户端,供你的逻辑函数和前端组件与 Twenty API 交互。 - -| 客户端 | 导入 | 端点 | 是否生成? | -| ------------------- | ---------------------------- | ------------------------ | --------- | -| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql`——工作区数据(记录、对象) | 是,在开发/构建时 | -| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata`——工作区配置、文件上传 | 否,已预构建提供 | - - - - -`CoreApiClient` 是用于查询和变更工作区数据的主要客户端。 它会在执行 `yarn twenty dev` 或 `yarn twenty 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, - }, -}); -``` - -该客户端使用选择集语法:传入 `true` 以包含某字段,使用 `__args` 传递参数,并通过嵌套对象表示关系。 你将基于工作区架构获得完整的自动补全和类型检查。 - - -**CoreApiClient 在开发/构建时生成。** 如果在未先运行 `yarn twenty dev` 或 `yarn twenty build` 的情况下尝试使用它,将会抛出错误。 该生成过程是自动完成的——CLI 会自省你的工作区 GraphQL 架构,并使用 `@genql/cli` 生成类型化客户端。 - - -#### 使用 CoreSchema 进行类型标注 - -`CoreSchema` 提供与工作区对象相匹配的 TypeScript 类型,可用于为组件状态或函数参数进行类型标注: - -```ts -import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; -import { useState } from 'react'; - -const [company, setCompany] = useState< - Pick | undefined ->(undefined); - -const client = new CoreApiClient(); -const result = await client.query({ - company: { - __args: { filter: { position: { eq: 1 } } }, - id: true, - name: true, - }, -}); -setCompany(result.company); -``` - - - - -`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,你可以用它来访问已上传的文件。 - - - - - - 当你的代码在 Twenty 上运行(逻辑函数或前端组件)时,平台会以环境变量的形式注入凭据: - - * `TWENTY_API_URL`——Twenty API 的基础 URL - * `TWENTY_APP_ACCESS_TOKEN`——作用域限定为你的应用默认函数角色的短期密钥 - - 你无需将这些值传递给客户端——它们会自动从 `process.env` 读取。 API 密钥的权限由你的 `application-config.ts` 中 `defaultRoleUniversalIdentifier` 引用的角色决定。 - diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/publishing.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/publishing.mdx deleted file mode 100644 index de1e441a05..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/publishing.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: 发布 -icon: 上传 -description: 将你的 Twenty 应用分发到应用市场,或进行内部部署。 ---- - -## 概览 - -一旦你的应用已[在本地构建并完成测试](/l/zh/developers/extend/apps/building),你可以通过两种方式进行分发: - -* **部署 tar 包** — 直接将你的应用上传到特定的 Twenty 服务器,以供内部或私有使用。 -* **发布到 npm** — 将你的应用在 Twenty 应用市场上架,供任何工作区发现并安装。 - -两种路径都从同一个**构建**步骤开始。 - -## 构建你的应用 - -Run the build command to compile your app and generate a distribution-ready `manifest.json`: - -```bash filename="Terminal" -yarn twenty build -``` - -This compiles TypeScript sources, transpiles logic functions and front components, and writes everything to `.twenty/output/`. Add `--tarball` to also produce a `.tgz` package for manual distribution or the deploy command. - -## 部署到服务器(tar 包) - -对于你不希望公开的应用(专有工具、仅供企业使用的集成或实验性构建),你可以将 tar 包直接部署到某台 Twenty 服务器。 - -### 先决条件 - -在部署之前,你需要配置一个指向目标服务器的远程。 远程会将服务器 URL 和身份验证凭据本地存储在 `~/.twenty/config.json` 中。 - -添加远程: - -```bash filename="Terminal" -yarn twenty remote add --api-url https://your-twenty-server.com --as production -``` - -### 部署 - -一步构建并将你的应用上传到服务器: - -```bash filename="Terminal" -yarn twenty deploy -# To deploy to a specific remote: -# yarn twenty deploy --remote production -``` - -### 共享已部署的应用 - - -在多个工作区之间共享私有(tarball)应用是一项 **Enterprise** 功能。 在您的工作区拥有有效的 Enterprise 密钥之前,**Distribution** 选项卡将显示升级提示,而不是共享控件。 请前往 [设置 > 管理面板 > Enterprise](/settings/admin-panel#enterprise) 以启用。 - - -通过 tar 包分发的应用不会出现在公共市场中,因此同一服务器上的其他工作区无法通过浏览发现它们。 一旦您的工作区升级到企业版计划,您就可以像这样分享已部署的应用: - -1. 前往 **Settings > Applications > Registrations** 并打开你的应用 -2. 在 **Distribution** 选项卡中,点击 **Copy share link** -3. 将此链接分享给其他工作区的用户 — 它会将他们直接带到该应用的安装页面 - -该分享链接使用服务器的基础 URL(不包含任何工作区子域),因此适用于该服务器上的任意工作区。 - -### 版本管理 - -在更新已部署的 tarball 应用时,服务器要求 `package.json` 中的 `version` 必须**严格高于**(按[语义化版本](https://semver.org)排序)当前已部署的版本。 在 tar 包存储之前,重新部署相同版本或推送更低版本都会被拒绝 — 你会在 CLI 中看到 `VERSION_ALREADY_EXISTS` 错误。 - -要发布更新: - -1. 将 `package.json` 中的 `version` 字段递增(例如:`1.2.3` → `1.2.4`、`1.3.0` 或 `2.0.0`) -2. Run `yarn twenty deploy` (or `yarn twenty deploy --remote production`) -3. 已安装该应用的工作区会在其设置中看到可用的升级 - - -预发布标签按预期工作:将 `1.0.0-rc.1` 递增为 `1.0.0-rc.2` 是允许的,并且像 `1.0.0` 这样的正式发布会被正确识别为高于 `1.0.0-rc.5`。 `package.json` 中的版本本身必须是有效的 SemVer 字符串。 - - -{/* 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`,则跳过该检查。 - - -服务器是权威校验方——它会在 tarball 上传和工作区安装时验证 `engines.twenty`。 即使你通过带外方式部署 tarball 或从应用市场安装,服务器仍会强制执行兼容性要求。 - - -## 自动化 CI/CD(脚手架生成的工作流) - -使用 `create-twenty-app` 生成的应用开箱即带有两个 GitHub Actions 工作流,位于 `.github/workflows/`。 当你将仓库推送到 GitHub 后即可运行——CI 无需额外设置,CD 只需要一个机密。 - -### CI — `ci.yml` - -它会在每次向 `main` 推送以及拉取请求上自动运行你的集成测试。 - -**作用:** - -1. 检出你的应用源代码。 -2. 使用 `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` 组合 action 启动一个隔离的 Twenty 测试实例(相当于 CI 中的 `yarn twenty server start --test`)。 -3. 启用 Corepack,从你的 `.nvmrc` 设置 Node.js,并使用 `yarn install --immutable` 安装依赖。 -4. 运行 `yarn test`,并从启动的实例传入 `TWENTY_API_URL` 和 `TWENTY_API_KEY`,以便你的测试可以与真实服务器通信。 - -**配置选项:** - -* `TWENTY_VERSION`(环境变量,默认 `latest`)— 通过在 `ci.yml` 中编辑它来固定 CI 使用的 Twenty 服务器版本。 -* 并发按 `github.ref` 分组,并会在有新的推送时取消进行中的运行。 - -不需要任何机密——测试实例是临时的,只在作业持续期间存在。 - -### CD — `cd.yml` - -在每次向 `main` 推送时将你的应用部署到已配置的 Twenty 服务器;当为拉取请求添加 `deploy` 标签时,也可从该拉取请求进行部署。 - -**作用:** - -1. 检出 PR 的 head(针对已加标签的 PR),或被推送的提交。 -2. 运行 `twentyhq/twenty/.github/actions/deploy-twenty-app@main`——相当于 CI 中的 `yarn twenty deploy`。 -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 密钥。 | - - -默认的 `TWENTY_DEPLOY_URL` 值 `http://localhost:3000` 只是占位符——从 GitHub 托管的 runner 无法访问任何资源。 在启用 CD 之前,将其更新为你服务器的公网 URL(或使用具有网络访问权限的自托管 runner)。 - - -**从 PR 触发预览部署:** - -为拉取请求添加 `deploy` 标签。 在 `cd.yml` 中的 `if:` 守卫会使用该 PR 的 head 提交为其运行作业,使你能在合并前在目标服务器上验证更改。 - -### 固定可复用的 actions - -两个工作流都引用了位于 `@main` 的可复用 actions,因此会自动获取 `twentyhq/twenty` 仓库中的 action 更新。 如果你希望构建具有确定性,请在每个 `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"] -} -``` - -### 应用市场元数据 - -The `defineApplication()` config supports optional fields that control how your app appears in the marketplace. Use `logoUrl` and `screenshots` to reference images from the `public/` folder: - -```ts src/application-config.ts -export default defineApplication({ - universalIdentifier: '...', - displayName: 'My App', - description: 'A great app', - defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, - logoUrl: 'public/logo.png', - screenshots: [ - 'public/screenshot-1.png', - 'public/screenshot-2.png', - ], -}); -``` - -See the [defineApplication accordion](/l/zh/developers/extend/apps/building#defineentity-functions) in the Building Apps page for the full list of marketplace fields (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.). - -#### 建议的屏幕截图尺寸 - -该市场会在固定的 `8:5` 容器中渲染 `screenshots`(例如,`1600×1000 px`)。 - - -任意纵横比的屏幕截图都会完整显示,且绝不会被裁剪,但相对于 `8:5` 明显更高或更窄的图片,两侧会出现空白边。 - - -### Publish - -```bash filename="Terminal" -yarn twenty publish -``` - -要在特定的 dist-tag(例如 `beta` 或 `next`)下发布: - -```bash filename="Terminal" -yarn twenty publish --tag beta -``` - -### 应用市场的发现机制如何运作 - -The Twenty server syncs its marketplace catalog from the npm registry **every hour**. - -You can trigger the sync immediately instead of waiting: - -```bash filename="Terminal" -yarn twenty server catalog-sync -# To target a specific remote: -# yarn twenty server catalog-sync --remote production -``` - -The metadata shown in the marketplace comes from your `defineApplication()` config — fields like `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl`, and `termsUrl`. - - -如果您的应用未在 `defineApplication()` 中定义 `aboutDescription`,市场将自动使用 npm 上您的软件包的 `README.md` 作为关于页面内容。 这意味着您可以为 npm 和 Twenty 市场维护同一个 README。 如果您希望在市场中使用不同的描述,请显式设置 `aboutDescription`。 - - -### CI 发布 - -Use this GitHub Actions workflow to publish automatically on every release (uses [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 build - - run: npm publish --provenance --access public - working-directory: .twenty/output -``` - -对于其他 CI 系统(GitLab CI、CircleCI 等),同样适用以下三条命令:`yarn install`、`yarn twenty build`,然后在 `.twenty/output` 目录下执行 `npm publish`。 - - -**npm provenance** 可选,但建议启用。 使用 `--provenance` 发布会在你的 npm 列表中添加可信徽章,使用户可以验证该包是由公共 CI 流水线中的特定提交构建的。 有关设置说明,请参见 [npm provenance 文档](https://docs.npmjs.com/generating-provenance-statements)。 - - -## 安装应用 - -Once an app is published (npm) or deployed (tarball), workspaces can install it through the UI. - -Go to the **Settings > Applications** page in Twenty, where both marketplace and tarball-deployed apps can be browsed and installed. - -{/* TODO: add screenshot of the UI when the app is registered */} - -You can also install apps from the command line: - -```bash filename="Terminal" -yarn twenty install -``` - - -服务器在安装时强制执行 SemVer 版本控制,与部署时的规则一致: - -* 尝试安装与工作区中已安装版本相同的版本将被拒绝,并返回 `APP_ALREADY_INSTALLED` 错误。 -* 尝试安装低于当前已安装版本的版本将被拒绝,并返回 `CANNOT_DOWNGRADE_APPLICATION` 错误。 - -若要安装较新的版本,请先部署或发布它,然后重新运行 `yarn twenty install`。 - diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/skills-and-agents.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/skills-and-agents.mdx deleted file mode 100644 index c904bfd334..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/apps/skills-and-agents.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: 技能与智能体 -description: 为你的应用定义 AI 技能和智能体。 -icon: robot ---- - - - 技能和智能体目前处于 Alpha 阶段。 该功能可用,但仍在演进中。 - - -应用可以定义存在于工作区内的 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`(可选)提供有关技能用途的更多上下文。 - - - - -智能体是在你的工作区内驻留的 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 模型。 - - - diff --git a/packages/twenty-docs/l/zh/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/l/zh/developers/extend/capabilities/apps.mdx deleted file mode 100644 index 677498e6b9..0000000000 --- a/packages/twenty-docs/l/zh/developers/extend/capabilities/apps.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Twenty 应用 -description: 以代码的形式构建并管理 Twenty 自定义项。 ---- - - -应用目前处于 Alpha 阶段。 该功能可用,但仍在演进中。 - - -## 什么是应用? - -应用可通过自定义对象、字段、逻辑函数、前端组件、AI 技能等来扩展 Twenty——全部以代码进行管理。 无需通过 UI 配置所有内容,你可以用 TypeScript 定义数据模型和逻辑,并将其部署到一个或多个工作空间。 - -**你可以构建的内容:** - -* **自定义对象和字段** — 使用新实体扩展你的数据模型,或为现有对象(如 Company 或 Person)添加字段 -* **逻辑函数** — 由数据库事件、定时任务(cron)或 HTTP 路由触发的服务端函数 -* **前端组件** — 在 Twenty 的 UI 中渲染的 React 组件(记录页面、命令菜单、侧边面板) -* **AI 技能和智能体** — 通过自定义能力扩展 Twenty 的 AI -* **视图和导航** — 预配置的已保存视图和侧边栏链接 - -## 快速开始 - -```bash filename="Terminal" -npx create-twenty-app@latest my-twenty-app -cd my-twenty-app -yarn twenty dev -``` - -这将搭建一个新的应用,可选地启动本地 Twenty 服务器,并开始监视你的文件变更。 请参阅[开始使用](/l/zh/developers/extend/apps/getting-started)指南,查看完整演练。 - -## 详细指南 - -| 指南 | 描述 | -| ----------------------------------------------- | ----------------------------------------------------------------------------------------- | -| [开始使用](/l/zh/developers/extend/apps/getting-started) | 搭建应用、设置本地服务器、项目结构、CI | -| [构建应用](/l/zh/developers/extend/apps/building) | 实体定义(`defineObject`、`defineLogicFunction`、`defineFrontComponent` 等)、API 客户端、npm 包、公共资源、测试 | -| [发布](/l/zh/developers/extend/apps/publishing) | 部署到服务器、发布到 npm、应用市场 | - -## 关键概念 - -### 实体检测 - -SDK 通过扫描你的 TypeScript 文件中的 `export default define({...})` 调用来检测实体。 文件命名和文件夹结构是灵活的 — 检测基于 AST,而非基于路径。 - -### 可用的实体类型 - -| 函数 | 目的 | -| ---------------------------------- | ----------------------- | -| `defineApplication()` | 应用元数据(必需,每个应用一个) | -| `defineObject()` | 带字段的自定义对象 | -| `defineField()` | 现有对象上的字段 | -| `defineLogicFunction()` | 带触发器的服务端逻辑 | -| `defineFrontComponent()` | Twenty 的 UI 中的 React 组件 | -| `defineRole()` | 权限角色 | -| `defineView()` | 已保存视图配置 | -| `defineNavigationMenuItem()` | 侧边栏导航链接 | -| `defineSkill()` | AI 智能体技能 | -| `defineAgent()` | 带提示词的 AI 智能体 | -| `definePageLayout()` | 自定义记录页面布局 | -| `definePreInstallLogicFunction()` | 在应用安装之前运行 | -| `definePostInstallLogicFunction()` | 在应用安装之后运行 | - -### 开发工作流程 - -1. **`yarn twenty dev`** — 监视源文件、更改时重新构建、同步到服务器、生成类型化的 API 客户端 -2. **`yarn twenty build`** — 生成可分发的构建产物 -3. **`yarn twenty deploy`** — 部署到远程 Twenty 服务器 -4. **`yarn twenty add`** — 交互式生成一个新实体 - -### CLI 参考 - -```bash filename="Terminal" -yarn twenty help # 列出所有命令 -yarn twenty server start # 启动本地开发服务器 -yarn twenty remote add # 连接到 Twenty 服务器 -yarn twenty exec -n fn # 执行逻辑函数 -yarn twenty logs -n fn # 实时查看函数日志 -``` - -请参阅[开始使用](/l/zh/developers/extend/apps/getting-started)指南以获取完整的 CLI 参考。 diff --git a/packages/twenty-docs/l/zh/user-guide/settings/capabilities/releases-settings.mdx b/packages/twenty-docs/l/zh/user-guide/settings/capabilities/releases-settings.mdx deleted file mode 100644 index 4180b67161..0000000000 --- a/packages/twenty-docs/l/zh/user-guide/settings/capabilities/releases-settings.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Releases Settings -description: Enable experimental features in Twenty. ---- - -## About Releases Settings - -The Releases section allows you to enable experimental features before they're generally available. - -## Lab Features - -Lab features are experimental capabilities that are still being developed. They may change or be removed without notice. - -### How to Enable Lab Features - -1. Go to **Settings → Releases** -2. Find the feature you want to enable -3. Toggle it on -4. The feature will be available immediately - - - Lab features are experimental and may not work as expected. Use them with caution in production environments. - - -## Feature Feedback - -Your feedback helps improve Twenty: - -* Report issues with experimental features -* Share how you're using new features -* Suggest improvements via the community Discord