i18n - docs translations (#22617)

Created by Github action

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22617?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
github-actions[bot]
2026-07-07 11:54:54 +02:00
committed by GitHub
parent 07a921f8ca
commit 18ca89bcdd
97 changed files with 13247 additions and 0 deletions
@@ -0,0 +1,70 @@
---
title: 5. Bir yapay zekâ ajanı
icon: robot
description: Bir ajanın, sizin aracınızı kullanarak bir sohbetten belgeler oluşturmasına izin verin.
---
`generate-document` bir **araç** olarak sunulduğu için, bir yapay zekâ ajanı onu çağırabilir.
Kullanıcıların yalnızca *"Jeffery Griffin için bir teklif hazırla"* diyebilmesi için bir ajan ve bir beceri ekleyelim.
## Beceri
Bir [skill](/l/tr/developers/extend/apps/logic/skills-and-agents), ajanlara eklediğiniz bilgi — yeniden kullanılabilir talimatlardır. Bizimki modele aracı nasıl kullanacağını öğretir.
```ts filename="src/skills/document-drafting.skill.ts"
import { defineSkill } from 'twenty-sdk/define';
export default defineSkill({
universalIdentifier: DOCUMENT_SKILL_UNIVERSAL_IDENTIFIER,
name: 'document-drafting',
label: 'Document drafting',
icon: 'IconFileText',
content: [
'To generate a document, call the `generate-document` tool with:',
'- `templateId`: the id of the document template to use.',
'- `recordId`: the id of the Person or Company the document is for.',
'',
'If the user names a template or person instead of an id, find the record first,',
'then pass its id. Make sure the template target matches the record type.',
].join('\n'),
});
```
## Ajan
Bir [ajan](/l/tr/developers/extend/apps/logic/skills-and-agents), bir istemi bir modelle eşleştirir. Bir derleme uyarısından kaçınmak için `responseFormat` değerini açıkça ayarlayın.
```ts filename="src/agents/document-assistant.agent.ts"
import { defineAgent } from 'twenty-sdk/define';
export default defineAgent({
universalIdentifier: DOCUMENT_AGENT_UNIVERSAL_IDENTIFIER,
name: 'document-assistant',
label: 'Document Assistant',
description: 'Generates documents from your templates and CRM records.',
icon: 'IconFileText',
responseFormat: { type: 'text' },
prompt: [
'You are the Document Assistant for a CRM.',
'You help users generate personalized documents from reusable templates',
'and the data already in their CRM. Use the generate-document tool, and',
'always confirm what you created.',
].join(' '),
});
```
<Note>
Ajan yalnızca rolü izin veriyorsa aracı çağırabilir. Uygulamanın rolünde `canAccessAllTools: true` ve `canBeAssignedToAgents: true` değerlerini [Bölüm 2](/l/tr/developers/extend/apps/tutorials/document-generator/generating-documents#grant-it-access) içinde zaten ayarladık.
</Note>
## Deneyin
**Document Assistant** ile bir sohbet açın ve CRMinizdeki bir kişi için bir belge taslağı hazırlamasını isteyin. Kaydı bulur, `generate-document` çağrısını yapar ve oluşturduğu belgeyi size bildirir — bu belge artık **Documents** görünümünüzde, komut menüsü ve iş akışı yollarındakiyle tam olarak aynı şekilde görünür.
Mantığı bir araç olarak açığa çıkarmanın getirisi budur: **tek fonksiyon, birçok ön kapı** — komut menüsü, HTTP, iş akışı adımı ve şimdi de doğal dil.
**Bu adımdan sonra:** uygulama özellik açısından tamamlanmış ve gerçekten kullanışlıdır. Artık onu yayımlama zamanı.
<Card title="Sonraki: yayımlama →" icon="rocket" href="/l/tr/developers/extend/apps/tutorials/document-generator/publishing">
Pazar yeri meta verilerini ekleyin ve yayımlayın.
</Card>
@@ -0,0 +1,277 @@
---
title: 4. UI'yi oluşturma
icon: table-columns
description: Görünümler, kenar çubuğu gezinmesi, bir komut ve ön uç bileşenleri.
---
Şu anda nesnelere yalnızca Settings üzerinden erişilebiliyor. Uygulamaya UI'de gerçek bir varlık kazandıralım: liste görünümleri, kenar çubuğu girişleri, tek tıkla **Generate document** komutu, bir belgeyi **önizlemek** için kayıt sayfası ön uç bileşeni ve şablonlar için yerel zengin metin **editor** sekmesi.
## Görünümler ve gezinme
Bir [görünüm](/l/tr/developers/extend/apps/layout/views), belirli bir nesnenin kaydedilmiş bir listesidir.
Bir [gezinme menüsü öğesi](/l/tr/developers/extend/apps/layout/navigation-menu-items)
bu görünümü kenar çubuğuna yerleştirir.
```ts filename="src/views/documents.view.ts"
import { defineView, ViewKey } from 'twenty-sdk/define';
export default defineView({
universalIdentifier: DOCUMENTS_VIEW_UNIVERSAL_IDENTIFIER,
name: 'All documents',
objectUniversalIdentifier: DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
icon: 'IconFile',
key: ViewKey.INDEX,
position: 0,
fields: [
{ universalIdentifier: DOCUMENTS_VIEW_NAME_FIELD_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: DOCUMENT_NAME_FIELD_UNIVERSAL_IDENTIFIER,
position: 0, isVisible: true, size: 280 },
{ universalIdentifier: DOCUMENTS_VIEW_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: DOCUMENT_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
position: 1, isVisible: true, size: 120 },
{ universalIdentifier: DOCUMENTS_VIEW_TEMPLATE_FIELD_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: DOCUMENT_TEMPLATE_FIELD_UNIVERSAL_IDENTIFIER,
position: 2, isVisible: true, size: 200 },
],
});
```
```ts filename="src/navigation-menu-items/documents.navigation-menu-item.ts"
import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define';
export default defineNavigationMenuItem({
universalIdentifier: DOCUMENTS_NAVIGATION_MENU_ITEM_UNIVERSAL_IDENTIFIER,
name: 'Documents',
icon: 'IconFile',
color: 'green',
position: 1,
type: NavigationMenuItemType.VIEW,
viewUniversalIdentifier: DOCUMENTS_VIEW_UNIVERSAL_IDENTIFIER,
});
```
Aynı çifti şablonlar için de ekleyin. İkisi de artık kenar çubuğunda görünüyor:
<Frame caption="Kenar çubuğunda Documents ve Templates, oluşturulmuş belge listelenmiş şekilde.">
<img src="/images/docs/developers/extends/apps/document-generator/04-documents-view.png" alt="Oluşturulmuş belgeli Documents görünümü" />
</Frame>
## Bir ön uç bileşeni
Bir [ön uç bileşeni](/l/tr/developers/extend/apps/layout/front-components), Twenty içinde izole edilmiş bir React bileşenidir. Bizimki seçili kaydı okur, kişi şablonlarını `CoreApiClient` aracılığıyla yükler ve bir önceki bölümdeki rotaya POST isteği gönderir.
```tsx filename="src/front-components/generate-document-form.front-component.tsx"
import { useEffect, useState } from 'react';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { defineFrontComponent } from 'twenty-sdk/define';
import { enqueueSnackbar, useSelectedRecordIds } from 'twenty-sdk/front-component';
const GenerateDocumentForm = () => {
const selectedRecordIds = useSelectedRecordIds();
const recordId = selectedRecordIds.length === 1 ? selectedRecordIds[0] : null;
const [templates, setTemplates] = useState<{ id: string; name: string }[]>([]);
const [templateId, setTemplateId] = useState('');
useEffect(() => {
new CoreApiClient()
.query({ documentTemplates: {
__args: { filter: { target: { eq: 'PERSON' } }, first: 100 },
edges: { node: { id: true, name: true } } } })
.then(({ documentTemplates }) => {
const list = documentTemplates?.edges?.map((e) => e.node) ?? [];
setTemplates(list);
if (list[0]) setTemplateId(list[0].id);
});
}, []);
const generate = async () => {
const apiBaseUrl = process.env.TWENTY_API_URL;
const token = process.env.TWENTY_APP_ACCESS_TOKEN ?? process.env.TWENTY_API_KEY;
const res = await fetch(`${apiBaseUrl}/s/documents/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ templateId, recordId }),
}).then((r) => r.json());
await enqueueSnackbar({
message: res.success ? 'Document generated.' : 'Generation failed.',
variant: res.success ? 'success' : 'error',
});
};
// ...render a <select> of templates and a Generate button
};
export default defineFrontComponent({
universalIdentifier: GENERATE_DOCUMENT_FORM_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
name: 'generate-document-form',
component: GenerateDocumentForm,
});
```
<Warning>
Değerleri `twenty-ui` içe aktarmak yerine satır içi CSS değişkenleriyle (`var(--t-color-blue)`) stillendirin. SDK, derleme sırasında bu paketi taklit eder, bu nedenle tema sabitlerinin modül düzeyindeki içe aktarımları `undefined` olur. Tam bileşene bakın:
[full component](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/front-components/generate-document-form.front-component.tsx).
</Warning>
## Onu açacak bir komut
Bir [komut menüsü öğesi](/l/tr/developers/extend/apps/layout/command-menu-items) `availabilityType: 'RECORD_SELECTION'` ile, bir Person seçildiğinde görünür ve bileşeni yan panelde açar.
```ts filename="src/command-menu-items/generate-document.command-menu-item.ts"
import { defineCommandMenuItem, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
export default defineCommandMenuItem({
universalIdentifier: GENERATE_DOCUMENT_COMMAND_UNIVERSAL_IDENTIFIER,
label: 'Generate document',
availabilityType: 'RECORD_SELECTION',
availabilityObjectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
frontComponentUniversalIdentifier:
GENERATE_DOCUMENT_FORM_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
});
```
## Tüm akışı deneyin
**People**'ı açın, bir kişiyi işaretleyin ve <kbd>⌘K</kbd> / <kbd>Ctrl K</kbd> tuşlarına basın.
"Generate document" komutu, uygulamanızla etiketlenmiş şekilde görünür:
<Frame caption="Bir Person seçildiğinde komut görünür.">
<img src="/images/docs/developers/extends/apps/document-generator/06-command-menu.png" alt="Generate document ile komut menüsü" />
</Frame>
Çalıştırın — bileşeniniz yan panelde açılır. Bir şablon seçin, **Generate**'a tıklayın ve **Documents** içinde yeni bir kayıt oluşsun.
<Frame caption="Ön uç bileşeni, şablonları yüklüyor ve tıklamayla oluşturuyor.">
<img src="/images/docs/developers/extends/apps/document-generator/06b-front-component.png" alt="Generate document yan paneli" />
</Frame>
Oluşturulan her belge, uygulamanızı yazarı olarak kaydeder:
<Frame caption="Document Generator tarafından oluşturuldu, durum Generated.">
<img src="/images/docs/developers/extends/apps/document-generator/05-document-record.png" alt="Oluşturulmuş bir belge kaydı" />
</Frame>
## Bir belgenin kayıt sayfasında önizleme
Bir ön uç bileşeni sadece komut menüleri için değildir — onu bir **kayıt sayfasında sekme** olarak da bağlayabilirsiniz. Belge kaydına, Markdown gövdesini şık, yazdırılabilir bir sayfa olarak işleyen bir *Preview* sekmesi ekleyelim.
Bileşen, çalışma bağlamından geçerli kayıt kimliğini okur, belgeyi yükler ve işler. Ön uç bileşenleri, yalnızca belirli bir HTML etiketleri beyaz listesine izin veren bir **sandbox** içinde çalışır — ham HTML enjeksiyonu (`dangerouslySetInnerHTML`) ve `\<style>` engellenir — bu nedenle Markdown'ı, satır içi stillerle, küçük bir [`Markdown`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/utils/markdown-to-react.tsx) yardımcısı aracılığıyla React öğeleri olarak işliyoruz.
```tsx filename="src/front-components/document-viewer.front-component.tsx"
import { CoreApiClient } from 'twenty-client-sdk/core';
import { defineFrontComponent } from 'twenty-sdk/define';
import { useFrontComponentExecutionContext } from 'twenty-sdk/front-component';
import { Markdown } from 'src/utils/markdown-to-react';
const DocumentViewer = () => {
const recordId = useFrontComponentExecutionContext((c) => c.recordId ?? null);
// ...load { content, file } for recordId, then derive the links:
const pdfUrl = document.file?.[0]?.url;
const webUrl = `${process.env.TWENTY_API_URL ?? ''}/s/documents/view?id=${recordId}`;
// Render the template body, plus quick links to the web page and the PDF.
// Links open in a new tab so they don't navigate the embedded component.
return (
<div style={styles.scroll}>
<div style={styles.actions}>
<a style={styles.actionLink} href={webUrl} target="_blank" rel="noopener noreferrer">
Open web page
</a>
{pdfUrl ? (
<a style={styles.actionLink} href={pdfUrl} target="_blank" rel="noopener noreferrer">
Download PDF
</a>
) : null}
</div>
<div style={styles.paper}>
<div style={styles.body}>
<Markdown content={document.content} />
</div>
</div>
</div>
);
};
export default defineFrontComponent({
universalIdentifier: DOCUMENT_VIEWER_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
name: 'document-viewer',
component: DocumentViewer,
});
```
Bunu bir [sayfa düzeni](/l/tr/developers/extend/apps/layout/page-layouts) ile bağlayın. Bir
`RECORD_PAGE` düzeni, bir nesnenin kayıt görünümüne sekmeler ekler; `CANVAS` sekmesindeki bir `FRONT_COMPONENT` widget'ı bileşeni barındırır:
```ts filename="src/page-layouts/document-record.page-layout.ts"
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
export default definePageLayout({
universalIdentifier: DOCUMENT_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER,
name: 'Document record page',
type: 'RECORD_PAGE',
objectUniversalIdentifier: DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
tabs: [{
universalIdentifier: DOCUMENT_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER,
title: 'Preview',
icon: 'IconEye',
position: 50,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
widgets: [{
universalIdentifier: DOCUMENT_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER,
title: 'Document preview',
type: 'FRONT_COMPONENT',
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier: DOCUMENT_VIEWER_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
},
}],
}],
});
```
Herhangi bir belgeyi açın — **Preview** sekmesi, belgeyi paylaşılabilir web sayfasına ve PDF'e bağlantılarla birlikte, güzel bir şekilde işler:
<Frame caption="Preview sekmesi, belgeyi satır içi stillerle ve hızlı bağlantılarla işler.">
<img src="/images/docs/developers/extends/apps/document-generator/09-document-viewer.png" alt="Kayıt sayfası sekmesinde belge görüntüleyici ön uç bileşeni" />
</Frame>
## Zengin metin editörüyle bir şablonu düzenleme
Şablonların özel bir bileşene hiç ihtiyacı yoktur. `body` bir
`RICH_TEXT` alanı olduğu için, Twenty bunun için zaten tam özellikli bir zengin metin editörü sunar — standart Note ve Task nesnelerinin kullandığı editörün aynısı. Biz sadece onu şablon kayıt sayfasında görünür hale getiriyoruz.
`FIELD` widget'lı, `EDITOR` görüntü modunda ve `fieldMetadataId` aracılığıyla `body` alanını işaret eden bir sekme ekleyin:
```ts filename="src/page-layouts/template-record.page-layout.ts"
{
universalIdentifier: TEMPLATE_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER,
title: 'Template',
position: 1,
layoutMode: PageLayoutTabLayoutMode.GRID,
widgets: [{
universalIdentifier: TEMPLATE_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER,
title: 'Template',
type: 'FIELD',
gridPosition: { row: 0, column: 0, rowSpan: 6, columnSpan: 12 },
configuration: {
configurationType: 'FIELD',
fieldMetadataId: TEMPLATE_BODY_FIELD_UNIVERSAL_IDENTIFIER,
fieldDisplayMode: 'EDITOR',
},
}],
}
```
Bir `RICH_TEXT` alanı, hem editörün blok JSON'unu hem de bir Markdown izdüşümünü saklar. Oluşturma hattı bu Markdown izdüşümünü okur, böylece yer tutucular, PDF ve paylaşılabilir web sayfası hiç değişmeden çalışmaya devam eder — tam [`template-record.page-layout.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/page-layouts/template-record.page-layout.ts) dosyasına bakın.
Artık editörler, şablonları düzgün bir zengin metin editöründe yazıyor:
<Frame caption="Template sekmesi: Twenty'nin yerel zengin metin editörü, body alanına bağlı.">
<img src="/images/docs/developers/extends/apps/document-generator/10-template-editor.png" alt="Yerel zengin metin editörü sekmesine sahip şablon kaydı" />
</Frame>
**Bu adımdan sonra:** belgeler harika bir şekilde önizlenir ve şablonlar uygulama içinde düzenlenebilir. Sırada, bir AI temsilcisinin bunları bir sohbetten oluşturmasını sağlamak var.
<Card title="Sırada: bir AI temsilcisi →" icon="robot" href="/l/tr/developers/extend/apps/tutorials/document-generator/ai-agent">
Aracınızı çağıran bir temsilci ve bir yetenek ekleyin.
</Card>
@@ -0,0 +1,132 @@
---
title: 1. Veri modeli
icon: database
description: Belgeleri ve şablonları nesneler, alanlar ve bir ilişki ile modelleyin.
---
Uygulamamızın iki özel nesneye ihtiyacı var: **belge şablonları** (ne yazılacağını belirten) ve
**belgeler** (oluşturulan sonuç). Bunları tanımlayalım.
CLI ile her varlık dosyasını iskelet olarak oluşturun — sizin için geçerli bir UUID ve doğru klasörü oluşturur:
```bash filename="Terminal"
yarn twenty dev:add object
```
Aşağıda tamamlanmış dosyaları gösteriyoruz.
<Note>
Her `*_UNIVERSAL_IDENTIFIER` sabiti
`src/constants/universal-identifiers.ts` içinde bulunur ve kullanıldığı yerde içe aktarılır. Aşağıdaki kod parçacıkları
kısalık için bu içe aktarmaları atlar — kendi dosyalarınızda bunları eklemeyi unutmayın.
</Note>
## Şablon nesnesi
Bir şablonun bir `name` alanı, `{{placeholders}}` içeren bir `body` alanı ve bunun bir Person mı yoksa bir Company için mi yazıldığını belirten bir `target` alanı vardır. `body` bir
`RICH_TEXT` alanıdır, bu nedenle Twenty ona tam özellikli bir zengin metin düzenleyicisi verir.
```ts filename="src/objects/document-template.object.ts"
import { defineObject, FieldType } from 'twenty-sdk/define';
export default defineObject({
universalIdentifier: DOCUMENT_TEMPLATE_OBJECT_UNIVERSAL_IDENTIFIER,
nameSingular: 'documentTemplate',
namePlural: 'documentTemplates',
labelSingular: 'Document template',
labelPlural: 'Document templates',
icon: 'IconFileText',
labelIdentifierFieldMetadataUniversalIdentifier:
TEMPLATE_NAME_FIELD_UNIVERSAL_IDENTIFIER,
fields: [
{ universalIdentifier: TEMPLATE_NAME_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.TEXT, name: 'name', label: 'Name', icon: 'IconAbc' },
{ universalIdentifier: TEMPLATE_BODY_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.RICH_TEXT, name: 'body', label: 'Body', icon: 'IconFileText',
description: 'Use {{placeholders}} like {{name.firstName}} or {{jobTitle}}.' },
{ universalIdentifier: TEMPLATE_TARGET_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.SELECT, name: 'target', label: 'Target', icon: 'IconTarget',
defaultValue: `'PERSON'`,
options: [
{ id: TEMPLATE_TARGET_OPTION_PERSON_UNIVERSAL_IDENTIFIER,
value: 'PERSON', label: 'Person', color: 'blue', position: 0 },
{ id: TEMPLATE_TARGET_OPTION_COMPANY_UNIVERSAL_IDENTIFIER,
value: 'COMPANY', label: 'Company', color: 'green', position: 1 },
] },
],
});
```
<Warning>
`SELECT` seçenek **değerleri** `UPPER_CASE` (`PERSON`, `person` değil) olmalıdır ve
`defaultValue` fazladan tırnak içine alınır: `` `'PERSON'` ``. `label`,
kullanıcıların gördüğü şeydir.
</Warning>
## Belge nesnesi
Oluşturulan belge, işlenmiş `content` ve bir `status` saklar. Bunu
yine aynı şekilde tanımlayın; `status` için `DRAFT` / `GENERATED` seçenekli bir select alanı kullanın. Tam dosya:
[`document.object.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/objects/document.object.ts).
## Bir ilişkiyle bunları birbirine bağlama
Her belge, geldiği şablonu işaret etmelidir. İlişkiler
**çift yönlüdür** — her iki tarafı da tanımlarsınız ve her birini kendi alan dosyasında belirtirsiniz.
```ts filename="src/fields/document-template-relation.field.ts"
import { defineField, FieldType, OnDeleteAction, RelationType } from 'twenty-sdk/define';
// The "many" side: each document belongs to one template.
export default defineField({
universalIdentifier: DOCUMENT_TEMPLATE_FIELD_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'template',
label: 'Template',
relationTargetObjectMetadataUniversalIdentifier:
DOCUMENT_TEMPLATE_OBJECT_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier:
TEMPLATE_DOCUMENTS_FIELD_UNIVERSAL_IDENTIFIER,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'templateId',
},
});
```
Diğer taraf (`template-documents-relation.field.ts`),
ters yönde işaret eden ve adı `documents` olan bir `RelationType.ONE_TO_MANY` alanıdır.
Tam desen için [Relations](/l/tr/developers/extend/apps/data/relations) bölümüne bakın.
## Bunu Twenty'de görün
`yarn twenty dev` çalışırken **Settings → Data model** bölümünü açın. Her iki nesne de
uygulamanızla etiketlenmiş olarak görünür.
<Frame caption="Her iki özel nesne, Document Generator uygulamasına aittir.">
<img src="/images/docs/developers/extends/apps/document-generator/01-data-model.png" alt="Veri modeli ayarları, Documents ve Document templates nesnelerini gösteriyor" />
</Frame>
Test etmek için bir şablon oluşturun — adını *Sales proposal* koyun, **Target** alanını
*Person* olarak ayarlayın ve birkaç yer tutucu içeren bir gövde yapıştırın:
```text
Dear {{name.firstName}} {{name.lastName}},
As {{jobTitle}} at {{company.name}}, we think you'll love our product.
Best,
The Team
```
<Frame caption="Bir şablon kaydı. Gövde, bir belge oluşturulana kadar yer tutucularını korur.">
<img src="/images/docs/developers/extends/apps/document-generator/03-template-record.png" alt="Yer tutucu gövdesine sahip bir Sales proposal şablon kaydı" />
</Frame>
**Bu adımdan sonra:** bir ilişkiyle birbirine bağlı `documentTemplate` ve `document` nesnelerine ve bunlardan belge oluşturmak için bir şablona sahipsiniz. Sırada, onu dolduran mantık var.
<Card title="Sıradaki: belgeleri oluşturma →" icon="bolt" href="/l/tr/developers/extend/apps/tutorials/document-generator/generating-documents">
Şablonu dolduran mantık fonksiyonunu yazın.
</Card>
@@ -0,0 +1,208 @@
---
title: 2. Belgeleri oluşturma
icon: bolt
description: Bir mantık fonksiyonu, bir yapay zekâ aracı ve bir iş akışı eylemi olarak sunulur.
---
Şimdi asıl kısma geçelim: bir şablon ve bir kaydı yükleyen, yer tutucuları dolduran ve yeni bir belge kaydeden bir [mantık fonksiyonu](/l/tr/developers/extend/apps/logic/logic-functions).
İş mantığını **handler** olarak bir kez yazacağız, sonra bunu birkaç tetikleyici üzerinden kullanıma sunacağız. Bu bölüm bunlardan ikisini birbirine bağlıyor — bir **AI aracı** ve bir **iş akışı eylemi**.
## Oluşturma yardımcısı
Saf mantığı kendi dosyasında tutun ki birim testlerini yazmak kolay olsun. Bu, bir kaydı `{{dot.path}}` belirteçlerine düzleştirir ve bunların yerine değerlerini koyar.
```ts filename="src/logic-functions/utils/render-template.ts"
const PLACEHOLDER_PATTERN = /\{\{\s*([\w.]+)\s*\}\}/g;
export const renderTemplate = (body: string, values: Record<string, string>) => {
const missingTokens = new Set<string>();
const content = body.replace(PLACEHOLDER_PATTERN, (_m, token: string) => {
const value = values[token];
if (value === undefined || value === '') { missingTokens.add(token); return ''; }
return value;
});
return { content, missingTokens: [...missingTokens] };
};
```
<Tip>
Bu dosyanın yan etkisi olmadığından, onu hızlı birim testleriyle kapsayabilirsiniz (`yarn test:unit`). Bkz. [Testing](/l/tr/developers/extend/apps/operations/testing).
</Tip>
## Handler
Handler, CRM verilerini okumak ve yazmak için oluşturulmuş [`CoreApiClient`](/l/tr/developers/extend/apps/logic/logic-functions) kullanır. Şablonu yükler, hedef kaydı yükler, gövdeyi doldurur ve bir `document` oluşturur.
```ts filename="src/logic-functions/handlers/generate-document-handler.ts"
import { CoreApiClient } from 'twenty-client-sdk/core';
import { loadRecordValues } from 'src/logic-functions/utils/load-record-values';
import { renderTemplate } from 'src/logic-functions/utils/render-template';
export const generateDocumentHandler = async (
input: { templateId: string; recordId: string },
) => {
const client = new CoreApiClient();
// Use a filtered list query, not the singular lookup: the singular query
// throws when nothing matches, which would become a 500 instead of a 404.
const { documentTemplates } = await client.query({
documentTemplates: {
__args: { filter: { id: { eq: input.templateId } }, first: 1 },
edges: { node: { id: true, name: true, body: true, target: true } },
},
});
const documentTemplate = documentTemplates?.edges?.[0]?.node;
if (!documentTemplate?.id) return { success: false, status: 404, message: 'Template not found.' };
const record = await loadRecordValues(client, documentTemplate.target, input.recordId);
if (!record.found) return { success: false, status: 404, message: 'Record not found.' };
const { content, missingTokens } = renderTemplate(documentTemplate.body ?? '', record.values);
const { createDocument } = await client.mutation({
createDocument: {
__args: { data: {
name: `${documentTemplate.name} — ${record.displayName}`,
content, status: 'GENERATED', templateId: documentTemplate.id,
} },
id: true, name: true,
},
});
return { success: true, documentId: createDocument.id, content, missingTokens };
};
```
`loadRecordValues`, Kişi ile Şirket için farklı bir sorgu çalıştırır ve sonucu düzleştirir — bkz.
[`load-record-values.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/logic-functions/utils/load-record-values.ts).
## Bunu bir araç ve bir iş akışı eylemi olarak kullanıma sunma
Tek bir `defineLogicFunction` birden çok tetikleyici barındırabilir. Burada, `toolTriggerSettings` onu AI aracılarının çağırabileceği hâle getirir ve `workflowActionTriggerSettings` onu görsel iş akışı oluşturucusundaki bir adıma dönüştürür. Her ikisi de girdilerini bir JSON şemasıyla tanımlar.
```ts filename="src/logic-functions/generate-document.ts"
import { defineLogicFunction } from 'twenty-sdk/define';
import { jsonSchemaToInputSchema } from 'twenty-sdk/logic-function';
import { GENERATE_DOCUMENT_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
import { generateDocumentHandler } from 'src/logic-functions/handlers/generate-document-handler';
import { generateDocumentInputSchema } from 'src/logic-functions/schemas/generate-document-input.schema';
export default defineLogicFunction({
universalIdentifier: GENERATE_DOCUMENT_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
name: 'generate-document',
description: 'Generate a document from a template and a CRM record.',
timeoutSeconds: 30,
toolTriggerSettings: {
inputSchema: generateDocumentInputSchema,
},
workflowActionTriggerSettings: {
label: 'Generate Document',
icon: 'IconFileText',
inputSchema: jsonSchemaToInputSchema(generateDocumentInputSchema),
outputSchema: [{ type: 'object', properties: {
success: { type: 'boolean' }, documentId: { type: 'string' },
} }],
},
handler: generateDocumentHandler,
});
```
Girdi şeması, `templateId` ve `recordId` değerlerini tanımlayan basit bir JSON şemasıdır — bkz. [`generate-document-input.schema.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/logic-functions/schemas/generate-document-input.schema.ts).
## Erişim verin
Mantık fonksiyonları, uygulamanın rolüyle çalışır. Şablonları ve kayıtları okuması ve belgeler oluşturması gerektiğinden, `src/roles/default-role.ts` içinde buna izin verin:
```ts
export default defineApplicationRole({
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Document Generator default role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canAccessAllTools: true,
canBeAssignedToAgents: true,
permissionFlagUniversalIdentifiers: [SystemPermissionFlag.UPLOAD_FILE],
});
```
`UPLOAD_FILE`, bir sonraki bölümde fonksiyonun oluşturulan PDF'yi yüklemesine olanak tanır.
Daha ayrıntılı izinler için bkz. [Roles](/l/tr/developers/extend/apps/config/roles).
## Gerçek bir PDF dosyası ekleyin
Oluşturulmuş bir metin alanı kullanışlıdır, ancak kullanıcılar gerçek bir belge ister. Bir **PDF** oluşturalım ve onu kayıt üzerinde indirilebilir bir dosya olarak saklayalım.
Önce, PDF'yi tutması için `document` nesnesine bir `FILES` alanı verin. Uygulamalar **kendi** dosya alanlarına yükleme yapar, dolayısıyla yüklemenin yönlendirildiği yer bu alandır:
```ts filename="src/objects/document.object.ts"
{
universalIdentifier: DOCUMENT_FILE_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.FILES,
name: 'file',
label: 'File',
icon: 'IconFileTypePdf',
universalSettings: { maxNumberOfValues: 1 },
}
```
Şimdi o PDF'yi oluşturun. Bir uygulama gerçek bir Node projesidir, bu yüzden ihtiyaç duyduğunuz herhangi bir npm paketini ekleyebilir ve onu başka yerlerde olduğu gibi içe aktarabilirsiniz. Biz, PDF'yi çizmek için **[pdf-lib](https://pdf-lib.js.org/)** ve Markdown gövdesini ayrıştırmak için **[marked](https://marked.js.org/)** kullanıyoruz — CLI bunları fonksiyonun çalışma zamanına sizin için kurar:
```bash filename="Terminal"
yarn add pdf-lib marked
```
Tam yardımcı işlev şuradadır: [`generate-document-pdf.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/logic-functions/utils/generate-document-pdf.ts).
Bu yardımcı, Markdown'ı `marked.lexer` ile belirteçlere ayrıştırır, ardından onları pdf-lib ile yerleştirir: gerçek başlıklar, **kalın**/*italik* kısımlar, madde ve numaralı listeler, alıntı blokları ve çizgiler — şablonun kendisinin, sadece bir metin yığını yerine, cilalı, çok sayfalı A4 formatında bir oluşturması.
<Frame caption="Oluşturulan PDF: gerçek tipografi ve Markdown biçimlendirmesiyle şablon gövdesinin oluşturulması.">
<img src="/images/docs/developers/extends/apps/document-generator/07b-generated-pdf.png" alt="Cilalı, pazarlanabilir oluşturulmuş bir PDF" />
</Frame>
<Note>
pdf-lib'in yerleşik yazı tipleri WinAnsi kodlaması kullanır, bu nedenle Batı Avrupa aksanları kutudan çıktığı gibi oluşturulur; yardımcı, akıllı tırnak işaretlerini ve tireleri eşler ve kodlayamadığı karakterleri atar. Latin olmayan yazı sistemlerini (Çince, Arapça, Kiril) oluşturmak, bir Unicode yazı tipi gömmek anlamına gelir.
</Note>
Sonra onu yükleyin ve başvurusunu kayıt üzerinde saklayın. `uploadFile`, baytları uygulamaya ait dosya alanınıza yönlendirir; döndürülen `id`, kaydettiğiniz değerdir:
```ts filename="src/logic-functions/handlers/generate-document-handler.ts"
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
import { generateDocumentPdf } from 'src/logic-functions/utils/generate-document-pdf';
const documentName = `${documentTemplate.name} — ${record.displayName}`;
const bytes = await generateDocumentPdf(documentName, content);
const fileName = 'proposal.pdf';
const uploaded = await new MetadataApiClient().uploadFile(
Buffer.from(bytes),
fileName,
'application/pdf',
DOCUMENT_FILE_FIELD_UNIVERSAL_IDENTIFIER,
);
await client.mutation({
updateDocument: {
__args: {
id: documentId,
data: { file: [{ fileId: uploaded.id, label: fileName }] },
},
id: true,
},
});
```
Artık oluşturulan belge indirilebilir bir PDF taşıyor:
<Frame caption="Oluşturulan PDF, belgenin Dosya alanında saklanır.">
<img src="/images/docs/developers/extends/apps/document-generator/08-document-with-pdf.png" alt="Oluşturulmuş PDF dosyasına sahip bir belge kaydı" />
</Frame>
<Note>
`uploadFile` yalnızca **uygulamaya ait** dosya alanlarını hedefler (bu nedenle yüklemeler her zaman alanın sahibi olan bir uygulama ve ayrıca `UPLOAD_FILE` rol bayrağı gerektirir). Bu nedenle PDF, kaydın kendi `file` alanına düşer — [call-recorder app](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/public/call-recorder) uygulamasının kayıtlar için kullandığıyla aynı desen.
</Note>
**Bu adımdan sonra:** her oluşturulan belgenin gerçek, indirilebilir bir PDF'si vardır. Ancak henüz hiçbir şey oluşturucuyu arayüzden *çağır*amıyor — bunun için bir HTTP rotasına ihtiyacımız var.
<Card title="Sırada: HTTP rotaları →" icon="küre" href="/l/tr/developers/extend/apps/tutorials/document-generator/http-routes">
Fonksiyonu HTTP üzerinden sunun ve belgeleri web sayfaları olarak oluşturun.
</Card>
@@ -0,0 +1,135 @@
---
title: 3. HTTP rotaları
icon: globe
description: İşlevi HTTP üzerinden tetikleyin ve belgeleri web sayfaları olarak oluşturun.
---
Aynı işleyici HTTP isteklerine de yanıt verebilir. İki rota ekleyeceğiz:
* belge oluşturmak için UI’ın çağırdığı bir **POST** uç noktası ve
* belgeyi yazdırılabilir bir web sayfası olarak oluşturan herkese açık bir **GET** uç noktası.
Her ikisi de `httpRouteTriggerSettings` kullanır. Uygulama rotaları Twenty sunucunuzda `/s` altında sunulur (ör. `http://localhost:2020/s/documents/generate`).
## POST rotası — isteğe bağlı oluşturma
Bu, `generateDocumentHandler`’ı yeniden kullanır, bu nedenle tekrarlanacak bir mantık yoktur — yalnızca istek gövdesini okuyan ince bir adaptör vardır.
```ts filename="src/logic-functions/generate-document-route.ts"
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';
import { Response } from 'twenty-sdk/logic-function';
import { generateDocumentHandler } from 'src/logic-functions/handlers/generate-document-handler';
const handler = async (event: RoutePayload): Promise<Response> => {
const body = event.body as Record<string, unknown> | null;
const result = await generateDocumentHandler({
templateId: (body?.templateId as string) ?? '',
recordId: (body?.recordId as string) ?? '',
});
// Map the handler's failure reason onto a real HTTP status (400/404/500)
// instead of always returning 200.
return new Response(JSON.stringify(result), {
status: result.success ? 200 : (result.status ?? 400),
headers: { 'Content-Type': 'application/json' },
});
};
export default defineLogicFunction({
universalIdentifier: GENERATE_DOCUMENT_ROUTE_UNIVERSAL_IDENTIFIER,
name: 'generate-document-route',
timeoutSeconds: 30,
handler,
httpRouteTriggerSettings: {
path: '/documents/generate',
httpMethod: 'POST',
isAuthRequired: true,
},
});
```
Paylaşılan işleyici, hata durumunda önerilen bir `status` döndürür; böylece rota uygun bir `4xx`/`5xx` koduyla yanıt verebilir. `isAuthRequired: true`, çağıranın geçerli bir belirteç sunması gerektiği anlamına gelir — bir sonraki bölümdeki ön bileşen, kullanıcının erişim belirtecini otomatik olarak iletir.
## GET rotası — web sayfası olarak oluşturma
JSON yerine HTML döndürmek için gövdeyi `Content-Type` başlığıyla birlikte bir `Response` içine alın. Bu rota herkese açıktır (`isAuthRequired: false`), böylece oluşturulan bir belge bağlantı olarak paylaşılabilir.
```ts filename="src/logic-functions/view-document.ts"
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';
import { Response } from 'twenty-sdk/logic-function';
import { CoreApiClient } from 'twenty-client-sdk/core';
import { documentHtmlPage } from 'src/utils/render-document';
const htmlResponse = (html: string, status = 200): Response =>
new Response(html, { status, headers: { 'Content-Type': 'text/html; charset=utf-8' } });
const handler = async (event: RoutePayload): Promise<Response> => {
const documentId = event.queryStringParameters?.id;
if (!documentId) {
return htmlResponse(documentHtmlPage('Missing document id', 'Provide ?id=<documentId>.'), 400);
}
// Filtered list query so an unknown id renders a clean 404 page instead of throwing.
const { documents } = await new CoreApiClient().query({
documents: {
__args: { filter: { id: { eq: documentId } }, first: 1 },
edges: { node: { id: true, name: true, content: true } },
},
});
const document = documents?.edges?.[0]?.node;
if (!document?.id) {
return htmlResponse(documentHtmlPage('Document not found', `No document with id ${documentId}.`), 404);
}
return htmlResponse(documentHtmlPage(document.name ?? 'Document', document.content ?? ''));
};
export default defineLogicFunction({
universalIdentifier: VIEW_DOCUMENT_ROUTE_UNIVERSAL_IDENTIFIER,
name: 'view-document',
timeoutSeconds: 15,
handler,
httpRouteTriggerSettings: {
path: '/documents/view',
httpMethod: 'GET',
isAuthRequired: false,
},
});
```
`documentHtmlPage`, Markdown gövdesini HTMLye dönüştürür ([marked](https://marked.js.org/) ile, temizlenmiş) ve onu yalnızca şablon içeriğini gösteren, temiz, yazdırılabilir bir sayfaya yerleştirir — PDF ve uygulama içi önizleme ile aynı görünüme sahiptir.
[Yardımcıyı inceleyin](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/utils/render-document.ts).
## Deneyin
Çalışma alanınızda bir şablon ve bir Kişi ile rotayı çağırın (**Settings → APIs & Webhooks** bölümünden bir belirteç alın):
```bash filename="Terminal"
curl -X POST http://localhost:2020/s/documents/generate \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"templateId":"<templateId>","recordId":"<personId>"}'
# → {"success":true,"documentId":"...","content":"Dear Jeffery Griffin, ..."}
```
Döndürülen belgeyi tarayıcınızda açın:
```
http://localhost:2020/s/documents/view?id=<documentId>
```
<Frame caption="Herkese açık GET rotası, belgeyi yazdırılabilir bir sayfa olarak oluşturur.">
<img src="/images/docs/developers/extends/apps/document-generator/07-rendered-document.png" alt="Oluşturulmuş bir belge web sayfası" />
</Frame>
<Tip>
Ayrıca test ederken bir işlevin günlüklerini `yarn twenty dev:function:logs` ile gerçek zamanlı izleyebilir veya `yarn twenty dev:function:exec` ile doğrudan çağırabilirsiniz.
</Tip>
**Bu adımdan sonra:** uygulama HTTP üzerinden belgeler oluşturabilir ve bunları web sayfaları olarak sunabilir. Şimdi bunu `curl` olmadan kullanılabilir hale getirelim.
<Card title="Sıradaki: arayüzü oluşturma →" icon="table-columns" href="/l/tr/developers/extend/apps/tutorials/document-generator/building-the-ui">
Görünümler, gezinme, komut ve bir ön bileşen.
</Card>
@@ -0,0 +1,61 @@
---
title: "Eğitim: Belge Oluşturucu"
icon: wand-magic-sparkles
description: CRM verilerinizden kişiselleştirilmiş belgeler oluşturan gerçek bir Twenty uygulaması geliştirin.
---
Bu eğitimde, yeniden kullanılabilir şablonları CRMinizde zaten bulunan verileri kullanarak kişiselleştirilmiş belgelere dönüştüren bir uygulama olan **Belge Oluşturucu**yu geliştireceksiniz.
`{{placeholders}}` ile bir kez şablon yazın, sonra komut menüsünden, bir yapay zeka aracısından veya bir iş akışından tek tıklamayla herhangi bir Kişi veya Şirket için doldurulmuş bir belge oluşturun.
<Frame caption="Belirli bir kişi için oluşturulmuş, yazdırılabilir bir sayfa olarak açılan bir şablon.">
<img src="/images/docs/developers/extends/apps/document-generator/07-rendered-document.png" alt="Oluşturulmuş bir Satış teklifi belgesi" />
</Frame>
## Neler öğreneceksiniz
Her bölüm bir yetenek ekler. Sonunda SDKnin çoğuna dokunmuş olacaksınız.
| Bölüm | Yetenek | Başvuru |
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| [1. Veri modeli](/l/tr/developers/extend/apps/tutorials/document-generator/data-model) | Nesneler, alanlar ve bir ilişki | [Veri](/l/tr/developers/extend/apps/data/overview) |
| [2. Belgeler oluşturma](/l/tr/developers/extend/apps/tutorials/document-generator/generating-documents) | Bir Markdown şablonunu dolduran ve cilalı bir PDF ekleyen mantık işlevi (Yapay zekâ aracı + iş akışı eylemi) | [Mantık işlevleri](/l/tr/developers/extend/apps/logic/logic-functions) |
| [3. HTTP rotaları](/l/tr/developers/extend/apps/tutorials/document-generator/http-routes) | Rotalardan JSON ve paylaşılabilir bir HTML sayfası sunma | [Mantık işlevleri](/l/tr/developers/extend/apps/logic/logic-functions) |
| [4. Kullanıcı arayüzünü oluşturma](/l/tr/developers/extend/apps/tutorials/document-generator/building-the-ui) | Görünümler, gezinme, komut menüsü ve bir belgeyi önizleyen ve bir şablonu düzenleyen ön bileşenler | [Düzen](/l/tr/developers/extend/apps/layout/overview) |
| [5. Bir yapay zekâ aracısı](/l/tr/developers/extend/apps/tutorials/document-generator/ai-agent) | Aracı + beceri | [Beceriler ve aracılar](/l/tr/developers/extend/apps/logic/skills-and-agents) |
| [6. Yayınlama](/l/tr/developers/extend/apps/tutorials/document-generator/publishing) | Onu pazaryerine gönderin | [Yayınlama](/l/tr/developers/extend/apps/operations/publishing) |
## Ön Gereksinimler
[Hızlı Başlangıç](/l/tr/developers/extend/apps/getting-started/quick-start) bölümünü tamamlamış olmalısınız:
`2020` portunda çalışan yerel bir Twenty sunucusu ve ona kimlik doğrulaması yapılmış CLI.
Değilse, şimdi bir tane çatısını oluşturup başlatın:
```bash filename="Terminal"
npx create-twenty-app@latest document-generator
cd document-generator
yarn twenty dev
```
<Note>
Bitmiş kodu okumayı mı tercih edersiniz? Tam uygulama şurada bulunur:
[`packages/twenty-apps/examples/document-generator`](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/document-generator).
Aşağıdaki her kod parçası oradan kopyalanmıştır.
</Note>
## Uygulamanın nasıl bir araya geldiği
<Frame>
<img src="/images/docs/developers/extends/apps/document-generator/how-it-fits.svg" alt="Yer tutucular içeren bir şablon, komut menüsünden, bir yapay zekâ aracısından, bir iş akışından veya paylaşılabilir bir bağlantıdan tetiklenerek PDFli cilalı bir belgeye dönüştürülür" />
</Frame>
`{{placeholders}}` ile zengin metin düzenleyicide bir **şablonu** bir kez yazarsınız. Bir şablon ve bir CRM kaydı seçmek, yer tutucuları doldurur ve cilalı bir **belgeyi** (PDF dosyasıyla birlikte) depolar. Geri kalan her şey — komut menüsü, yapay zekâ aracısı, iş akışı adımı, paylaşılabilir bağlantı — o tek oluşturucuyu tetiklemenin sadece farklı bir yoludur.
## Bu döngüyü çalışır durumda tutun
Tüm eğitim boyunca bir terminalde `yarn twenty dev` komutunu çalışır durumda bırakın. `src/` altında her dosya eklediğinizde veya düzenlediğinizde, birkaç saniye içinde sunucunuzla yeniden eşitlenir; böylece siz oluştururken her yeteneğin kullanıcı arayüzünde nasıl ortaya çıktığını izleyebilirsiniz.
<Card title="Oluşturmaya başlayın →" icon="database" href="/l/tr/developers/extend/apps/tutorials/document-generator/data-model">
1. Bölüm: belgeleri ve şablonları modelleyin.
</Card>
@@ -0,0 +1,126 @@
---
title: 6. Yayımlama
icon: rocket
description: Pazaryeri meta verilerini ekleyin ve uygulamanızı yayımlayın.
---
Uygulamanız çalışıyor. Son adım, onu pazaryeri için tanımlamak ve yayımlamaktır.
## Pazaryeri meta verilerini ekleyin
[Application config](/l/tr/developers/extend/apps/config/application), pazaryerinde görünen kimliği taşır: yazar, kategori, logo ve destek bağlantıları. `public/` içine bir logo yerleştirin ve `logoUrl` ile referans verin.
```ts filename="src/application-config.ts"
import { defineApplication } from 'twenty-sdk/define';
export default defineApplication({
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
displayName: 'Document Generator',
description:
'Create reusable document templates and generate personalized documents from your CRM records.',
logoUrl: 'public/document-generator.svg',
author: 'Twenty',
category: 'Productivity',
websiteUrl: 'https://docs.twenty.com/l/tr/developers/extend/apps',
termsUrl: 'https://www.twenty.com/terms',
emailSupport: 'contact@twenty.com',
issueReportUrl: 'https://github.com/twentyhq/twenty/issues',
});
```
<Tip>
Varsayılan rol, kendi dosyasında `defineApplicationRole()` ile tanımlanır — artık burada `defaultRoleUniversalIdentifier` iletmiyorsunuz.
</Tip>
Ayrıca uygulamanın bulunabilir olması için `package.json` dosyasına `twenty-app` anahtar sözcüğünü ekleyin:
```json filename="package.json"
{ "keywords": ["twenty-app"] }
```
## Galeri ekran görüntüleri ekleyin
Bir pazaryeri listesi, kendini ekran görüntüleriyle satar. Birkaç PNG dosyasını `public/gallery/` içine bırakın ve bunlara `screenshots` ile referans verin — listeleme sayfasında bir galeri olarak görüntülenirler.
```ts filename="src/application-config.ts"
export default defineApplication({
// ...identity from above
screenshots: [
'public/gallery/01-generated-document.png',
'public/gallery/02-command-menu.png',
'public/gallery/03-template-editor.png',
'public/gallery/04-documents.png',
],
});
```
<Tip>
Kazançla başlayın: ilk ekran görüntüsünü bitmiş sonuç (oluşturulmuş bir belge) yapın, ardından nasıl tetiklendiğini ve hazırlandığını gösterin. Net, yüksek çözünürlüklü görüntüler kullanın — bunlar bir kullanıcının gördüğü ilk şeydir.
</Tip>
`README.md` dosyasına da aynı özeni gösterin — npm ve GitHub üzerindeki ön sayfadır.
Değer önerisi ve bir ekran görüntüsüyle başlayın, öne çıkan özellikleri listeleyin ve derleme ayrıntılarını sayfanın alt kısmında tutun.
## Yayımlamadan önce kontrol edin
CI ile aynı denetimleri çalıştırın:
```bash filename="Terminal"
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
yarn twenty dev --once --dry-run # preview the metadata diff
```
Taslak çalıştırma, sunucuda neyin değişeceğini uygulamadan, tam olarak yazdırır — iyi bir son akıl sağlığı kontrolüdür. Bkz.
[Testing](/l/tr/developers/extend/apps/operations/testing) ve
[Syncing & recovery](/l/tr/developers/extend/apps/operations/sync-and-recovery).
## Yayımla
```bash filename="Terminal"
# Public app → npm (default)
yarn twenty app:publish
# Or deploy privately to a specific server's registry
yarn twenty app:publish --private -r <remote>
```
`app:publish` varsayılan olarak oluşturur ve npm'e yayımlar; `--private` bunun yerine bir tarball dosyasını bir Twenty sunucusunun özel kaydına yükler. Yayımlanmış bir uygulamayı bir örneğin pazaryerinde görünür kılmak için, bir katalog eşitlemesini tetikleyin:
```bash filename="Terminal"
yarn twenty dev:catalog-sync -r <remote>
```
Tüm ayrıntılar ve yayımlama kontrol listesi:
[Publishing](/l/tr/developers/extend/apps/operations/publishing).
## Bir uygulama geliştirdiniz 🎉
Altı bölümde SDK yüzeyinin büyük kısmını kullandınız:
* Verileri modellemek için **nesneler, alanlar ve bir ilişki**
* Bir **mantık fonksiyonu**nun **AI aracı**, bir **iş akışı eylemi** ve **HTTP yolları** olarak sunulması
* Arayüz için **görünümler, gezinme, bir komut ve bir ön bileşen**
* Doğal dil üretimi için bir **ajan + beceri**
* **Pazaryeri meta verileri** ve yayımlama akışı
Bitmiş uygulama şurada bulunur:
[`packages/twenty-apps/examples/document-generator`](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/document-generator).
## Sırada nereye gidebilirsiniz
<CardGroup cols={2}>
<Card title="Veri başvurusu" icon="database" href="/l/tr/developers/extend/apps/data/overview">
Her alan türü, ilişki ve indeks seçeneği.
</Card>
<Card title="Mantık başvurusu" icon="bolt" href="/l/tr/developers/extend/apps/logic/overview">
Cron ve veritabanı olayı tetikleyicileri, anahtar-değer deposu, OAuth bağlantıları.
</Card>
<Card title="Düzen başvurusu" icon="table-columns" href="/l/tr/developers/extend/apps/layout/overview">
Sayfa düzenleri, pano bileşenleri ve daha fazla arayüz yüzeyi.
</Card>
<Card title="İşlemler" icon="rocket" href="/l/tr/developers/extend/apps/operations/overview">
CLI, test, uzak depolar ve CI.
</Card>
</CardGroup>