snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
---
title: Komut menüsü öğeleri
description: Ön bileşenleri hızlı işlemler ve komut menüsü (Cmd+K) girdileri olarak `defineCommandMenuItem` ile sunun.
icon: terminal
---
Bir **komut menüsü öğesi**, kullanıcı ile bir [ön bileşen](/l/tr/developers/extend/apps/layout/front-components) arasında köprü görevi görür. Bu, bileşeni Twenty'nin komut menüsüne (Cmd+K) ve isteğe bağlı olarak sayfanın sağ üst köşesindeki sabitlenmiş bir hızlı işlem düğmesi olarak kaydeder.
```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',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
## Yapılandırma alanları
| 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 bileşenin `universalIdentifier` değeri |
| `shortLabel` | Hayır | Sabitlenmiş hızlı işlem düğmesinde görüntülenen daha kısa etiket |
| `icon` | Hayır | **Kullanımdan kaldırıldı** — uygulama simgesi tercih edildiği için yok sayılır; ayarlanırsa oluşturma sırasında bir uyarı verir |
| `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), `'GLOBAL_OBJECT_CONTEXT'` (yalnızca bir nesne bağlamı olan sayfalarda — indeks ve kayıt sayfaları), `'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 | Görünürlüğü dinamik olarak kontrol eden bir boolean ifade (aşağıya bakın) |
## Arayüzsüz komutlar
Bir [arayüzsüz ön bileşen](/l/tr/developers/extend/apps/layout/front-components#headless-vs-non-headless) ile eşleştirilmiş bir komut menüsü öğesi, tek tıklamayla bir eylem sunmanın — kod çalıştırma, gezinme veya onaylayıp yürütme — yaygın kullanılan biçimidir. Ön Bileşenler sayfası, eylem-ve-kaldırma modelini yöneten [SDK Command bileşenlerini](/l/tr/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) kapsar.
Tipik bir akış: başsız bir bileşen `<Command execute={...} />` oluşturur (bkz. [tam örnek](/l/tr/developers/extend/apps/layout/front-components#sdk-command-components)) ve komut menüsü öğesi ona işaret eder:
```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',
frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
});
```
## 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,
),
});
```
<Note>
`RECORD_SELECTION` zaten boş olmayan bir seçim anlamına gelir — `numberOfSelectedRecords` ifadesini yalnızca belirli sayılar için kullanın (örneğin `>= 2`).
</Note>
### 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` |

View File

