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,81 @@
---
title: 5. An AI agent
icon: robot
description: Nechte agenta generovat dokumenty z chatu, pomocí vašeho nástroje.
---
Protože je `generate-document` vystaven jako **nástroj**, agent AI ho může zavolat.
Přidejme agenta a dovednosti, aby uživatelé mohli říci *"vygenerovat návrh
Jeffery Griffin"*.
## Dovednost
[skill](/l/cs/developers/extend/apps/logic/skills-and-agents) je opakovaně použitelné
znalosti, které připojujete k agentům. Náš model učí jak použít
nástroj.
```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'),
});
```
## Zástupce
[agent](/l/cs/developers/extend/apps/logic/skills-and-agents) spáruje výzvu s modelem
. Nastavte `responseFormat` explicitně, abyste se vyhnuli varování sestavení.
```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>
Zástupce může zavolat nástroj pouze v případě, že jej jeho role umožňuje. Již jsme nastavili
`canAccessAllTools: true` a `canBeAssignedToAgents: true` na roli aplikace v
[Chapter 2](/l/cs/developers/extend/apps/tutorials/document-generator/generating-documents#grant-it-access).
</Note>
## Vyzkoušejte
Otevřete chat s **asistentem dokumentu** a požádejte jej, aby připravil dokument pro osobu
ve vašem CRM. Najde záznam, volání `generate-document`, a nahlásí
dokument, který vytvořil který se nyní zobrazí ve tvém **dokumentu** zobrazení,
přesně se líbí příkazové menu a cesty workflow.
To je výsledek odhalování logiky jako nástroje: **jedna funkce, mnoho předních dveří** —
příkaz menu, HTTP, krok workflow a nyní přirozený jazyk.
**Po tomto kroku:** je aplikace kompletní a opravdu užitečná. Čas do
lodi.
<Card title="Další: publikování →" icon="rocket" href="/l/cs/developers/extend/apps/tutorials/document-generator/publishing">
Přidat metadata a publikovat tržiště.
</Card>
@@ -0,0 +1,305 @@
---
title: 4. Budování uživatelského rozhraní
icon: table-columns
description: Zobrazení, postranní navigace, příkaz a přední komponenty.
---
Právě teď jsou objekty dosažitelné pouze v nastavení. Dejme aplikaci
skutečnou přítomnost v UI: seznam zobrazení, položek postranního panelu, jedním kliknutím
**Generovat příkaz** dokumentu, přední část rekordové stránky do **náhledu** dokumentu
a nativní text **editor** pro šablony.
## Zobrazení a navigace
[view](/l/cs/developers/extend/apps/layout/views) je uložený seznam daného objektu.
[Položka navigačního menu](/l/cs/developers/extend/apps/layout/navigation-menu-items)
umístí toto zobrazení do postranního panelu.
```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,
});
```
Přidejte stejný pár šablon. Oba se nyní zobrazí v postranním panelu:
<Frame caption="Dokumenty a šablony v postranním panelu s vygenerovaným dokumentem.">
<img src="/images/docs/developers/extends/apps/document-generator/04-documents-view.png" alt="Zobrazení dokumentů s vygenerovaným dokumentem" />
</Frame>
## Přední část
[přední komponenta](/l/cs/developers/extend/apps/layout/front-components) je komponenta React
boxovaná uvnitř dvaceti. Náš přečte vybraný záznam, načte
šablony osob přes `CoreApiClient`, a POSTE na trasu z poslední kapitoly
.
```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>
Styl s inline CSS proměnnými (`var(--t-color-blue)`), ne hodnoty importované z
`twenty-ui`. SDK mocniny, které balí během sestavení, takže importy
téma na úrovni modulů by byly `nedefinované`. Podívejte se na
[plnou komponentu](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/front-components/generate-document-form.front-component.tsx).
</Warning>
## Příkaz k otevření
[položka příkazu](/l/cs/developers/extend/apps/layout/command-menu-items) s
`availabilityType: 'RECORD_SELECTION'` se zobrazí při výběru osoby, a
otevře komponentu v postranním panelu.
```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,
});
```
## Vyzkoušejte celý tok
Otevřete **People**, zaškrtněte osobu a stiskněte <kbd>⌘K</kbd> / <kbd>Ctrl K</kbd>.
"Generovat doklad" se zobrazí, označeno vaší aplikací:
<Frame caption="Příkaz se zobrazí při výběru osoby.">
<img src="/images/docs/developers/extends/apps/document-generator/06-command-menu.png" alt="Příkazové menu s generováním dokumentu" />
</Frame>
Spusťte - komponenta se otevře v postranním panelu. Vyberte si šablonu, klikněte na
**Generovat**, a nový záznam země v **dokumentech**.
<Frame caption="Přední komponenta, načítání šablon a generování kliknutím.">
<img src="/images/docs/developers/extends/apps/document-generator/06b-front-component.png" alt="Generovat boční panel dokumentu" />
</Frame>
Každý vygenerovaný dokument zaznamenává vaši aplikaci jako autora:
<Frame caption="Vytvořeno generátorem dokumentů, stav vygenerován.">
<img src="/images/docs/developers/extends/apps/document-generator/05-document-record.png" alt="Generovaný záznam dokladu" />
</Frame>
## Náhled dokumentu na jeho stránce s záznamem
Přední komponenta není pouze pro menu příkazů — jeden můžete připojit jako \*\*kartu na stránce
záznamu \*\*. Přidejme kartu \*Náhled \* k záznamu dokumentu, která vykreslí
Markdown tělo jako leštěnou, vytisknutelnou stránku.
Komponenta čte aktuální id záznamu z kontextového kontextu, načte
dokument a vykreslí jej. Přední komponenty běží v **sandboxu**, který povoluje pouze
bílou listinu HTML tagů — syrovou injekci HTML (`dangerouslySetInnerHTML`) a
`\<style>` jsou blokovány — takže Markdown vykreslujeme jako React prvky s inline
pomocí malého [`Markdown`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/utils/markdown-to-react.tsx)
helper.
```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,
});
```
Připojit ji s [page layout](/l/cs/developers/extend/apps/layout/page-layouts). Rozložení
`RECORD_PAGE` přidává záložky do zobrazení záznamu objektu; widget `FRONT_COMPONENT`
v záložce `CANVAS` hostí komponentu:
```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,
},
}],
}],
});
```
Otevřete jakýkoliv dokument - záložka \*\*Náhled \*\* jej krásně vykreslí s odkazy na
sdílitelnou webovou stránku a PDF:
<Frame caption="Karta Náhled vykresluje dokument s vloženými styly, plus rychlé odkazy.">
<img src="/images/docs/developers/extends/apps/document-generator/09-document-viewer.png" alt="Hlavní komponenta prohlížeče dokumentů na záložce záznamu" />
</Frame>
## Upravit šablonu pomocí editoru s bohatým textem
Šablony vůbec nepotřebují vlastní komponentu. Protože je `body`
`RICH_TEXT`, Dvacet již poskytuje plně bohatý textový editor
stejné jako standardní použití objektů poznámky a úkolu. Prostě jsme ji vykreslili na
šablonové stránce záznamu.
Přidejte kartu s `FIELD` widgetem v režimu zobrazení `EDITOR` ukazujícím na pole `body`
pomocí `fieldMetadataId`:
```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',
},
}],
}
```
Pole `RICH_TEXT` ukládá JSON blok editoru a Markdown
projekci. Plynovod pro výrobu čte tyto Markdown projekce, takže
zástupné symboly, PDF, a sdílitelná webová stránka všechny nefungují beze změny —
viz celý
[`template-record. age-layout.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/page-layouts/template-record.page-layout.ts).
Nyní editoři píší šablony v řádném editoru s bohatým textem:
<Frame caption="Šablona: Dvacátý nativní textový editor vázaný na pole těla.">
<img src="/images/docs/developers/extends/apps/document-generator/10-template-editor.png" alt="Záznam šablony s nativním textovým editorem" />
</Frame>
**Po tomto kroku:** náhled dokumentů krásně a šablony jsou editovatelné
v aplikaci. Dále nechte AI agenta generovat je z chatu.
<Card title="Další: agent AI →" icon="robot" href="/l/cs/developers/extend/apps/tutorials/document-generator/ai-agent">
Přidejte agenta a dovednosti, které volají po nástroji.
</Card>
@@ -0,0 +1,135 @@
---
title: 1. Datový model
icon: database
description: Vzor dokumentů a šablon s objekty, polími a vztahem.
---
Naše aplikace potřebuje dva vlastní objekty: **šablony dokumentů** (co psát) a
**dokumenty** (generovaný výsledek). Pojďme je definovat.
Scaffold each entity file with the CLI — has a valid UUID and the right
folder for you:
```bash filename="Terminal"
yarn twenty dev:add object
```
Níže zobrazujeme dokončené soubory.
<Note>
Každý `*_UNIVERSAL_IDENTIFIER` trvale žije v
`src/constants/universal-identifiers.ts` a je importován, pokud je použit. snippety
níže vynechávají tyto importy pro brevitu - uchovejte je ve svých vlastních souborech.
</Note>
## Objekt šablony
Šablona má `name`, `body` s `{{placeholders}}`, a `cíl`, který
říká, zda je napsán pro osobu nebo společnost. `Těloy` je
`RICH_TEXT` pole, takže dvacet jí dává plně bohatý textový editor.
```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` volba **hodnoty** musí být `UPPER_CASE` (`PERSON`, ne `person`) a
`defaultValue` je zabalena do extra uvozovek: `` `'PERSON'` ``. `Štítek` je to, co vidí
uživatelé.
</Warning>
## Objekt dokumentu
Vygenerovaný dokument ukládá vykreslený `content` a `status`. Definujte
stejným způsobem s `status` výběrem `DRAFT` / `GENERATED`. Celý soubor:
[`document.object.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/objects/document.object.ts).
## Propojení se vztahem
Každý dokument by se měl vrátit ke šabloně, odkud pochází. Vztahy jsou
**obousměrné** — definujete obě strany, každý ve vlastním souboru polí.
```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',
},
});
```
Druhá strana (`template-documents-relation.field.ts`) je
`RelationType.ONE_TO_MANY` pole `documents`, které ukazuje opačnou cestu.
Celý vzor viz [Relations](/l/cs/developers/extend/apps/data/relations).
## Podívejte se na to ve dvaceti letech
S spuštěním `příze dvacet dev` otevřete **Nastavení → Datový model**. Oba objekty
se zobrazí a označují vaší aplikací.
<Frame caption="Oba vlastní objekty v aplikaci Generátor dokumentů.">
<img src="/images/docs/developers/extends/apps/document-generator/01-data-model.png" alt="Nastavení datového modelu zobrazující dokumenty a šablony dokumentů" />
</Frame>
Vytvořte jednu šablonu pro testování - pojmenujte ji *návrhem prodeje*, nastavte **Cíl** na
*Osobní* a vložte tělo s několika zástupnými znaky:
```text
Dear {{name.firstName}} {{name.lastName}},
As {{jobTitle}} at {{company.name}}, we think you'll love our product.
Best,
The Team
```
<Frame caption="Záznam šablony. Subjekt uchovává své zástupné znaky, dokud není doklad vygenerován.">
<img src="/images/docs/developers/extends/apps/document-generator/03-template-record.png" alt="Záznam šablony obchodního návrhu s zástupným tělem" />
</Frame>
**Po tomto kroku:** máte `documentTemplate` a `document` objekty, propojené
relací a jednu šablonu, ze které chcete vygenerovat. Dále platí, že logika, která ji naplňuje.
<Card title="Další: generování dokumentů →" icon="bolt" href="/l/cs/developers/extend/apps/tutorials/document-generator/generating-documents">
Napište logickou funkci, která vyplňuje šablonu.
</Card>
@@ -0,0 +1,239 @@
---
title: 2. Generování dokumentů
icon: bolt
description: Jedna logická funkce, zobrazená jako nástroj AI a akce pracovního postupu.
---
Nyní jádro: [logická funkce](/l/cs/developers/extend/apps/logic/logic-functions)
, která načte šablonu a záznam, vyplní zástupné symboly a uloží nový
dokument.
Obchodní logiku napíšeme jednou jako **handler**, pak ji vystavíme pomocí
několika spouštěčů. Tato kapitola vrací dva z nich **nástroj AI** a
**akce pracovního postupu**.
## Pomocník pro vykreslování
Uchovávejte čistou logiku ve svém vlastním souboru, takže je snadné testovat jednotku. Toto zarovná záznam
do `{{dot.path}}` tokenů a nahradí je.
```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>
Protože tento soubor nemá žádné vedlejší účinky, můžete jej zakrýt rychlými jednotkovými testy
(`yarn test:unit`). Viz [Testing](/l/cs/developers/extend/apps/operations/testing).
</Tip>
## Řidič
Pracovník používá vygenerovaná [`CoreApiClient`](/l/cs/developers/extend/apps/logic/logic-functions)
pro čtení a zápis dat CRM. Načte šablonu, načte cílový záznam, vyplní
tělo a vytvoří `dokument`.
```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` spouští jiný dotaz na Osobu vs. společnost a flattens
výsledek — viz
[`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).
## Vystavit ji jako nástroj a akce pracovního postupu
Jediný `defineLogicFunction` může obsahovat několik spouštěčů. Zde `toolTriggerSettings`
dělá volatelné AI agenty a `workflowActionTriggerSettings` jej promění v
krok na vizuálním workflow. Obě popisují svůj vstup se schématem JSON.
```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,
});
```
Schéma vstupů je schéma prostého JSON popisující `templateId` a `recordId` —
viz [`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).
## Povolit přístup
Logické funkce běží jako role aplikace. Potřebuje přečíst šablony a záznamy
a vytvořit dokumenty, aby v `src/roles/default-role.ts`:
```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` umožňuje funkci nahrát vygenerovaný PDF v další sekci.
Pro jemnozrnná oprávnění viz [Roles](/l/cs/developers/extend/apps/config/roles).
## Připojit skutečný soubor PDF
Vykreslené textové pole je užitečné, ale uživatelé chtějí skutečný dokument. Vygenerujme
**PDF** a uložme jej do záznamu jako soubor ke stažení.
Nejprve zadejte objekt `document` pole `FILES` pro držení PDF. Aplikace nahrávají
do svých **vlastních** polí souborů, takže toto pole slouží jako trasa nahrávání:
```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 },
}
```
Nyní vytvořte tento PDF. Aplikace je skutečný projekt uzlu, takže můžete přidat libovolný balíček npm
, který potřebujete a importovat jako kdekoli jinde. Používáme **[pdf-lib](https://pdf-lib.js.org/)**
k nakreslení PDF a **[marked](https://marked.js.org/)** k rozepsání těla Markdown
CLI je nainstaluje do běhu funkce pro vás:
```bash filename="Terminal"
yarn add pdf-lib marked
```
Úplný pomocník je
[`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).
Parazuje Markdown do tokenů s `označeným. exer`, pak je stanoví s
pdf-lib: skutečné nadpisy, **tučné**/*italic* běhy, odrážky a číslované seznamy,
blokuje kotace a pravidla leštěné, vícestránkové A4 vykreslování šablony
samotné, nikoli zeď textu.
<Frame caption="Vygenerovaný PDF: skutečné typografie a Markdown formátování, vykreslování těla šablony.">
<img src="/images/docs/developers/extends/apps/document-generator/07b-generated-pdf.png" alt="Leštěný, tržně generovaný PDF" />
</Frame>
<Note>
Vestavěné fonty ve formátu pdf-lib, používají WinAnsi kódování, takže západoevropské akcenty vyřadí
z krabice; pomocník mapy chytrých uvozovek a pomlček a kapne znaky, které nemůže kódovat
. Vykreslování nelatinských skriptů (čínské, arabské, cyrilice) by znamenalo, že by
vložil písmo Unicode.
</Note>
Pak ho nahrajte a uložte odkaz do záznamu. `uploadFile` routy bytů
do vašeho pole souborů vlastněných aplikací; vrácený `id` je to, co uložíte:
```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,
},
});
```
Vygenerovaný dokument má nyní stažitelný PDF:
<Frame caption="Vygenerovaný PDF uložený v poli Soubor dokumentu.">
<img src="/images/docs/developers/extends/apps/document-generator/08-document-with-pdf.png" alt="Záznam dokladu s vygenerovaným souborem PDF" />
</Frame>
<Note>
`uploadFile` se zaměřuje pouze na **soubory vlastněné aplikací** (takže nahrávání vždy vyžaduje aplikaci
, která toto pole vlastní, plus proměnnou role `UPLOAD_FILE`). To je důvod, proč PDF
přistane na vlastním `file` pole záznamu — stejný vzor
[call-recorder app](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/public/call-recorder)
používá pro nahrávání.
</Note>
**Po tomto kroku:** každý vygenerovaný dokument má skutečný, stahovatelný PDF. Ale
nemůže *volat* generátor z uživatelského rozhraní - pro to potřebujeme HTTP trasu.
<Card title="Další: HTTP trasy →" icon="zeměkoule" href="/l/cs/developers/extend/apps/tutorials/document-generator/http-routes">
Zapněte funkci přes HTTP a vykreslete dokumenty jako webové stránky.
</Card>
@@ -0,0 +1,148 @@
---
title: 3. HTTP trasy
icon: globe
description: Spuštění funkce přes HTTP a vykreslení dokumentů jako webových stránek.
---
Stejný handler může také odpovědět na HTTP požadavky. Přidáme dva trasy:
* **POST** koncový bod uživatelského rozhraní volá, aby vytvořilo dokument, a
* veřejný **GET** koncový bod, který vykresluje dokument jako tiskovou webovou stránku.
Oba použijte `httpRouteTriggerSettings`. Trasy aplikací jsou vedeny pod `/s` na vašem
serveru (např. `http://localhost:2020/s/documents/generate`).
## POST trasa generovat na požádání
Toto znovu používá `generateDocumentHandler`, takže není logika opakovat - jen tenký
adaptér, který čte tělo požadavku.
```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,
},
});
```
Sdílený handler vrátí navržený `status` při selhání, takže trasa může
odpovědět správným `4xx`/`5xx` kódem. `isAuthRequd: true` znamená, že volající
musí prezentovat platný token — přední komponenta v další kapitole automaticky prochází přístupovým tokenem uživatele
.
## Cesta GET vykreslit jako webovou stránku
Chcete-li vrátit HTML místo JSON, zabalte tělo do `Response` pomocí
`Content-Type` hlavičky. Tato cesta je veřejná (`isAuthRequd: false`), takže
generovaný dokument může být sdílen jako odkaz.
```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` vykresluje Markdown tělo na HTML (s [marked](https://marked.js.org/),
zmaskoval) a klesne do čistého, vytisknutelná stránka, která zobrazuje pouze obsah šablony
stejný vzhled jako PDF a náhled v aplikaci.
[Viz pomocník] (https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/examples/document-generator/src/utils/render-document.ts).
## Vyzkoušejte
Pomocí šablony a osoby ve vašem pracovním prostoru zavolejte na trasu (získejte token z
**Nastavení → API a Webhooks**):
```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, ..."}
```
Otevřete vrácený dokument ve vašem prohlížeči:
```
http://localhost:2020/s/documents/view?id=<documentId>
```
<Frame caption="Veřejná trasa GET vykresluje dokument jako tiskovou stránku.">
<img src="/images/docs/developers/extends/apps/document-generator/07-rendered-document.png" alt="Vykreslená webová stránka dokumentu" />
</Frame>
<Tip>
Během testování s
`yarn twenty dev:function:logs`, nebo vyvolat přímo s
`yarn twenty dev:exec`.
</Tip>
**Po tomto kroku:** aplikace může generovat dokumenty přes HTTP a sloužit jako
webové stránky. Nyní ho použijeme bez `curl`.
<Card title="Další: budování UI →" icon="table-columns" href="/l/cs/developers/extend/apps/tutorials/document-generator/building-the-ui">
Zobrazení, navigace, příkaz a přední součást.
</Card>
@@ -0,0 +1,66 @@
---
title: "Tutoriál: Generátor dokumentů"
icon: wand-magic-sparkles
description: Vytvořte skutečnou aplikaci Twenty, která generuje personalizované dokumenty z vašich CRM dat.
---
V tomto tutoriálu vytvoříte **Generátor dokumentů** — aplikaci, která převádí znovu použitelná šablonová nastavení na personalizované dokumenty pomocí dat, která už máte ve svém CRM.
Napište šablonu jednou pomocí `{{placeholders}}` a poté vygenerujte vyplněný dokument pro libovolnou osobu nebo společnost jedním kliknutím — z příkazové nabídky, od AI agenta nebo z workflow.
<Frame caption="Jeden šablonový dokument, vygenerovaný pro konkrétní osobu, otevřený jako stránka připravená k tisku.">
<img src="/images/docs/developers/extends/apps/document-generator/07-rendered-document.png" alt="Vygenerovaný dokument obchodního návrhu" />
</Frame>
## Co se naučíte
Každá kapitola přidá jednu schopnost. Na konci se dotknete většiny SDK.
| Kapitola | Schopnost | Referenční dokumentace |
| ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| [1. Datový model](/l/cs/developers/extend/apps/tutorials/document-generator/data-model) | Objekty, pole a relace | [Data](/l/cs/developers/extend/apps/data/overview) |
| [2. Generování dokumentů](/l/cs/developers/extend/apps/tutorials/document-generator/generating-documents) | Logická funkce (nástroj AI + akce workflow), která vyplní šablonu v Markdownu a připojí vyladěné PDF | [Logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) |
| [3. HTTP trasy](/l/cs/developers/extend/apps/tutorials/document-generator/http-routes) | Poskytování JSONu a sdílené HTML stránky z tras | [Logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) |
| [4. Vytváření uživatelského rozhraní](/l/cs/developers/extend/apps/tutorials/document-generator/building-the-ui) | Zobrazení, navigace, příkazová nabídka a front komponenty, které zobrazují náhled dokumentu a upravují šablonu | [Rozvržení](/l/cs/developers/extend/apps/layout/overview) |
| [5. Agent AI](/l/cs/developers/extend/apps/tutorials/document-generator/ai-agent) | Agent + dovednost | [Dovednosti a agenti](/l/cs/developers/extend/apps/logic/skills-and-agents) |
| [6. Publikování](/l/cs/developers/extend/apps/tutorials/document-generator/publishing) | Dodejte ho na marketplace | [Publikování](/l/cs/developers/extend/apps/operations/publishing) |
## Předpoklady
Měli byste mít hotový [rychlý start](/l/cs/developers/extend/apps/getting-started/quick-start):
lokální server Twenty běžící na portu `2020` a CLI k němu přihlášené.
Pokud ne, nyní si jeden vytvořte a spusťte:
```bash filename="Terminal"
npx create-twenty-app@latest document-generator
cd document-generator
yarn twenty dev
```
<Note>
Raději byste si přečetli hotový kód? Celá aplikace je v
[`packages/twenty-apps/examples/document-generator`](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/document-generator).
Každý úryvek níže je z ní zkopírovaný.
</Note>
## Jak do sebe aplikace zapadá
<Frame>
<img src="/images/docs/developers/extends/apps/document-generator/how-it-fits.svg" alt="Šablona se zástupnými symboly je převedena na vyladěný dokument s PDF souborem, spuštěná z příkazové nabídky, agenta AI, workflow nebo sdíleného odkazu" />
</Frame>
**Šablonu** napíšete jednou v rich-text editoru pomocí `{{placeholders}}`. Výběr
šablony a CRM záznamu vyplní zástupné symboly a uloží vyladěný
**dokument** (s PDF souborem). Všechno ostatní — příkazová nabídka, agent AI,
krok workflow, sdílený odkaz — je jen jiný způsob, jak spustit ten
jeden generátor.
## Udržujte tento cyklus v chodu
Nechte `yarn twenty dev` běžet v terminálu po celý návod. Pokaždé, když
přidáte nebo upravíte soubor pod `src/`, během několika sekund se znovu synchronizuje s vaším serverem, takže můžete sledovat, jak se každá schopnost objevuje v uživatelském rozhraní, jak ji vytváříte.
<Card title="Začněte vytvářet →" icon="database" href="/l/cs/developers/extend/apps/tutorials/document-generator/data-model">
Kapitola 1: model dokumentů a šablon.
</Card>
@@ -0,0 +1,137 @@
---
title: 6. Publikování
icon: rocket
description: Přidejte metadata tržiště a publikujte svou aplikaci.
---
Vaše aplikace funguje. Posledním krokem je popsat jej pro tržiště a publikovat.
## Přidat metadata tržiště
[application config](/l/cs/developers/extend/apps/config/application) nese
identitu, která se objeví v tržišti: autor, kategorie, logo a podpora
. Vložte logo do `public/` a odkazujte na `logoUrl`.
```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/cs/developers/extend/apps',
termsUrl: 'https://www.twenty.com/terms',
emailSupport: 'contact@twenty.com',
issueReportUrl: 'https://github.com/twentyhq/twenty/issues',
});
```
<Tip>
Výchozí role je deklarována s `defineApplicationRole()` ve svém vlastním souboru vy
zde již neprojdete `defaultRoleUniversalIdentifier`.
</Tip>
Přidejte také klíčové slovo `dvacet app` do `package.json` tak, aby byla aplikace nalezena:
```json filename="package.json"
{ "keywords": ["twenty-app"] }
```
## Přidat galerii snímků obrazovky
tržiště se prodává se snímky obrazovky. Přetáhněte několik PNGů do
`public/gallery/` a odkazujte na ně `screenshots` — vykreslují se jako galerie
na stránce seznamu.
```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>
Lead with the payoff: make the first screenshot the finished result (generated
document), then show how it is triggered and authored. (Automatic Copy) Použít křiklavé, vysoké rozlišení
snímky — jsou to první věc, kterou uživatel vidí.
</Tip>
Dejte `README.md` stejné ošetření — je to přední stránka na npm a GitHub.
Otevřete pomocí nabídky hodnot a snímku obrazovky, vyberte titulky,
ponechte podrobnosti sestavení pod složkou.
## Zkontrolovat před odesláním
Spustit tytéž brány CI:
```bash filename="Terminal"
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
yarn twenty dev --once --dry-run # preview the metadata diff
```
Suchý běh vypíše přesně to, co by se změnilo na serveru bez jeho použití
je dobrá závěrečná kontrola. Viz
[Testing](/l/cs/developers/extend/apps/operations/testing) a
[Synchronizace a obnovy](/l/cs/developers/extend/apps/operations/sync-and-recovery).
## Publikovat
```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` ve výchozím nastavení sestavuje a publikuje do npm; `--private` nahraje
tarball do privátního registru 20 serverů. Chcete-li zobrazit publikovanou aplikaci
v obchodě instance, spustí synchronizaci katalogu:
```bash filename="Terminal"
yarn twenty dev:catalog-sync -r <remote>
```
Úplné podrobnosti a kontrolní seznam vydání:
[Publishing](/l/cs/developers/extend/apps/operations/publishing).
## Vytvořili jste aplikaci 🎉
V šesti kapitolách jste použili většinu povrchu SDK:
* **Objekty, pole a vztahy** modelovat data
* **Logická funkce** obklopená jako **nástroj AI**, **akce pracovního postupu** a **cesty HTTP**
* **Zobrazení, navigace, příkaz a přední složka** pro UI
* **agent + dovednost** pro generování přirozeného jazyka
* **Metadata tržiště** a tok zveřejnění
Dokončená aplikace je na
[`packages/twenty-apps/examples/document-generator`](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/document-generator).
## Kde jít dál
<CardGroup cols={2}>
<Card title="Odkaz na údaje" icon="database" href="/l/cs/developers/extend/apps/data/overview">
Každý typ pole, vztah a index.
</Card>
<Card title="Logický odkaz" icon="bolt" href="/l/cs/developers/extend/apps/logic/overview">
Cron a databázová událost, klíčový obchod, připojení OAuth.
</Card>
<Card title="Odkaz na rozložení" icon="table-columns" href="/l/cs/developers/extend/apps/layout/overview">
Rozvržení stránky, widgety hlavního panelu a další povrchy uživatelského rozhraní.
</Card>
<Card title="Operace" icon="rocket" href="/l/cs/developers/extend/apps/operations/overview">
CLI, testování, dálkové ovládání a CI.
</Card>
</CardGroup>