@@ -0,0 +1,849 @@
---
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, korumalı (sandbox) ve opak kökenli bir iframe içinde yürütülür; ancak UI, o iframe ile sınırlanmak yerine sayfada yerel olarak işlenir.
<Warning>
Front bileşenleri hâlâ aktif geliştirme aşamasındadır. Kodunuz gerçek bir tarayıcı sayfası yerine kısmi bir DOM üzerinde çalışır, bu nedenle ileri düzey kullanımlar çoğu zaman sessizce başarısız olabilir. [Mevcut sınırlamalara](#current-limitations) bakın.
</Warning>
## Ön uç bileşenlerinin kullanılabileceği yerler
Ön uç bileşenler, Twenty içinde üç 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üzenleri](/l/tr/developers/extend/apps/layout/page-layouts) içinde 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.
* **Uygulama ayarları** — [`defineSettingsFrontComponent()`](#custom-settings-component) ile tanımlandığında, ön uç bileşeni, varsayılan değişken yapılandırma kullanıcı arayüzü yerine uygulamanın **Settings** sekmesi içinde bir bölüm olarak görüntülenir.
Tek başına bir ön uç bileşenine kullanıcı arayüzünden erişilemez — onu görünür hâle getirmeniz gerekir. Bunu yapmanın üç yolu vardır:
* **Onu bir [komut menüsü öğesi](/l/tr/developers/extend/apps/layout/command-menu-items) ile eşleştirin** — komut menüsüne (Cmd+K) ve isteğe bağlı olarak sabitlenmiş hızlı işlem olarak kaydeder.
* **Onu bir [sayfa düzeni](/l/tr/developers/extend/apps/layout/page-layouts) içinde widget olarak gömün** — bir kaydın ayrıntı sayfasına veya panosuna yerleştirir.
* **Bunu [`defineSettingsFrontComponent()`](#custom-settings-component) ile tanımlayın** — varsayılan değişken yapılandırma kullanıcı arayüzü yerine uygulamanın **Settings** sekmesi içinde bir bölüm olarak görüntülenir.
## Basit örnek
Bir ön uç bileşenini çalışır halde görmenin en hızlı yolu, onu [`defineCommandMenuItem`](/l/tr/developers/extend/apps/layout/command-menu-items) ile eşleştirmektir; böylece sayfanın sağ üst köşesinde bir hızlı işlem düğmesi olarak görünür:
```tsx src/front-components/hello-world.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
const HelloWorld = () => {
return (
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
<h1>Hello from my app!</h1>
<p>This component renders inside Twenty.</p>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
name: 'hello-world',
description: 'A simple front component',
component: HelloWorld,
});
```
```ts src/command-menu-items/hello-world.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345',
shortLabel: 'Hello',
label: 'Hello World',
isPinned: true,
availabilityType: 'GLOBAL',
frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948',
});
```
`yarn twenty dev` ile senkronize ettikten sonra (veya tek seferlik bir `yarn twenty apply` çalıştırdıktan sonra), hızlı işlem sayfanın sağ üst köşesinde görünür:
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/quick-action.png" alt="Sağ üst köşedeki hızlı işlem düğmesi" />
</div>
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 [Sayfa Düzenleri](/l/tr/developers/extend/apps/layout/page-layouts) bölümüne bakın.
## Özel ayarlar bileşeni
Uygulamanızın **Settings** sekmesindeki otomatik oluşturulan değişken yapılandırma kullanıcı arayüzünü kendi bileşeninizle değiştirmek için, `defineFrontComponent` yerine `defineSettingsFrontComponent` ile tanımlayın. Ayarlar bileşeni her zaman görünür bir kullanıcı arayüzü oluşturduğundan, kabul edilmeyen `isHeadless` dışında, aynı [yapılandırma alanlarını](#configuration-fields) alır ve ek olarak bileşeni uygulamanın ayarlar arayüzü olarak işaretler.
Bileşen, tüm sekmenin yerine geçmek yerine, Settings sekmesi içinde bir bölüm olarak görüntülenir. Twenty'nin sistem tarafından yönetilen bölümleri — otomatik yükseltme, App URL ve bağlantılar — her zaman bunun üstünde görüntülenir ve uygulama tarafından geçersiz kılınamaz.
```tsx src/front-components/app-settings.tsx
import { defineSettingsFrontComponent } from 'twenty-sdk/define';
const AppSettings = () => {
return (
<div style={{ padding: '20px' }}>
<h2>My app settings</h2>
{/* render your own configuration UI here */}
</div>
);
};
export default defineSettingsFrontComponent({
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
name: 'app-settings',
description: "Custom UI for the app's Settings tab",
component: AppSettings,
});
```
Uygulama başına yalnızca bir ayarlar ön uç bileşenine izin verilir; birden fazla tanımlanması derlemenin başarısız olmasına neden olur. Mevcut olduğunda, uygulamanın **Settings** sekmesi, varsayılan değişken yapılandırma kullanıcı arayüzü yerine bu bileşeni görüntüler.
## 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 { useSelectedRecordIds, enqueueSnackbar } from 'twenty-sdk/front-component';
import { useEffect } from 'react';
const SyncTracker = () => {
const [recordId] = useSelectedRecordIds();
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 uç 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/front-component` 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`** — Bir yan panel sayfasını açar. Props, `page`'e bağlıdır — örneğin `ViewRecord`, `recordId` + `objectNameSingular` alır (ayrıca kaydı belirli bir sekmede açmak için isteğe bağlı bir `tab` kimliği alır), diğer sayfalar `pageTitle` + `pageIcon` alır.
`Command` kullanarak komut menüsünden bir eylem çalıştıran headless bir ön uç bileşenin tam örneği:
```tsx src/front-components/run-action.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { Command } from 'twenty-sdk/front-component';
import { CoreApiClient } from 'twenty-client-sdk/core';
const RunAction = () => {
const execute = async () => {
const client = new CoreApiClient();
await client.mutation({
createTask: {
__args: { data: { title: 'Created by my app' } },
id: true,
},
});
};
return <Command execute={execute} />;
};
export default defineFrontComponent({
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234',
name: 'run-action',
description: 'Creates a task from the command menu',
component: RunAction,
isHeadless: true,
});
```
```ts src/command-menu-items/run-action.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345',
label: 'Run my action',
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/front-component';
const DeleteDraft = () => {
const execute = async () => {
// perform the deletion
};
return (
<CommandModal
title="Delete draft?"
subtitle="This action cannot be undone."
execute={execute}
confirmButtonText="Delete"
confirmButtonAccent="danger"
/>
);
};
export default defineFrontComponent({
universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456',
name: 'delete-draft',
description: 'Deletes a draft with confirmation',
component: DeleteDraft,
isHeadless: true,
});
```
Ve geçerli kaydı belirli bir sekmede yan panelde açmak için `CommandOpenSidePanelPage` kullanan bir örnek. `tab`, bir sayfa düzeni sekme kimliğidir (varsayılan düzenler `company-tab-emails` veya `company-tab-timeline` gibi kimlikler kullanır; özel düzenler sekmenin kendi kimliğini kullanır). Kimlik kaydın düzeninde mevcut değilse, bunun yerine varsayılan sekme açılır:
```tsx src/front-components/open-company-emails.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import {
CommandOpenSidePanelPage,
SidePanelPages,
useSelectedRecordIds,
} from 'twenty-sdk/front-component';
const OpenCompanyEmails = () => {
const selectedRecordIds = useSelectedRecordIds();
const recordId = selectedRecordIds.length === 1 ? selectedRecordIds[0] : null;
if (!recordId) {
return null;
}
return (
<CommandOpenSidePanelPage
page={SidePanelPages.ViewRecord}
recordId={recordId}
objectNameSingular="company"
tab="company-tab-emails"
resetNavigationStack={false}
/>
);
};
export default defineFrontComponent({
universalIdentifier: 'b8c9d0e1-f2a3-4567-bcde-678901234567',
name: 'open-company-emails',
description: 'Opens the current company on its Emails tab',
component: OpenCompanyEmails,
isHeadless: true,
});
```
## Bir mantık işlevini çağırma
Ön bileşenler, tarayıcı tarafında, opak kaynaklı bir iframe içinde yer alan korumalı (sandbox) bir Web Worker içinde çalışırken, [mantık işlevleri](/l/tr/developers/extend/apps/logic/logic-functions) sunucu tarafında çalışır. İkisi arasında doğrudan, işlem içi bir çağrı yoktur — bunun yerine, bir ön bileşen bir mantık işlevine HTTP üzerinden erişir.
`httpRouteTriggerSettings` ile bildirilen bir mantık işlevi, rota yolunda HTTP üzerinden erişilebilir durumdadır. `RestApiClient`, `/s/` ile başlayan yolları uygulama rotaları olarak ele alır, bunları fonksiyonlarınızın sunulduğu URL'ye çözümler ve `TWENTY_APP_ACCESS_TOKEN` ile kimlik doğrulaması yapar.
> **Twenty Cloud üzerinde, HTTP ile tetiklenen mantık işlevleri, çalışma alanı başına ayrılmış özel bir etki alanında** `https://\<your-workspace-subdomain>.withtwenty.com\<path>` adresinde sunulur. Harici çağrıcılar için, tam URL’yi işlevin **HTTP trigger** ayarlarından veya uygulamanın **Settings** sekmesinden kopyalayın.
Başsız bir ön bileşen, çağrıyı `Command` bileşeni aracılığıyla mount sırasında çalıştırabilir ve ardından otomatik olarak unmount olabilir:
```tsx src/front-components/sync-prs.tsx
import { RestApiClient } from 'twenty-client-sdk/rest';
import { defineFrontComponent } from 'twenty-sdk/define';
import { Command } from 'twenty-sdk/front-component';
const SyncPrs = () => {
const execute = async () => {
await new RestApiClient().post('/s/github/fetch-prs', {
owner: 'twentyhq',
repo: 'twenty',
});
};
return <Command execute={execute} />;
};
export default defineFrontComponent({
universalIdentifier: '...',
name: 'sync-prs',
description: 'Triggers the fetch-prs logic function',
isHeadless: true,
component: SyncPrs,
});
```
`RestApiClient`'e iletilen yol, başına `/s` eklenmiş mantık işlevinin `httpRouteTriggerSettings.path` değeridir. `isAuthRequired: true` ayarını koruyun; bileşeniniz için Twenty'nin oluşturduğu `TWENTY_APP_ACCESS_TOKEN` isteği kimlik doğrular:
```ts src/logic-functions/fetch-prs.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk/define';
import type { RoutePayload } from 'twenty-sdk/logic-function';
const handler = async (event: RoutePayload) => {
const { owner, repo } = (event.body ?? {}) as { owner: string; repo: string };
// ...fetch from GitHub and persist records...
return { ok: true };
};
export default defineLogicFunction({
universalIdentifier: '...',
name: 'fetch-prs',
handler,
httpRouteTriggerSettings: {
path: '/github/fetch-prs',
httpMethod: 'POST',
isAuthRequired: true,
},
});
```
<Note>
`TWENTY_APP_ACCESS_TOKEN` otomatik olarak enjekte edilir — bkz. [Uygulama değişkenleri](#application-variables). Gizli uygulama değişkenleri asla ön bileşenlere açığa çıkarılmadığından, API anahtarlarını ve diğer hassas mantığı ön bileşende değil, mantık işlevinin içinde tutun.
</Note>
### Twenty REST API'sini çağırma
Uygulama HTTP rotalarını çağırmak veya bir front bileşeninden Twenty kayıtlarını okumak ve yazmak için `twenty-client-sdk/rest` paketinden `RestApiClient` kullanın. `/s/...` yollarını çalışma alanınızdaki işlevlerin temel URL'sine, `/rest/...` dahil diğer tüm yolları ise `TWENTY_API_URL`'ye gönderir.
Her zaman sayfayı görüntüleyen kişi olarak hareket eder. `runAs: 'application'` yalnızca bir mantık işlevi seçeneğidir: bir bileşen uygulamanızın kendi token'ını asla almaz; dolayısıyla burada bunu istemek hata verir. Uygulamanın kendi erişimini gerektiren işleri bir mantık işlevinin arkasına yerleştirin ve bunun yerine onu çağırın.
| Yöntem | Açıklama |
| --------------------------------- | ------------------------------------------------------------------------ |
| `get(path, options?)` | Bir `GET` isteği gönderir |
| `post(path, body?, options?)` | Bir `POST` isteği gönderir |
| `put(path, body?, options?)` | Bir `PUT` isteği gönderir |
| `patch(path, body?, options?)` | Bir `PATCH` isteği gönderir |
| `delete(path, options?)` | Bir `DELETE` isteği gönderir |
| `request(method, path, options?)` | Herhangi bir HTTP yöntemiyle genel istek |
| `resolveUrl(path, options?)` | Bir istekte bulunmadan (bağlantılar için) bir yolu tam URL'sine çözümler |
`options`, `headers`, `query` (sorgu dizesi parametrelerinin kaydı; null benzeri değerler atlanır) ve `signal` aracılığıyla bir `AbortSignal` kabul eder. `FormData` olmayan bir `body` nesnesi otomatik olarak JSON’a serileştirilir. `401` durumunda, istemci erişim jetonunu bir kez ana makine (host) üzerinden yeniler ve isteği yeniden dener.
Temel URL ve jeton varsayılan olarak ortamdan çözümlenir. Gerektiğinde — örneğin testlerde — kurucuya (constructor) geçersiz kılmalar (override) iletin:
```ts
const client = new RestApiClient({
baseUrl: 'https://myworkspace.twenty.com',
token: 'my-token',
});
```
Başarısız istekler, `status`, `statusText`, `url` ve ayrıştırılmış `body` değerlerini açığa çıkaran bir `RestApiClientError` fırlatır:
```tsx
import { RestApiClient, RestApiClientError } from 'twenty-client-sdk/rest';
const client = new RestApiClient();
try {
const people = await client.get('/rest/people', {
query: { limit: 10 },
});
} catch (error) {
if (error instanceof RestApiClientError) {
console.error(error.status, error.body);
}
}
```
## Ç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,
useSelectedRecordIds,
useFrontComponentId,
} from 'twenty-sdk/front-component';
const RecordInfo = () => {
const userId = useUserId();
const [recordId] = useSelectedRecordIds();
const componentId = useFrontComponentId();
return (
<div>
<p>User: {userId}</p>
<p>Record: {recordId ?? 'No record context'}</p>
<p>Component: {componentId}</p>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012',
name: 'record-info',
component: RecordInfo,
});
```
Kullanılabilir hook'lar:
| Hook | Döndürür | Açıklama |
| --------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------- |
| `useUserId()` | `string` veya `null` | Geçerli kullanıcının ID'si |
| `useSelectedRecordIds()` | `string[]` | 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 |
| `useTimelineActivityId()` | `string` veya `null` | Özel bir zaman çizelgesi satırı oluşturulurken geçerli zaman çizelgesi etkinlik kimliği |
| `useColorScheme()` | `'light'` veya `'dark'` | Ana makine kullanıcı arayüzünün etkin renk şeması (`System` zaten çözümlenmiştir) |
| `useFrontComponentExecutionContext(selector)` | değişir | Bir seçici işlevle tam yürütme bağlamına erişin |
## Uygulama değişkenleri
`isSecret: false` ile [`defineApplication()`](/l/tr/developers/extend/apps/config/application) içinde tanımlanan uygulama değişkenleri, `getApplicationVariable` yardımcı işlevi aracılığıyla ön uç bileşenleri içinde kullanılabilir:
```tsx src/front-components/greeting.tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { getApplicationVariable } from 'twenty-sdk/front-component';
const Greeting = () => {
const recipientName = getApplicationVariable('DEFAULT_RECIPIENT_NAME') ?? 'World';
return <p>Hello, {recipientName}!</p>;
};
export default defineFrontComponent({
universalIdentifier: '...',
name: 'greeting',
component: Greeting,
});
```
<Warning>
Gizli değişkenler (`isSecret: true`) ön uç bileşenlere açıklanmaz. Bunlar yalnızca sunucu tarafında çalışan [mantık işlevlerinde](/l/tr/developers/extend/apps/logic/logic-functions) kullanılabilir. Bu, API anahtarları gibi hassas değerlerin tarayıcıya gönderilmesini engeller.
</Warning>
`getApplicationVariable`, değişkenin bildirilen `type` değerinden bağımsız olarak her zaman bir **string** (veya `undefined`) döndürür. String, türe göre tutarlı bir şekilde serileştirilir (boolean değerler `"true"` / `"false"`, sayılar ondalık dizeler olarak, diziler/nesneler JSON olarak) ve bu, `process.env` mantık işlevi için kullanılan formatla aynıdır — veriyi kendiniz ayrıştırın (`Number(...)`, `JSON.parse(...)`, `=== 'true'`). [Değişken türleri](/l/tr/developers/extend/apps/config/application#variable-types) bölümüne bakın.
Aşağıdaki sistem değişkenleri her zaman `process.env` aracılığıyla kullanılabilir:
| Değişken | Açıklama |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `TWENTY_API_URL` | Twenty çekirdek API'sinin temel URL'si |
| `TWENTY_APP_ACCESS_TOKEN` | Oturum açmış kişinin roluyla uygulamanızın rolunun kesişimiyle kapsamı belirlenen kısa ömürlü token; böylece bir bileşen, onu görüntüleyen kişiden asla daha fazlasını yapamaz |
### `TWENTY_FUNCTIONS_URL`
Twenty ayrıca `TWENTY_FUNCTIONS_URL` değişkenini front bileşenlerine ve mantık işlevlerine enjekte eder: bu, uygulamanızın HTTP ile tetiklenen mantık işlevlerinin sunulduğu temel URL'dir.
Bu değişken, bu URL her zaman Twenty sunucusunun kendisi olmadığı için mevcuttur. Twenty Cloud üzerinde, uygulama rotaları, uygulama tarafından yazılmış yanıtların Twenty uygulamasının kaynağı yerine yalıtılmış bir kaynak üzerinde çalışması için, çalışma alanı başına ayrılmış özel bir etki alanında (`https://\<your-workspace-subdomain>.withtwenty.com` veya yapılandırıldığında uygulamanın birincil genel etki alanında) sunulur. Self-hosted ve yerel örnekler, uygulama rotalarını sunucunun kendisinde `/s` öneki altında sunar ve değişkeni hiç ayarlamayabilir. Temel URL, çalışma alanına ve örneğe göre değiştiğinden, kodunuz bunu sabit olarak yazamaz — sunucu, çalışma zamanında doğru değeri enjekte eder.
Bu değişkeni doğrudan okumanız nadiren gerekir. Rotalarınızı, yolu `/s/` önekiyle birlikte vererek `RestApiClient` üzerinden çağırın; istemci URL'yi sizin için çözümler: `/s` önekini kaldırır ve `TWENTY_FUNCTIONS_URL`'yi hedefler, değişken ayarlı değilse `\<TWENTY_API_URL>/s` adresine geri döner. Mutlak URL'yi bir istek göndermeden elde etmek için, örneğin bir bağlantı için, `resolveUrl('/s/\<path>')` kullanın. Değişkeni yalnızca bir URL'yi elle oluştururken doğrudan okuyun:
```ts
const routeUrl = `${process.env.TWENTY_FUNCTIONS_URL || `${process.env.TWENTY_API_URL}/s`}/documents/generate`;
```
## 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 |
| `uploadFile(file, { fieldMetadataId, fileName? })` | Bir `Blob` dosyasını FILES alanına yükler; `{ status: 'uploaded', file: { fileId, path, url, size, mimeType } }` veya `{ status: 'failed', reason: 'invalid-params' \| 'upload-failed' }` sonucunu döndürür. |
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 { enqueueSnackbar, closeSidePanel, useSelectedRecordIds } from 'twenty-sdk/front-component';
import { CoreApiClient } from 'twenty-client-sdk/core';
const ArchiveRecord = () => {
const [recordId] = useSelectedRecordIds();
const handleArchive = async () => {
const client = new CoreApiClient();
await client.mutation({
updateTask: {
__args: { id: recordId, data: { status: 'ARCHIVED' } },
id: true,
},
});
await enqueueSnackbar({
message: 'Record archived',
variant: 'success',
});
await closeSidePanel();
};
return (
<div style={{ padding: '20px' }}>
<p>Archive this record?</p>
<button onClick={handleArchive}>Archive</button>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678',
name: 'archive-record',
description: 'Archives the current record',
component: ArchiveRecord,
});
```
### Depolama
`localStorage` ve `sessionStorage` normal bir sayfada olduğu gibi, standart senkron API ile çalışır. Anahtarlarınız, uygulama kurulumunuza ve oturum açmış kullanıcıya göre kapsamlandırılır: başka hiçbir uygulama bunları okuyamaz ve aynı tarayıcıda oturum açan başka bir kullanıcı, boş bir depoyla başlar. `localStorage`'a yazılan değerler, yeniden yüklemeler arasında cihazda kalır; `sessionStorage` ise tarayıcı oturumu süresince geçerlidir.
```tsx src/front-components/note-draft.tsx
import { useState } from 'react';
import { defineFrontComponent } from 'twenty-sdk/define';
const NoteDraft = () => {
const [draft, setDraft] = useState(() => localStorage.getItem('draft') ?? '');
const handleChange = (nextDraft: string) => {
setDraft(nextDraft);
localStorage.setItem('draft', nextDraft);
};
return (
<textarea value={draft} onChange={(event) => handleChange(event.target.value)} />
);
};
export default defineFrontComponent({
universalIdentifier: 'd0e1f2a3-b4c5-6789-def0-890123456789',
name: 'note-draft',
description: 'Keeps an unsaved note on this device',
component: NoteDraft,
});
```
Twenty, değerleri uygulamanız adına depolar, böylece bir yazma işlemi yerelde anında uygulanır ve arka planda kaydedilir. Okumalar hiçbir zaman ana makine üzerinde beklemez. Hiçbir şey eşitlenmez: değerler kullanıcıyı başka bir tarayıcıya veya makineye takip etmez, bu yüzden cihaz değişikliğinden sonra da korunması gereken her şey için bir mantık fonksiyonunun [anahtar-değer deposunu](/l/tr/developers/extend/apps/logic/key-value-store) kullanın.
Yazma işlemleri sınırlandırılmıştır ve her sınır bayt yerine karakterleri sayar: anahtarlar en fazla 512 karakter, tek bir değer en fazla 262.144 karakter ve her depolama alanı uygulama ve kullanıcı başına en fazla 1.048.576 karakter olabilir. Bir sınırı aşan yazma, tarayıcı API’sinde olduğu gibi bir `QuotaExceededError` fırlatır.
### 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 } from 'twenty-sdk/define';
import { useSelectedRecordIds } from 'twenty-sdk/front-component';
import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component';
import { CoreApiClient } from 'twenty-client-sdk/core';
const BulkExport = () => {
const selectedRecordIds = useSelectedRecordIds();
const handleExport = async () => {
const client = new CoreApiClient();
for (const recordId of selectedRecordIds) {
await client.mutation({
updateTask: {
__args: { id: recordId, data: { exported: true } },
id: true,
},
});
}
await enqueueSnackbar({
message: `Exported ${selectedRecordIds.length} records`,
variant: 'success',
});
await closeSidePanel();
};
return (
<div style={{ padding: '20px' }}>
<p>Export {selectedRecordIds.length} selected record(s)?</p>
<button onClick={handleExport}>Export</button>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
name: 'bulk-export',
description: 'Export selected records',
component: BulkExport,
});
```
Bunu, kayıt seçimleriyle sınırlandırılmış bir [komut menüsü öğesi](/l/tr/developers/extend/apps/layout/command-menu-items) ile sunun:
```ts src/command-menu-items/bulk-export.command-menu-item.ts
import { defineCommandMenuItem } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902',
label: 'Bulk Export',
availabilityType: 'RECORD_SELECTION',
frontComponentUniversalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901',
});
```
## Genel varlıklar
Ön uç bileşenleri, `getPublicAssetUrl` kullanarak uygulamanın `public/` dizinindeki dosyalara erişebilir:
```tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { getPublicAssetUrl } from 'twenty-sdk/utils';
const Logo = () => <img src={getPublicAssetUrl('logo.png')} alt="Logo" />;
export default defineFrontComponent({
universalIdentifier: '...',
name: 'logo',
component: Logo,
});
```
Ayrıntılar için [genel varlıklar bölümüne](/l/tr/developers/extend/apps/config/public-assets) bakın.
## Ön uç bileşenleri arasında bağımlılıkları paylaşma
Varsayılan olarak, her ön uç bileşeni içe aktardığı her bir kitaplığın kendi kopyasını paketler; bu nedenle beş bileşenli bir uygulama React'i beş kez paketler. Bu kitaplıkları tek seferde derlemek ve uygulamanın her bileşeninin onları tek bir önbelleğe alınmış dosyadan yüklemesini sağlamak için, paylaşılan bağımlılıkları uygulamanızın `package.json` dosyasında belirtin:
```json package.json
{
"frontComponentSharedDependencies": ["react", "react-dom/client", "twenty-ui/input"]
}
```
Her bileşen daha sonra bağımlılıklarını önceden olduğu gibi içe aktarır — bileşen kodunuzda hiçbir şey değişmez:
```tsx src/front-components/counter.tsx
import { useState } from 'react';
import { defineFrontComponent } from 'twenty-sdk/define';
const Counter = () => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
};
export default defineFrontComponent({
universalIdentifier: '...',
name: 'counter',
component: Counter,
});
```
Bilmeniz gereken birkaç nokta:
* **Uygulama başına tek bir paylaşılan bağımlılık paketi.** Paket, uygulamanızın kendi bağımlılıklarından oluşturulur; böylece dağıttığınız sürümler üzerinde tam denetim sizde olur.
* **İçe aktardığınız tam belirteçleri listeleyin.** `twenty-ui/input` ve `twenty-ui/display` iki ayrı girdidir; yalnızca bir paket adı alt yollarını kapsamaz. `react`i listelemek, otomatik olarak `react/jsx-runtime`ı da kapsar.
* **`react` ile birlikte `react-dom/client`ı paylaşın.** Her bileşen `createRoot` üzerinden oluşturulur; bu yüzden bunu dışarıda bırakmak, her bileşenin yine de React DOM'u paketlemesi anlamına gelir.
* **Paket önbelleğe alınır.** İçerik karma değerine sahip bir URL altında, uzun ömürlü, değişmez bir önbellekle sunulur; bu nedenle bir kez indirilir ve bağımlılıklarından biri değişene kadar uygulamanın tüm bileşenleri arasında yeniden kullanılır.
* **Paylaşılan paketlerden hiçbirini içe aktarmayan bileşenler onu asla indirmez.**
## 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'nin kendi bileşen kitaplığı; aşağıda [Twenty UI bileşenlerini kullanma](#using-twenty-ui-components) bölümüne bakın
* **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ığı**
## Twenty UI bileşenlerini kullanma
Twenty, bileşen kitaplığını [`twenty-ui`](https://www.npmjs.com/package/twenty-ui/v/1.0.0-alpha.1) paketi olarak sunar. Ön uç bileşenleri bunu, çalışma alanının açık ve koyu temasıyla otomatik olarak eşleşen düğmeler, etiketler, durum rozetleri, çipler, avatarlar, simgeler, tipografi ve tema belirteçleri için kullanabilir.
### Kurulum
Paketi, Twenty örneğinizle birlikte gelen sürüme sabitleyerek uygulamanıza ekleyin:
```bash
yarn add twenty-ui@1.0.0-alpha.1
```
`twenty-ui`, derleme zamanında ön uç bileşeninize paketlenir, bu nedenle yalnızca uygulamanızın bir bağımlılığı olması gerekir — çalışma zamanında yapılandırmanız gereken hiçbir şey yoktur.
### Bileşenleri içe aktarma
Kullandığınız bileşenlerin yalnızca paketinizde yer alması için, paket kökü yerine eşleşen alt yoldan içe aktarın:
| Alt yol | Dışa aktardıkları |
| --------------------------- | -------------------------------------------------------- |
| `twenty-ui/input` | `Button` ve form girdileri |
| `twenty-ui/data-display` | `Tag`, `Status`, `Chip`, `Avatar` ve daha fazlası |
| `twenty-ui/feedback` | `Callout`, `Banner`, `Info` ve daha fazlası |
| `twenty-ui/typography` | `H1Title`, `H2Title`, `H3Title`, `Label` ve daha fazlası |
| `twenty-ui/icon` | `Icon*` bileşenleri (ör. `IconCheck`) |
| `twenty-ui/theme-constants` | `ThemeProvider`, `themeCssVariables` |
```tsx
import { defineFrontComponent } from 'twenty-sdk/define';
import { Status, Tag } from 'twenty-ui/data-display';
import { Button } from 'twenty-ui/input';
const StyledWidget = () => {
return (
<div style={{ padding: '16px', display: 'flex', gap: '8px' }}>
<Button title="Click me" onClick={() => alert('Clicked!')} />
<Tag text="Active" color="green" />
<Status color="green" text="Online" />
</div>
);
};
export default defineFrontComponent({
universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456',
name: 'styled-widget',
component: StyledWidget,
});
```
### İkonlar
Tek tek simgeleri `twenty-ui/icon` içinden içe aktarın:
```tsx
import { IconBox, IconCheck } from 'twenty-ui/icon';
```
Her adlandırılmış simge ağaç sallama optimizasyonuna tabidir; bu nedenle birkaç tanesini içe aktarmak paket boyutunuza çok az şey ekler. `IconsProvider`, `useIcons` ve `iconsState` kullanmaktan kaçının — bunlar, birkaç MB'lık Tabler simge setinin tamamını içeri çeker.
### Temalandırma ve tema belirteçleri
Twenty UI bileşenleri, çalışma alanının açık ve koyu temasıyla otomatik olarak eşleşir — oluşturucu etkin renk şemasını barındırıcıda uygular ve bileşenler renklerini buna göre belirler.
Kendi satır içi stillerinizde aynı tasarım belirteçlerini kullanmak için `useTheme()` kancasını çağırın. Bu kanca, etkin temaya bağlı Twenty tema belirteçlerini (boşluklandırma, renkler, köşe yarıçapları, yazı tipleri) döndürür; bileşeninizde `ThemeProvider` kurulumuna gerek yoktur:
```tsx
import { useTheme } from 'twenty-ui/theme-constants';
const Card = () => {
const theme = useTheme();
return (
<div
style={{
padding: theme.spacing[4],
background: theme.background.secondary,
color: theme.font.color.primary,
}}
>
Themed card
</div>
);
};
```
`useTheme()` bir kanca olduğundan, belirteçleri bileşen gövdesinin içinde okursunuz; böylece değerler her zaman etkin temayı yansıtır. Aynı belirteç haritası, `themeCssVariables` sabiti olarak da dışa aktarılır; ancak ön uç bileşenlerinde `useTheme()` kullanmayı tercih edin — `themeCssVariables` öğesini dolaylı olarak kullanan modül düzeyinde bir sabit, uygulama manifesti çıkarılırken tanımsız olabilir.
Etkin şemaya açıkça dallanmak için, `twenty-sdk/front-component` içindeki `useColorScheme()` ile okuyun; bu kanca `'light'` veya `'dark'` döndürür.
## Mevcut sınırlamalar
Front bileşenleri aktif geliştirme aşamasındadır. Oluşturma, stillendirme, olayları işleme, öğeleri ölçme ve tarayıcı depolaması iyi çalışır. Bu sınırların *ötesine* geçen her şey (bir ref üzerinde bir DOM yöntemi çağırmak, öğe yeniden boyutlandırmalarını gözlemlemek, ağacınızın dışına portal oluşturmak) bugün eksik ya da tamamlanmamış durumda ve çoğu sessizce başarısız oluyor: ne bir istisna fırlatılıyor ne de TypeScript hatası görülüyor; çünkü iskeletin türleri tam tarayıcı DOM’una göre tanımlanmıştır.
Bunlardan biri sizi engelliyorsa, önceliklendirilmesi için [bir issue açın](https://github.com/twentyhq/twenty/issues/new/choose).
### Yerleşim ve ölçüm
Öğeler kendilerini ölçebilir: ana bilgisayar geometriyi sandbox’a yansıtır; bu nedenle okumalar yerel olarak yanıtlanır ancak bir kareye kadar eski olabilir ve hiç ölçülmemiş bir öğenin ilk okunması sıfırlar döndürür. Yazdıktan sonra, `requestAnimationFrame` geri çağrısında veya bir effect içinde yeniden okuyun.
| API | Ne olur |
| ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `getBoundingClientRect()`, `offsetWidth`, `clientWidth`, `scrollWidth`, `offsetTop`, ... | Ayna üzerinden çalışır; `scrollTop` / `scrollLeft` atamak etkisizdir |
| `getClientRects()`, `window.matchMedia()` | Fırlatır |
| `window.innerWidth`, `innerHeight`, `devicePixelRatio`, `scrollX`, `scrollY` | Çalışır ve **tarayıcı görünüm alanını** raporlar; bileşeninizin kendi boyutları `document.body.clientWidth` / `clientHeight` değerleridir |
| `window.getComputedStyle()` | Yalnızca öğenin satır içi stillerini döndürür, hiçbir zaman ana bilgisayar tarafından hesaplanan kaskadı döndürmez |
| `ResizeObserver`, `IntersectionObserver` | `ReferenceError` (`typeof` kontrolleri çalışır) |
| `MutationObserver` | Çalışır, `subtree`, `attributeFilter`, eski değerler ve `takeRecords()` dahil |
`getBoundingClientRect` ile konumlandırma artık çalışıyor, ancak boyut değişikliklerini `ResizeObserver` aracılığıyla izleyen her şey (recharts `ResponsiveContainer`, Floating UI'nin `autoUpdate`) hâlâ çalışmıyor. Yerleşim için yine de CSS'i tercih edin: stil sayfanız gerçek sayfaya erişir, bu nedenle flexbox, grid, `aspect-ratio`, `clamp()` ve `@container` gecikme olmadan normal şekilde davranır.
<Note>
`requestAnimationFrame`, `fetch`, `setTimeout` ve `queueMicrotask` `window.` öneki olmadan çalışır. Yalnızca `window.requestAnimationFrame(...)` ve benzerleri fırlatır.
</Note>
### DOM erişimi
Bir `ref` size bir `HTMLElement` değil, bir sandbox öğesi verir.
| Ne yazarsınız | Ne olur | Bunun yerine şunu kullanın |
| ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ref.current.focus()`, `.click()`, `.select()`, `.setSelectionRange()`, `.scrollIntoView()`, `video.play()` | Fırlatır | Kontrollü bileşenler; değerleri `event.target` üzerinden okuyun |
| `element.classList.add(...)` | Fırlatır (`classList` `undefined` durumundadır) | `className` dizgisini kendiniz oluşturun |
| `document.createTreeWalker()` | Fırlatır | `querySelector()` / `querySelectorAll()` ve `getElementById()` çalışır; `getElementsByClassName()` de çalışır, ancak canlı bir `HTMLCollection` yerine statik bir koleksiyon döndürür |
| `document.activeElement` | Her zaman `undefined` | Odağı `onFocus` / `onBlur` ile takip edin |
| `canvas` | Hiçbir şey render etmez, hata yoktur | SVG veya ekranda olmayan bir alanda çizim yapıp sonucu bir `img` öğesinde gösterin |
| `createPortal(node, document.body)` | `isConnected` başarı bildirmesine rağmen hiçbir şey render etmez | `position: absolute` ile satır içi yerleştirilmiş bindirmeler veya kitaplığa kendi konteyner öğenizi iletin |
Portal boşluğu, Radix, Headless UI, MUI ve react-select popover’larının varsayılan olarak hiçbir şey render etmemesinin nedenidir. Çoğu bir container prop kabul eder; onu render ettiğiniz bir öğeye yönlendirin.
### Etkinlikler
Mouse, pointer, touch, sürükleme, klavye, odak, `input`/`change`/`submit`, `scroll`/`wheel`/`contextmenu` ve `animationend`/`transitionend` host’a iletilir, ayrıca öğe başına birkaç tane daha vardır: `img` üzerinde `load`/`error`, `input`/`textarea` üzerinde pano ve bileşim olayları, `video`/`audio` üzerinde medya, `details`/`dialog` üzerinde `toggle`. Bunun dışındaki her şey (`onAuxClick`, `onSelect`, `onInvalid`, `onReset`, `onAnimationStart`, pointer capture, `img` üzerinde `onLoad`) uyarı verilmeden kaldırılır.
`document.addEventListener()` ve `window.addEventListener()` hatasız olarak kaydolur ancak asla tetiklenmez; bu yüzden bir sürükleme, işaretçi başladığı öğeden ayrılır ayrılmaz durur. `event.preventDefault()` da geçmez; form gönderimi, `dragover`/`drop` ve bağlantı tıklamaları zaten sizin için korunmuştur.
### Öznitelikler ve stillendirme
Her öğe, kendi özelliklerini host DOM’a iletir (`a` üzerinde `href`, `img` üzerinde `src`/`alt`, `input` üzerinde `value`/`placeholder`/`disabled` vb.), ayrıca her öğede ortak bir küme vardır: `id`, `className`, `style`, `title`, `tabIndex`, `role`, `draggable` ve tireli olan tüm `aria-*` / `data-*` öznitelikleri (bu yüzden `ariaLabel` düşürülür). Bunun dışındaki her şey sessizce atılır, bu yüzden özel durumu `data-*` olarak ifade edin.
Bileşen CSS’i, ister `import './styles.css'` ile, ister CSS-in-JS ile ya da bir `style` öğesinden gelsin, host sayfanın `head` kısmına **scope’suz** olarak enjekte edilir. Bu nedenle sınıf adları Twenty’nin kendi sınıf adlarıyla çakışır (onlara önek ekleyin ve asla çıplak `div { ... }` seçicileri yazmayın) ve `@media`, sizin bileşeniniz yerine tarayıcı penceresini hedefler (kendi `container-type` değerinizle `@container` kullanın). Satır içi `style` prop’ları etkilenmez.
### Depolama ve ağ
`localStorage` ve `sessionStorage` tarayıcı yerine Twenty tarafından sağlanır: bileşen, opak bir origin’de bir worker içinde çalışır, bu nedenle ana makine değerleri uygulamanız adına depolar. Kapsamları ve sınırları için [storage](#storage) bölümüne bakın. IndexedDB, çerezler, Cache API ve `BroadcastChannel` hâlâ kullanılamaz. Durumu cihazlar arasında kalıcı hale getirmek için bir [mantık fonksiyonu](/l/tr/developers/extend/apps/logic/logic-functions) çağırın ve onun [anahtar-değer deposunu](/l/tr/developers/extend/apps/logic/key-value-store) kullanın.
`fetch` çalışır, bazı sınırlamalarla:
* Twenty API’sine ve uygulamanızın rotalarına yapılan çağrılar host tarafından proxy’lenir, bu yüzden [`RestApiClient`](#calling-the-twenty-rest-api) kullanmayı tercih edin. Proxy’lenen çağrılarda `AbortSignal` ve diğer `RequestInit` seçenekleri kaldırılır ve yalnızca `string` ve `URLSearchParams` gövdeleri desteklenir.
* Diğer origin’ler sandbox’tan `Origin: null` ile çıkar, bu yüzden üçüncü taraf bir API yalnızca `Access-Control-Allow-Origin: *` gönderirse yanıt verir. Bunun yerine onu bir mantık fonksiyonundan çağırın.
* `fetch('/rest/people')` hiçbir zaman Twenty API’siyle eşleştirilmez, çünkü sandbox’un göreli yolu çözecek bir sayfa URL’si yoktur.
### Ortam yakalama
`navigator.mediaDevices.getUserMedia()` ve `MediaRecorder`, sandbox polyfill’leri aracılığıyla front bileşenleri içinde çalışır; böylece standart kayıt kodu değişmeden çalışır ve `MediaRecorder.isTypeSupported`, yaygın kapsayıcı/kodek kombinasyonları için yanıt verir. Ayrıntılı `getUserMedia` kısıtlama nesneleri kabul edilir ancak iletilmez — ana makine, istenen türler için varsayılanlarıyla yakalama yapar — ve uygulamalar genelinde aynı anda yalnızca bir yakalama etkin olabilir. Kaydedilmiş bir `Blob`’u `uploadFile` host fonksiyonu ile saklayın.
### Diğer eksikler
* **Dosya içerikleri.** `file` türünde bir `input`, handler’ınıza yalnızca dosya meta verilerini verir, baytları vermez, bu yüzden `FileReader` kullanılamaz. Kodunuzun zaten elinde bulunan bir `Blob`’u — örneğin `MediaRecorder` tarafından üretilen bir tane — yüklemek için `uploadFile` host fonksiyonunu kullanın.
* **Sürükle-bırak yükleri.** Sürükleme olayları tetiklenir, ancak `event.dataTransfer` `undefined` durumundadır.
* **Node yerleşikleri.** `fs`, `path` ve `node:crypto` build aşamasında başarısız olur; bu nedenle bu işi bir [mantık fonksiyonuna](/l/tr/developers/extend/apps/logic/logic-functions) taşıyın. Web Crypto, `fetch`, `TextEncoder` ve `URL` kullanılabilir durumdadır.
* **`iframe`** her zaman `allow-same-origin` olmadan yeniden sandbox’a alınır, bu nedenle kendi oturumuna güvenen bir yerleştirme, oturum kapalı olarak işlenir. `onLoad`’u da yoktur.

View File

@@ -0,0 +1,46 @@
---
title: Gezinme Menüsü Öğeleri
description: Çalışma alanı kenar çubuğuna özel öğeler ekleyin — kaydedilmiş görünümlere veya harici URL’lere bağlantılar.
icon: bars
---
Bir **gezinme menüsü öğesi**, sol kenar çubuğundaki bir öğedir. Özel kenar çubuğu bağlantıları sunmak için `defineNavigationMenuItem()` kullanın — genellikle sunduğunuz her [görünüm](/l/tr/developers/extend/apps/layout/views) için bir tane — veya harici URL’lere işaret etmek için 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ğlantı verdiğini belirler. Her tür, belirli bir tanımlayıcı alanıyla eşleşir:
| Tür | Ne yapar | Zorunlu alan |
| ------------------------------------ | ------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `NavigationMenuItemType.VIEW` | Kaydedilmiş bir görünümü açar | `viewUniversalIdentifier` |
| `NavigationMenuItemType.LINK` | Harici bir URL açar | `link` |
| `NavigationMenuItemType.FOLDER` | İç içe geçmiş öğeleri bir etiket altında gruplar | `name` (ve alt öğeler klasöre `folderUniversalIdentifier` aracılığıyla referans verir) |
| `NavigationMenuItemType.OBJECT` | Bir nesnenin varsayılan indeks sayfasını açar | `targetObjectUniversalIdentifier` |
| `NavigationMenuItemType.PAGE_LAYOUT` | Bağımsız bir sayfa düzeni açar | `pageLayoutUniversalIdentifier` |
* `position`, kenar çubuğundaki sıralamayı kontrol eder.
* Enum, ayrıca kullanıcı tarafından oluşturulan kayıt favorileri için dahili olarak kullanılan `NavigationMenuItemType.RECORD` değerini de içerir — bir kayda referans verecek alan bulunmadığından uygulama manifestinde kullanılamaz.
* `icon` ve `color` isteğe bağlıdır ve öğenin görünümünü özelleştirir.
* `folderUniversalIdentifier`, herhangi bir öğede de mevcuttur ve onu bir `FOLDER` türü üst öğenin içine yerleştirmek için kullanılır.
<Note>
**Yaygın hata:** gezinme menüsü öğesiyle ilişkilendirilmemiş bir nesne oluşturmak, o nesneyi kenar çubuğundan erişilemez hale getirir. Teknik/içsel bir nesne olmadığı sürece, her özel nesnenin ona işaret eden bir kenar çubuğu öğesi olmalıdır.
</Note>

View File

@@ -0,0 +1,59 @@
---
title: Genel Bakış
description: Uygulamanızı Twenty'nin kullanıcı arayüzüne yerleştirin — kenar çubuğu öğeleri, kaydedilmiş görünümler, kayıt sayfası sekmeleri ve yalıtılmış React bileşenleri.
icon: table-columns
---
Bir Twenty uygulamasının **düzen katmanı**, kullanıcının gördüğü her şeydir: uygulamanın kenar çubuğunda nerede göründüğü, hangi liste görünümlerini sunduğu, kayıt detay sayfalarının nasıl düzenlendiği ve bu sayfaların içinde hangi özel React bileşenlerinin oluşturulduğu.
```text
Sidebar Record list Record detail page
─────── ─────────── ──────────────────
[📋 My View] ────▶ ┌──────────┐ ┌─────────────────────┐
[📋 Drafts ] │ Companies│ │ Tabs: [Overview ] │
[📋 Inbox ] │ ──────── │ │ [Notes ] │
▲ │ Apple │ │ [Hello ]◀──── definePageLayoutTab
│ │ Acme │ │ │ adds a tab...
└ defineNavi- │ … │ │ ┌────────────────┐ │
gationMenu- └────▲─────┘ │ │ │ │
Item points │ │ │ React UI │◀── …with a
to a defineView │ │ │ (sandboxed in │ │ defineFrontComponent
└ defineView │ │ a Worker) │ │ widget inside
picks columns │ └────────────────┘ │
and filters └─────────────────────┘
```
## Bu bölümde
<CardGroup cols={2}>
<Card title="Görünümler" icon="list" href="/l/tr/developers/extend/apps/layout/views">
`defineView` — kaydedilmiş liste yapılandırmaları: görünür sütunlar, filtreler, gruplar.
</Card>
<Card title="Gezinme Menüsü Öğeleri" icon="bars" href="/l/tr/developers/extend/apps/layout/navigation-menu-items">
`defineNavigationMenuItem` — görünümlere veya harici URL'lere işaret eden kenar çubuğu girişleri.
</Card>
<Card title="Sayfa Düzenleri" icon="table-columns" href="/l/tr/developers/extend/apps/layout/page-layouts">
`definePageLayout` ve `definePageLayoutTab` — bir kaydın detay sayfasındaki sekmeler ve bileşenler.
</Card>
<Card title="Ön uç bileşenleri" icon="window-maximize" href="/l/tr/developers/extend/apps/layout/front-components">
`defineFrontComponent` — Twenty içinde oluşturulan yalıtılmış React bileşenleri.
</Card>
<Card title="Komut Menüsü Öğeleri" icon="terminal" href="/l/tr/developers/extend/apps/layout/command-menu-items">
`defineCommandMenuItem` — ön uç bileşenlerini Cmd+K girişleri ve hızlı eylemler olarak kaydedin.
</Card>
</CardGroup>
## Uygulamanın göründüğü yerler
| Görünüm alanı | Neyi kontrol eder | Varlık |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| **Kenar çubuğu** | Kaydedilmiş bir görünüme veya harici bir URL'ye bağlanan özel bir giriş | `defineNavigationMenuItem` |
| **Kayıt listesi** | Bir nesne için kaydedilmiş yapılandırma — görünür sütunlar, sıralama, filtreler, gruplar | `defineView` |
| **Kayıt detay sayfası** | Bir kayıt sayfasındaki sekmeler ve bileşenler (kendi nesnenize ait veya standart bir nesneye ait) | `definePageLayout`, `definePageLayoutTab` |
| **Yukarıdakilerin herhangi birinin içinde** | Özel bir React bileşeni — butonlar, formlar, panolar, entegrasyonlar | `defineFrontComponent` |
| **Uygulama ayarları** | Varsayılan değişkenler arayüzü yerine, uygulamanın Ayarlar sekmesinde özel bir yapılandırma bölümü | `defineSettingsFrontComponent` |
| **Komut menüsü (Cmd+K)** | Sabitlenmiş bir hızlı eylem veya gizli komut | `defineCommandMenuItem` |
Ön uç bileşenleri, Remote DOM kullanan yalıtılmış bir Web Worker içinde çalışır — sayfada *yerel* olarak oluşturulurlar (bir iframe içinde değil), ancak ana makine sayfasına veya DOM'a doğrudan erişemezler. Twenty ile iletişim, mesaj iletimi yapan bir ana makine API'si aracılığıyla gerçekleşir.
Hâlâ etkin geliştirme aşamasındalar: sandbox kısmi bir DOM uygular, bu nedenle gelişmiş kullanımlar başarısız olabilir. [Mevcut sınırlamalara](/l/tr/developers/extend/apps/layout/front-components#current-limitations) bakın.

View File

@@ -0,0 +1,158 @@
---
title: Sayfa Düzenleri
description: definePageLayout ve definePageLayoutTab kullanarak kayıt ayrıntı sayfalarını — sekmeleri, widget'ları ve ön uç bileşenlerinin nerede oluşturulacağını — özelleştirin.
icon: table-columns
---
Bir sayfa düzeni, bir kaydın ayrıntı sayfasının nasıl düzenleneceğini kontrol eder: hangi sekmelerin görüneceği ve bunların hangi widget'ları içereceği. Sahibi olduğunuz bir nesne için bir düzen tanımlamak amacıyla `definePageLayout()` kullanın veya halihazırda var olan bir düzene (size ait veya standart bir Twenty düzenine) tek bir sekme eklemek için `definePageLayoutTab()` kullanın.
| Kullanım alanı | Varlık |
| ------------------------------------------------------------------------------ | --------------------- |
| Sahibi olduğunuz bir nesnedeki bir kayıt sayfası için tüm düzeni tanımlayın | `definePageLayout` |
| Var olan bir düzene bir sekme ekleyin (kendi nesneniz veya standart bir nesne) | `definePageLayoutTab` |
## definePageLayout
Tüm ayrıntı sayfasına sahip olduğunuz durumlarda bunu kullanın — genellikle kendiniz tanımladığınız özel bir nesne için.
```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.VERTICAL_LIST,
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`, `'RECORD_INDEX'`, `'RECORD_PAGE'`, `'DASHBOARD'` veya `'STANDALONE_PAGE'` değerlerinden biridir. Belirli bir nesnenin ayrıntı görünümünü özelleştirmek için `'RECORD_PAGE'` kullanın.
* `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: kayıt sayfaları ve bağımsız sayfalar için `VERTICAL_LIST`, panolar için `GRID` ve sekmenin görüntü alanını doldurması gereken tek bir widget için `CANVAS`. Bir `VERTICAL_LIST` sekmesi widget'ları dikey olarak sıralar. Zaman çizelgeleri, dosyalar, notlar, görevler ve iş akışları gibi kendi kaydırmalarını yöneten yerleşik widget'lar tek bir görüntü alanını doldurur; alanlar, ön bileşenler, grafikler ve içeriğe sığan diğer widget'lar ise içeriklerinde veya yapılandırılmış yüksekliklerinde oluşturulur. Bir `GRID` sekmesi, widget'larını her zaman 12 sütunlu bir ızgarada kartlar olarak düzenler. Bir `CANVAS` widget'ının açıkça belirtilmiş bir konumu yoktur; bir canvas sekmesi birden fazla widget içeriyorsa, görüntü alanını doldurmak yerine içerik yüksekliklerinde işlenirler.
* `layoutMode` değerini açıkça ayarlayın. Bunu atlamak, bir `STANDALONE_PAGE` üzerinde size `VERTICAL_LIST` ve diğer her yerde `GRID` verir; bu da, bir kayıt sayfasında nadiren istediğiniz davranıştır.
* Bir sekmenin içindeki her `widget`, bir [ön uç bileşeni](/l/tr/developers/extend/apps/layout/front-components), bir ilişki listesi veya diğer yerleşik widget türlerini oluşturabilir.
* Bir `FRONT_COMPONENT` pencere öğesi, `headerCommandMenuItemUniversalIdentifiers` özelliğini aynı uygulamadaki komut menüsü öğelerinin evrensel tanımlayıcılarından oluşan sıralı bir diziye ayarlayabilir. Bu eylemler, pencere öğesi kart başlığında simge düğmeleri olarak görünür ve komut düzeyindeki kullanılabilirliklerini ve izin denetimlerini korur. Tanımlayıcılar benzersiz olmalı ve uygulama yüklendiğinde çözümlenebilmelidir.
* 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).
### Alan bileşenleri
Bir `FIELD` bileşeni, kaydın tek bir alanını görüntüler. İlişki alanları için, ilişkili kayıtların bir listesini de gömebilir:
```ts
{
universalIdentifier: 'c1c2c3c4-c5c6-4000-8000-000000000003',
title: 'People → Opportunities',
type: 'FIELD',
configuration: {
configurationType: 'FIELD',
fieldMetadataId: PEOPLE_FIELD_UNIVERSAL_IDENTIFIER,
fieldDisplayMode: 'TABLE',
nestedRelationFieldMetadataId: OPPORTUNITIES_FIELD_UNIVERSAL_IDENTIFIER,
},
}
```
* `fieldMetadataId`, yerleşimin nesnesindeki bir alanın evrensel tanımlayıcısını alır.
* `fieldDisplayMode`, `'FIELD'`, `'CARD'`, `'EDITOR'`, `'VIEW'` veya `'TABLE'` değerlerinden biridir. `TABLE`, bire-çok ilişki alanının kayıtlarını listeleyen bir görünümü gömer.
* `nestedRelationFieldMetadataId` isteğe bağlıdır ve ilişki hedef nesnesi üzerinde, iki ilişki adımı uzaktaki kayıtları listelemek için bire-çok ilişki alanının evrensel tanımlayıcısını alır (örneğin, bir Şirket sayfasının şirketin kişilerinin fırsatlarını listelemesi veya bir Kişi sayfasının kişinin şirketinin fırsatlarını listelemesi gibi). İlk adım bire-çok veya çoktan-bire ilişki alanı olabilir, ikincisi mutlaka bire-çok olmalıdır (bağlantı ilişkileri desteklenmez) ve `fieldDisplayMode: 'TABLE'` gerektirir — bunu başka herhangi bir görüntüleme modu ile birleştirmek bir doğrulama hatasıdır, çünkü iç içe bir bileşen her zaman gömülü bir görünüm olarak işlenir.
## definePageLayoutTab
Bunu yalnızca mevcut bir düzene **sekme eklemek** istediğinizde kullanın — örneğin, standart Company sayfasında bir analitik sekmesi veya kendi nesnenizin düzenine eklenmiş bir yapay zeka özeti sekmesi.
```ts src/page-layouts/example-extra-tab.ts
import {
definePageLayoutTab,
PageLayoutTabLayoutMode,
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
export default definePageLayoutTab({
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
pageLayoutUniversalIdentifier:
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage
.universalIdentifier,
title: 'Hello World',
position: 1000,
icon: 'IconWorld',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
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` **zorunludur** ve kurulum anında zaten var olan bir sayfa düzenini işaret etmelidir — standart bir Twenty düzeni veya kendi uygulamanız tarafından tanımlanan bir düzen olabilir. Başka yüklü bir uygulamaya ait düzenlere uygulamalar arası referanslar bugün desteklenmemektedir. Üst düzen eksik olduğunda, kurulum net bir doğrulama hatasıyla başarısız olur.
* Standart Twenty yerleşimleri için tanımlayıcıları `twenty-sdk/define` içinden içe aktarın:
```ts
import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier
// …
```
Her yerleşim girdisi ayrıca `tabs` ve bunların `widgets` ögelerini de açığa çıkarır, böylece herhangi bir düzeye başvurabilirsiniz:
```ts
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home
.universalIdentifier;
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets
.fields.universalIdentifier;
```
Kısa bir takma ad `STANDARD_PAGE_LAYOUT` da kullanılabilir:
```ts
import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define';
STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier;
```
* `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](/l/tr/developers/extend/apps/layout/front-components), 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üzene sahip olduğunuzda `definePageLayout` kullanın.

View File

@@ -0,0 +1,113 @@
---
title: Görünümler
description: Uygulamanızdaki nesneler için önceden yapılandırılmış kaydedilmiş görünümleri — sütun sırası, filtreler, gruplar — sunun.
icon: list
---
Bir **görünüm**, bir nesnenin kayıtlarının nasıl görüntüleneceğine ilişkin kaydedilmiş bir yapılandırmadır: hangi alanların görüneceği, sıraları, görünür olup olmadıkları ve uygulanan tüm filtreler veya gruplar. Uygulamanızla önceden yapılandırılmış görünümler sunmak için `defineView()` kullanın.
Her nesne, sunucu tarafından otomatik olarak sağlanan kendi ana liste görünümünü (bir `OBJECT` gezinme öğesinin açtığı "All ..." tablosu) alır — buna, uygulamanızın oluşturduğu nesneler de dahildir; uygulamanızın bildirdiği her alan için bir sütunla birlikte. `defineView()` ile tanımladığınız görünümler, bu varsayılan görünümün üzerine eklenen her zaman **ek** görünümlerdir.
```ts src/views/example-view.ts
import { defineView } 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',
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. Bu, tanımladığınız özel bir nesne veya standart bir Twenty nesnesi olabilir.
* Nesnenin ana liste görünümü sunucuya aittir: `key` kullanımdan kaldırılmıştır ve yok sayılır, bu nedenle bir manifest görünümü bunu asla talep edemez. Görünümünüzün kenar çubuğunda tek tık uzağınızda olmasını istiyorsanız bir `VIEW` gezinme öğesi ekleyin.
* `fields`, hangi sütunların görüneceğini ve hangi sırayla görüneceğini kontrol eder. Her alan bir `fieldMetadataUniversalIdentifier` öğesine referans verir. Otomatik oluşturulan `createdAt` gibi bir sistem alanına başvurmak için bkz. [Sistem Metaverilerini Hedefleme](/l/tr/developers/extend/apps/data/system-fields).
* Daha gelişmiş yapılandırmalar için `filters`, `filterGroups`, `sorts`, `groups` ve `fieldGroups` da tanımlayabilirsiniz.
* `position`, aynı nesne için birden fazla görünüm olduğunda sıralamayı kontrol eder.
## İsteğe bağlı özellikler
| Özellik | Değerler | Açıklama |
| ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | `ViewType.TABLE` (varsayılan), `ViewType.LIST`, `ViewType.KANBAN`, `ViewType.CALENDAR` | Kayıtların nasıl düzenlendiği. (`FIELDS_WIDGET`, `TABLE_WIDGET`, `KANBAN_WIDGET`, `LIST_WIDGET` ve `CALENDAR_WIDGET` da mevcuttur ancak sayfa düzeni bileşenleri tarafından dahili olarak kullanılır.) |
| `visibility` | `ViewVisibility.WORKSPACE` (varsayılan), `ViewVisibility.UNLISTED` | Görünümün tüm çalışma alanı için listelenip listelenmediği veya seçicilerden gizlenip gizlenmediği. |
| `openRecordIn` | kullanımdan kaldırıldı | Artık okunmuyor: Kayıtların nerede açılacağı artık [object](/l/tr/developers/extend/apps/data/objects) (`defineObject()` içindeki `openRecordIn`) özelliğiyle belirlenir; ayar yoksa her üyenin kendi tercihi esas alınır. |
| `sorts` | `{ fieldMetadataUniversalIdentifier, direction: ViewSortDirection.ASC \| DESC }[]` | Varsayılan sıralama düzeni. |
| `isCompact` | `boolean` | Kompakt satır görünümü. |
| `mainGroupByFieldMetadataUniversalIdentifier` + `shouldHideEmptyGroups` | — | Kayıtları bir alana göre gruplayın (ör. kanban sütunları). |
| `kanbanAggregateOperation`, `kanbanAggregateOperationFieldMetadataUniversalIdentifier`, `kanbanColumnWidth` | `AggregateOperations.*` | Kanban sütunu toplamları ve boyutlandırması. |
| `calendarLayout`, `calendarFieldMetadataUniversalIdentifier`, `calendarEndFieldMetadataUniversalIdentifier` | `ViewCalendarLayout.DAY` / `WEEK` / `MONTH` | Takvim görünümleri: düzen, kayıtları konumlandıran tarih alanı ve isteğe bağlı bir bitiş tarihi alanı. |
Yukarıdaki tüm enum'lar `twenty-sdk/define` içinden dışa aktarılır.
## Filtreler
Bir görünüm, önceden uygulanmış filtrelerle gelebilir. Her filtrenin üç koordinatı vardır: filtrelenen **alan**, **işleç** (nasıl karşılaştırılacağı) ve **değer** (neyle karşılaştırılacağı). Üçünün de hizalı olması gerekir — bir alan türüne uygulanmayan bir işlecin kullanılması, senkronizasyon sırasında reddedilir.
```ts
import { ViewFilterOperand } from 'twenty-sdk/define';
filters: [
{
universalIdentifier: '...',
fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER,
operand: ViewFilterOperand.IS,
value: ['ACTIVE'],
},
],
```
### Alan türüne göre desteklenen işleçler
| Alan tipi | Desteklenen işleçler |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `BOOLEAN` | `IS` |
| `UUID` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` |
| `TS_VECTOR` | `VECTOR_SEARCH` |
> Benzer adlara sahip alan türleri tamamen farklı işleçler kullanabilir — buna `SELECT` ve `MULTI_SELECT` yaygın bir örnektir.
### Her işleç için değer biçimi
`value` alanı her zaman JSON olarak serileştirilebilir bir değerdir, ancak beklenen biçim işlece bağlıdır:
| İşleç ailesi | Değer biçimi | Örnek |
| ------------------------------------------------------------- | ------------------------------------- | ------------------------ |
| `SELECT` üzerinde `IS`, `IS_NOT` | seçenek anahtarları dizisi (metinler) | `['ACTIVE', 'PENDING']` |
| `MULTI_SELECT` üzerinde `CONTAINS`, `DOES_NOT_CONTAIN` | seçenek anahtarları dizisi (metinler) | `['TAG_A']` |
| `RELATION` üzerinde `IS`, `IS_NOT` | kayıt kimlikleri dizisi (uuid'ler) | `['c5a1...']` |
| metin benzeri alanlar üzerinde `CONTAINS`, `DOES_NOT_CONTAIN` | metin | `'acme'` |
| `NUMBER` üzerinde `IS`, `IS_NOT` | metin (değer) | `'5'` |
| `RATING` / `UUID` üzerinde `IS` | metin (değer) | `'5'` |
| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | metin (sınır) | `'10'` |
| `DATE` / `DATE_TIME` üzerinde `IS`, `IS_BEFORE`, `IS_AFTER` | ISO 8601 dizesi | `'2025-01-01T00:00:00Z'` |
| `IS_EMPTY`, `IS_NOT_EMPTY` | boş dize | `''` |
| `BOOLEAN` üzerinde `IS` | `'true'` veya `'false'` | `'true'` |
## Görünümlerin kullanıcı arayüzünde görüntülenme şekli
Tek başına bir görünüm, kenar çubuğundan erişilebilir değildir. Orada görünmesini sağlamak için, türü `VIEW` olan ve görünümün `universalIdentifier` değerini işaret eden bir [navigasyon menüsü öğesi](/l/tr/developers/extend/apps/layout/navigation-menu-items) ile eşleştirin. Bu, kanonik desendir: Her özel nesne, oluşturma sırasında otomatik olarak oluşturulan varsayılan bir indeks görünümü alır; dolayısıyla yalnızca onu açan kenar çubuğu öğesini eklemeniz yeterlidir.