From 2b3b2362db653e5b478cf847f2f0e8dfe7857316 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 15:21:04 +0200 Subject: [PATCH] i18n - docs translations (#21789) Created by Github action Co-authored-by: github-actions --- packages/twenty-docs/docs.json | 596 ++++++------- .../l/ar/developers/extend/api.mdx | 2 +- .../extend/apps/config/application.mdx | 64 ++ .../extend/apps/config/install-hooks.mdx | 206 +++++ .../extend/apps/config/overview.mdx | 51 ++ .../extend/apps/config/public-assets.mdx | 67 ++ .../developers/extend/apps/config/roles.mdx | 94 +++ .../extend/apps/data/extending-objects.mdx | 50 ++ .../developers/extend/apps/data/objects.mdx | 104 +++ .../developers/extend/apps/data/overview.mdx | 97 +++ .../developers/extend/apps/data/relations.mdx | 160 ++++ .../extend/apps/getting-started/concepts.mdx | 101 +++ .../apps/getting-started/local-server.mdx | 87 ++ .../getting-started/project-structure.mdx | 61 ++ .../apps/getting-started/quick-start.mdx | 176 ++++ .../apps/getting-started/scaffolding.mdx | 58 ++ .../apps/getting-started/troubleshooting.mdx | 14 + .../extend/apps/layout/command-menu-items.mdx | 148 ++++ .../extend/apps/layout/front-components.mdx | 545 ++++++++++++ .../apps/layout/navigation-menu-items.mdx | 44 + .../extend/apps/layout/overview.mdx | 56 ++ .../extend/apps/layout/page-layouts.mdx | 132 +++ .../developers/extend/apps/layout/views.mdx | 97 +++ .../extend/apps/logic/connections.mdx | 192 +++++ .../extend/apps/logic/logic-functions.mdx | 514 +++++++++++ .../developers/extend/apps/logic/overview.mdx | 55 ++ .../extend/apps/logic/skills-and-agents.mdx | 138 +++ .../developers/extend/apps/operations/cli.mdx | 105 +++ .../extend/apps/operations/overview.mdx | 32 + .../extend/apps/operations/publishing.mdx | 294 +++++++ .../apps/operations/sync-and-recovery.mdx | 111 +++ .../extend/apps/operations/testing.mdx | 301 +++++++ .../developers/extend/capabilities/apis.mdx | 2 +- .../self-host/capabilities/docker-compose.mdx | 8 +- .../self-host/capabilities/key-rotation.mdx | 60 ++ .../self-host/capabilities/setup.mdx | 17 +- .../self-host/capabilities/upgrade-guide.mdx | 12 + packages/twenty-docs/l/ar/navigation.json | 22 +- .../l/ar/twenty-ui/input/buttons.mdx | 2 +- .../l/ar/twenty-ui/input/color-scheme.mdx | 2 +- .../l/ar/twenty-ui/input/image-input.mdx | 6 +- .../l/ar/twenty-ui/input/radio.mdx | 2 +- .../twenty-docs/l/ar/twenty-ui/input/text.mdx | 2 +- .../l/ar/twenty-ui/navigation/links.mdx | 4 +- .../l/ar/user-guide/ai/capabilities/mcp.mdx | 7 +- .../permissions-access-control.mdx | 2 +- .../l/ar/user-guide/ai/how-tos/ai-faq.mdx | 2 +- .../l/ar/user-guide/ai/overview.mdx | 2 +- .../billing/capabilities/pricing-plans.mdx | 22 +- .../user-guide/calendar-emails/overview.mdx | 27 +- .../capabilities/field-mapping.mdx | 10 +- .../capabilities/file-formats.mdx | 2 +- .../how-tos/migrating-from-other-crms.mdx | 2 +- .../migrating-from-self-hosted-to-cloud.mdx | 2 +- .../how-tos/prepare-your-csv-files.mdx | 2 +- .../update-existing-records-via-import.mdx | 4 +- .../data-model/capabilities/fields.mdx | 4 + .../capabilities/permissions.mdx | 30 +- .../how-tos/permissions-faq.mdx | 6 +- .../capabilities/domains-settings.mdx | 15 +- .../capabilities/member-management.mdx | 2 +- .../settings/how-tos/settings-faq.mdx | 4 +- .../l/ar/user-guide/settings/overview.mdx | 2 +- .../show-expected-amount-in-pipeline.mdx | 2 +- .../how-tos/track-time-in-stage.mdx | 2 +- .../capabilities/workflow-actions.mdx | 2 +- .../how-tos/need-more-help/workflows-faq.mdx | 2 +- .../l/cs/developers/extend/api.mdx | 2 +- .../extend/apps/config/application.mdx | 64 ++ .../extend/apps/config/install-hooks.mdx | 206 +++++ .../extend/apps/config/overview.mdx | 51 ++ .../extend/apps/config/public-assets.mdx | 67 ++ .../developers/extend/apps/config/roles.mdx | 94 +++ .../extend/apps/data/extending-objects.mdx | 50 ++ .../developers/extend/apps/data/objects.mdx | 104 +++ .../developers/extend/apps/data/overview.mdx | 97 +++ .../developers/extend/apps/data/relations.mdx | 160 ++++ .../extend/apps/getting-started/concepts.mdx | 101 +++ .../apps/getting-started/local-server.mdx | 87 ++ .../getting-started/project-structure.mdx | 61 ++ .../apps/getting-started/quick-start.mdx | 176 ++++ .../apps/getting-started/scaffolding.mdx | 58 ++ .../apps/getting-started/troubleshooting.mdx | 14 + .../extend/apps/layout/command-menu-items.mdx | 148 ++++ .../extend/apps/layout/front-components.mdx | 545 ++++++++++++ .../apps/layout/navigation-menu-items.mdx | 44 + .../extend/apps/layout/overview.mdx | 56 ++ .../extend/apps/layout/page-layouts.mdx | 132 +++ .../developers/extend/apps/layout/views.mdx | 97 +++ .../extend/apps/logic/connections.mdx | 192 +++++ .../extend/apps/logic/logic-functions.mdx | 515 +++++++++++ .../developers/extend/apps/logic/overview.mdx | 55 ++ .../extend/apps/logic/skills-and-agents.mdx | 138 +++ .../developers/extend/apps/operations/cli.mdx | 105 +++ .../extend/apps/operations/overview.mdx | 32 + .../extend/apps/operations/publishing.mdx | 294 +++++++ .../apps/operations/sync-and-recovery.mdx | 111 +++ .../extend/apps/operations/testing.mdx | 301 +++++++ .../developers/extend/capabilities/apis.mdx | 2 +- .../self-host/capabilities/docker-compose.mdx | 8 +- .../self-host/capabilities/key-rotation.mdx | 60 ++ .../self-host/capabilities/setup.mdx | 17 +- .../self-host/capabilities/upgrade-guide.mdx | 12 + packages/twenty-docs/l/cs/navigation.json | 22 +- .../twenty-docs/l/cs/twenty-ui/input/text.mdx | 2 +- .../l/cs/user-guide/ai/capabilities/mcp.mdx | 7 +- .../permissions-access-control.mdx | 2 +- .../l/cs/user-guide/ai/how-tos/ai-faq.mdx | 2 +- .../l/cs/user-guide/ai/overview.mdx | 2 +- .../billing/capabilities/pricing-plans.mdx | 22 +- .../user-guide/calendar-emails/overview.mdx | 27 +- .../capabilities/field-mapping.mdx | 10 +- .../capabilities/file-formats.mdx | 2 +- .../how-tos/migrating-from-other-crms.mdx | 2 +- .../migrating-from-self-hosted-to-cloud.mdx | 2 +- .../how-tos/prepare-your-csv-files.mdx | 2 +- .../data-model/capabilities/fields.mdx | 4 + .../capabilities/permissions.mdx | 30 +- .../how-tos/permissions-faq.mdx | 6 +- .../capabilities/domains-settings.mdx | 15 +- .../capabilities/member-management.mdx | 2 +- .../settings/how-tos/settings-faq.mdx | 4 +- .../l/cs/user-guide/settings/overview.mdx | 2 +- .../show-expected-amount-in-pipeline.mdx | 2 +- .../how-tos/track-time-in-stage.mdx | 2 +- .../send-emails-from-workflows.mdx | 2 +- .../capabilities/workflow-actions.mdx | 2 +- .../how-tos/need-more-help/workflows-faq.mdx | 2 +- .../twenty-docs/l/de/twenty-ui/input/text.mdx | 2 +- .../capabilities/field-mapping.mdx | 2 +- .../capabilities/file-formats.mdx | 2 +- .../update-existing-records-via-import.mdx | 4 +- .../backend-development/custom-objects.mdx | 6 +- .../backend-development/server-commands.mdx | 1 + .../backend-development/zapier.mdx | 4 +- .../capabilities/bug-and-requests.mdx | 1 + .../best-practices-front.mdx | 17 +- .../folder-architecture-front.mdx | 3 +- .../frontend-commands.mdx | 3 +- .../frontend-development/hotkeys.mdx | 6 +- .../frontend-development/style-guide.mdx | 3 +- .../frontend-development/work-with-figma.mdx | 4 +- .../contribute/capabilities/local-setup.mdx | 190 ++--- .../l/es/developers/contribute/commands.mdx | 77 ++ .../l/es/developers/contribute/contribute.mdx | 1 - .../es/developers/contribute/style-guide.mdx | 176 ++++ .../l/es/developers/extend/api.mdx | 55 ++ .../extend/apps/config/application.mdx | 64 ++ .../extend/apps/config/install-hooks.mdx | 206 +++++ .../extend/apps/config/overview.mdx | 51 ++ .../extend/apps/config/public-assets.mdx | 67 ++ .../developers/extend/apps/config/roles.mdx | 94 +++ .../extend/apps/data/extending-objects.mdx | 50 ++ .../developers/extend/apps/data/objects.mdx | 104 +++ .../developers/extend/apps/data/overview.mdx | 97 +++ .../developers/extend/apps/data/relations.mdx | 160 ++++ .../extend/apps/getting-started/concepts.mdx | 101 +++ .../apps/getting-started/local-server.mdx | 87 ++ .../getting-started/project-structure.mdx | 61 ++ .../apps/getting-started/quick-start.mdx | 176 ++++ .../apps/getting-started/scaffolding.mdx | 58 ++ .../apps/getting-started/troubleshooting.mdx | 14 + .../extend/apps/layout/command-menu-items.mdx | 148 ++++ .../extend/apps/layout/front-components.mdx | 545 ++++++++++++ .../apps/layout/navigation-menu-items.mdx | 44 + .../extend/apps/layout/overview.mdx | 56 ++ .../extend/apps/layout/page-layouts.mdx | 132 +++ .../developers/extend/apps/layout/views.mdx | 97 +++ .../extend/apps/logic/connections.mdx | 192 +++++ .../extend/apps/logic/logic-functions.mdx | 515 +++++++++++ .../developers/extend/apps/logic/overview.mdx | 55 ++ .../extend/apps/logic/skills-and-agents.mdx | 138 +++ .../developers/extend/apps/operations/cli.mdx | 105 +++ .../extend/apps/operations/overview.mdx | 32 + .../extend/apps/operations/publishing.mdx | 294 +++++++ .../apps/operations/sync-and-recovery.mdx | 111 +++ .../extend/apps/operations/testing.mdx | 301 +++++++ .../developers/extend/capabilities/apis.mdx | 8 +- .../extend/capabilities/webhooks.mdx | 2 +- .../l/es/developers/extend/extend.mdx | 13 +- .../l/es/developers/extend/oauth.mdx | 189 +++++ .../l/es/developers/extend/webhooks.mdx | 117 +++ .../l/es/developers/introduction.mdx | 33 +- .../self-host/capabilities/docker-compose.mdx | 15 +- .../self-host/capabilities/key-rotation.mdx | 60 ++ .../self-host/capabilities/setup.mdx | 155 ++-- .../capabilities/troubleshooting.mdx | 7 +- .../self-host/capabilities/upgrade-guide.mdx | 395 ++------- packages/twenty-docs/l/es/navigation.json | 145 ++-- .../l/es/twenty-ui/display/app-tooltip.mdx | 123 +-- .../l/es/twenty-ui/display/checkmark.mdx | 84 +- .../l/es/twenty-ui/display/chip.mdx | 144 ++-- .../l/es/twenty-ui/display/icons.mdx | 93 +- .../l/es/twenty-ui/display/soon-pill.mdx | 1 - .../l/es/twenty-ui/display/tag.mdx | 57 +- .../l/es/twenty-ui/input/block-editor.mdx | 36 +- .../l/es/twenty-ui/input/buttons.mdx | 796 ++++++++++-------- .../l/es/twenty-ui/input/checkbox.mdx | 65 +- .../l/es/twenty-ui/input/color-scheme.mdx | 91 +- .../l/es/twenty-ui/input/icon-picker.mdx | 73 +- .../l/es/twenty-ui/input/image-input.mdx | 45 +- .../l/es/twenty-ui/input/radio.mdx | 147 ++-- .../l/es/twenty-ui/input/select.mdx | 67 +- .../twenty-docs/l/es/twenty-ui/input/text.mdx | 218 ++--- .../l/es/twenty-ui/input/toggle.mdx | 54 +- .../l/es/twenty-ui/introduction.mdx | 1 + .../twenty-docs/l/es/twenty-ui/navigation.mdx | 1 + .../l/es/twenty-ui/navigation/breadcrumb.mdx | 56 +- .../l/es/twenty-ui/navigation/links.mdx | 242 +++--- .../l/es/twenty-ui/navigation/menu-item.mdx | 701 ++++++++------- .../twenty-ui/navigation/navigation-bar.mdx | 73 +- .../l/es/twenty-ui/navigation/step-bar.mdx | 46 +- .../l/es/twenty-ui/progress-bar.mdx | 102 ++- .../user-guide/ai/capabilities/ai-agents.mdx | 2 +- .../user-guide/ai/capabilities/ai-chatbot.mdx | 2 +- .../l/es/user-guide/ai/capabilities/mcp.mdx | 141 ++++ .../permissions-access-control.mdx | 4 +- .../l/es/user-guide/ai/how-tos/ai-faq.mdx | 4 +- .../l/es/user-guide/ai/overview.mdx | 4 +- .../billing/capabilities/credits.mdx | 27 +- .../billing/capabilities/pricing-plans.mdx | 21 +- .../billing/how-tos/billing-faq.mdx | 114 +-- .../l/es/user-guide/billing/overview.mdx | 2 - .../how-tos/can-i-send-emails-from-twenty.mdx | 2 +- .../how-tos/limit-emails-imported.mdx | 2 +- .../user-guide/calendar-emails/overview.mdx | 32 +- .../capabilities/chart-settings.mdx | 16 +- .../dashboards/capabilities/dashboards.mdx | 4 +- .../dashboards/capabilities/widgets.mdx | 16 +- .../dashboards/how-tos/dashboards-faq.mdx | 1 - .../l/es/user-guide/dashboards/overview.mdx | 10 +- .../capabilities/field-mapping.mdx | 4 +- .../capabilities/import-relations.mdx | 20 +- .../capabilities/uniqueness-constraints.mdx | 4 +- .../how-tos/export-your-data.mdx | 6 +- .../how-tos/fix-import-errors.mdx | 28 +- .../how-tos/import-companies-via-csv.mdx | 18 +- .../how-tos/import-contacts-via-csv.mdx | 24 +- .../how-tos/import-data-via-api.mdx | 18 +- ...port-relations-between-objects-via-csv.mdx | 15 +- .../how-tos/migrating-from-other-crms.mdx | 31 +- .../migrating-from-self-hosted-to-cloud.mdx | 6 +- .../how-tos/prepare-your-csv-files.mdx | 17 +- .../update-existing-records-via-import.mdx | 10 +- .../es/user-guide/data-migration/overview.mdx | 16 +- .../data-model/capabilities/fields.mdx | 4 + .../capabilities/relation-fields.mdx | 4 +- .../how-tos/create-custom-fields.mdx | 4 +- .../how-tos/create-custom-objects.mdx | 2 +- .../how-tos/create-many-to-many-relations.mdx | 79 +- .../how-tos/create-relation-fields.mdx | 6 +- .../data-model/how-tos/data-model-faq.mdx | 189 +++-- .../l/es/user-guide/data-model/overview.mdx | 9 +- .../capabilities/what-is-twenty.mdx | 2 +- .../l/es/user-guide/introduction.mdx | 14 +- .../layout/capabilities/navigation.mdx | 38 + .../layout/capabilities/record-pages.mdx | 61 ++ .../l/es/user-guide/layout/overview.mdx | 45 + .../capabilities/permissions.mdx | 42 +- .../capabilities/sso-configuration.mdx | 2 +- .../how-tos/permissions-faq.mdx | 164 ++-- .../permissions-access/overview.mdx | 3 - .../capabilities/domains-settings.mdx | 17 +- .../capabilities/experience-settings.mdx | 2 +- .../capabilities/member-management.mdx | 6 +- .../capabilities/profile-settings.mdx | 2 +- .../capabilities/updates-settings.mdx | 2 +- .../capabilities/workspace-settings.mdx | 2 +- .../settings/how-tos/settings-faq.mdx | 230 ++--- .../l/es/user-guide/settings/overview.mdx | 3 +- .../capabilities/table-views.mdx | 2 +- .../capabilities/view-settings.mdx | 4 +- .../create-a-table-view-with-grouping.mdx | 2 +- .../how-tos/set-up-a-sales-pipeline.mdx | 2 +- .../show-expected-amount-in-pipeline.mdx | 6 +- .../how-tos/track-time-in-stage.mdx | 8 +- .../user-guide/views-pipelines/overview.mdx | 5 +- .../send-emails-from-workflows.mdx | 2 +- .../use-branches-in-workflows.mdx | 2 +- .../capabilities/workflow-actions.mdx | 40 +- .../capabilities/workflow-branches.mdx | 6 +- .../capabilities/workflow-credits.mdx | 10 +- .../capabilities/workflow-triggers.mdx | 23 +- .../handle-arrays-in-code-actions.mdx | 4 +- .../bring-product-data-in-twenty.mdx | 4 +- .../bring-typeform-submissions-in-twenty.mdx | 2 +- .../generate-pdf-from-twenty.mdx | 87 +- .../generate-quote-or-invoice-from-twenty.mdx | 2 +- .../set-up-a-webhook-trigger.mdx | 2 +- .../auto-reply-to-inbound-emails.mdx | 121 +++ .../display-number-of-emails-received.mdx | 6 +- .../display-related-record-data.mdx | 4 +- .../crm-automations/formula-fields.mdx | 4 +- .../notify-teammates-of-note-to-review.mdx | 6 +- .../send-email-alerts-with-tasks-due.mdx | 2 +- .../workflow-troubleshooting.mdx | 2 +- .../how-tos/need-more-help/workflows-faq.mdx | 22 +- .../l/es/user-guide/workflows/overview.mdx | 1 - .../backend-development/custom-objects.mdx | 6 +- .../backend-development/server-commands.mdx | 8 + .../backend-development/zapier.mdx | 4 +- .../capabilities/bug-and-requests.mdx | 1 + .../best-practices-front.mdx | 13 +- .../folder-architecture-front.mdx | 3 +- .../frontend-commands.mdx | 1 + .../frontend-development/hotkeys.mdx | 6 +- .../frontend-development/style-guide.mdx | 7 +- .../frontend-development/work-with-figma.mdx | 4 +- .../contribute/capabilities/local-setup.mdx | 190 ++--- .../l/fr/developers/contribute/commands.mdx | 77 ++ .../l/fr/developers/contribute/contribute.mdx | 1 - .../fr/developers/contribute/style-guide.mdx | 176 ++++ .../l/fr/developers/extend/api.mdx | 55 ++ .../extend/apps/config/application.mdx | 64 ++ .../extend/apps/config/install-hooks.mdx | 206 +++++ .../extend/apps/config/overview.mdx | 51 ++ .../extend/apps/config/public-assets.mdx | 67 ++ .../developers/extend/apps/config/roles.mdx | 94 +++ .../extend/apps/data/extending-objects.mdx | 50 ++ .../developers/extend/apps/data/objects.mdx | 104 +++ .../developers/extend/apps/data/overview.mdx | 97 +++ .../developers/extend/apps/data/relations.mdx | 160 ++++ .../extend/apps/getting-started/concepts.mdx | 101 +++ .../apps/getting-started/local-server.mdx | 87 ++ .../getting-started/project-structure.mdx | 61 ++ .../apps/getting-started/quick-start.mdx | 176 ++++ .../apps/getting-started/scaffolding.mdx | 58 ++ .../apps/getting-started/troubleshooting.mdx | 14 + .../extend/apps/layout/command-menu-items.mdx | 148 ++++ .../extend/apps/layout/front-components.mdx | 545 ++++++++++++ .../apps/layout/navigation-menu-items.mdx | 44 + .../extend/apps/layout/overview.mdx | 56 ++ .../extend/apps/layout/page-layouts.mdx | 132 +++ .../developers/extend/apps/layout/views.mdx | 97 +++ .../extend/apps/logic/connections.mdx | 192 +++++ .../extend/apps/logic/logic-functions.mdx | 515 +++++++++++ .../developers/extend/apps/logic/overview.mdx | 55 ++ .../extend/apps/logic/skills-and-agents.mdx | 138 +++ .../developers/extend/apps/operations/cli.mdx | 105 +++ .../extend/apps/operations/overview.mdx | 32 + .../extend/apps/operations/publishing.mdx | 294 +++++++ .../apps/operations/sync-and-recovery.mdx | 111 +++ .../extend/apps/operations/testing.mdx | 301 +++++++ .../developers/extend/capabilities/apis.mdx | 8 +- .../extend/capabilities/webhooks.mdx | 2 +- .../l/fr/developers/extend/extend.mdx | 13 +- .../l/fr/developers/extend/oauth.mdx | 189 +++++ .../l/fr/developers/extend/webhooks.mdx | 117 +++ .../l/fr/developers/introduction.mdx | 33 +- .../self-host/capabilities/docker-compose.mdx | 15 +- .../self-host/capabilities/key-rotation.mdx | 60 ++ .../self-host/capabilities/setup.mdx | 155 ++-- .../capabilities/troubleshooting.mdx | 5 + .../self-host/capabilities/upgrade-guide.mdx | 395 ++------- packages/twenty-docs/l/fr/navigation.json | 145 ++-- .../l/fr/twenty-ui/display/app-tooltip.mdx | 123 +-- .../l/fr/twenty-ui/display/checkmark.mdx | 84 +- .../l/fr/twenty-ui/display/chip.mdx | 144 ++-- .../l/fr/twenty-ui/display/icons.mdx | 93 +- .../l/fr/twenty-ui/display/soon-pill.mdx | 1 - .../l/fr/twenty-ui/display/tag.mdx | 57 +- .../l/fr/twenty-ui/input/block-editor.mdx | 36 +- .../l/fr/twenty-ui/input/buttons.mdx | 796 ++++++++++-------- .../l/fr/twenty-ui/input/checkbox.mdx | 65 +- .../l/fr/twenty-ui/input/color-scheme.mdx | 91 +- .../l/fr/twenty-ui/input/icon-picker.mdx | 73 +- .../l/fr/twenty-ui/input/image-input.mdx | 45 +- .../l/fr/twenty-ui/input/radio.mdx | 147 ++-- .../l/fr/twenty-ui/input/select.mdx | 68 +- .../twenty-docs/l/fr/twenty-ui/input/text.mdx | 215 +++-- .../l/fr/twenty-ui/input/toggle.mdx | 54 +- .../l/fr/twenty-ui/introduction.mdx | 1 + .../twenty-docs/l/fr/twenty-ui/navigation.mdx | 1 + .../l/fr/twenty-ui/navigation/breadcrumb.mdx | 56 +- .../l/fr/twenty-ui/navigation/links.mdx | 242 +++--- .../l/fr/twenty-ui/navigation/menu-item.mdx | 701 ++++++++------- .../twenty-ui/navigation/navigation-bar.mdx | 73 +- .../l/fr/twenty-ui/navigation/step-bar.mdx | 46 +- .../l/fr/twenty-ui/progress-bar.mdx | 102 ++- .../user-guide/ai/capabilities/ai-agents.mdx | 2 +- .../user-guide/ai/capabilities/ai-chatbot.mdx | 2 +- .../l/fr/user-guide/ai/capabilities/mcp.mdx | 141 ++++ .../permissions-access-control.mdx | 4 +- .../l/fr/user-guide/ai/how-tos/ai-faq.mdx | 4 +- .../l/fr/user-guide/ai/overview.mdx | 4 +- .../billing/capabilities/credits.mdx | 25 +- .../billing/capabilities/pricing-plans.mdx | 21 +- .../billing/how-tos/billing-faq.mdx | 114 +-- .../l/fr/user-guide/billing/overview.mdx | 2 - .../how-tos/can-i-send-emails-from-twenty.mdx | 2 +- .../user-guide/calendar-emails/overview.mdx | 32 +- .../capabilities/chart-settings.mdx | 16 +- .../dashboards/capabilities/dashboards.mdx | 4 +- .../dashboards/capabilities/widgets.mdx | 16 +- .../dashboards/how-tos/dashboards-faq.mdx | 3 +- .../l/fr/user-guide/dashboards/overview.mdx | 10 +- .../capabilities/field-mapping.mdx | 4 +- .../capabilities/file-formats.mdx | 2 +- .../capabilities/import-relations.mdx | 20 +- .../capabilities/uniqueness-constraints.mdx | 4 +- .../how-tos/export-your-data.mdx | 6 +- .../how-tos/fix-import-errors.mdx | 28 +- .../how-tos/import-companies-via-csv.mdx | 18 +- .../how-tos/import-contacts-via-csv.mdx | 24 +- .../how-tos/import-data-via-api.mdx | 18 +- ...port-relations-between-objects-via-csv.mdx | 15 +- .../how-tos/migrating-from-other-crms.mdx | 31 +- .../migrating-from-self-hosted-to-cloud.mdx | 6 +- .../how-tos/prepare-your-csv-files.mdx | 15 +- .../update-existing-records-via-import.mdx | 10 +- .../fr/user-guide/data-migration/overview.mdx | 18 +- .../data-model/capabilities/fields.mdx | 4 + .../capabilities/relation-fields.mdx | 4 +- .../how-tos/create-custom-fields.mdx | 4 +- .../how-tos/create-custom-objects.mdx | 2 +- .../how-tos/create-many-to-many-relations.mdx | 79 +- .../how-tos/create-relation-fields.mdx | 4 +- .../how-tos/customize-your-data-model.mdx | 2 +- .../data-model/how-tos/data-model-faq.mdx | 189 +++-- .../l/fr/user-guide/data-model/overview.mdx | 9 +- .../capabilities/what-is-twenty.mdx | 2 +- .../l/fr/user-guide/introduction.mdx | 14 +- .../layout/capabilities/navigation.mdx | 38 + .../layout/capabilities/record-pages.mdx | 61 ++ .../l/fr/user-guide/layout/overview.mdx | 45 + .../capabilities/permissions.mdx | 42 +- .../capabilities/sso-configuration.mdx | 2 +- .../how-tos/permissions-faq.mdx | 164 ++-- .../permissions-access/overview.mdx | 3 - .../capabilities/domains-settings.mdx | 17 +- .../capabilities/member-management.mdx | 6 +- .../capabilities/profile-settings.mdx | 2 +- .../capabilities/updates-settings.mdx | 2 +- .../capabilities/workspace-settings.mdx | 2 +- .../settings/how-tos/settings-faq.mdx | 230 ++--- .../l/fr/user-guide/settings/overview.mdx | 3 +- .../capabilities/table-views.mdx | 2 +- .../capabilities/view-settings.mdx | 4 +- .../create-a-table-view-with-grouping.mdx | 2 +- .../how-tos/restrict-access-to-your-view.mdx | 2 +- .../how-tos/set-up-a-sales-pipeline.mdx | 2 +- .../show-expected-amount-in-pipeline.mdx | 8 +- .../how-tos/track-time-in-stage.mdx | 8 +- .../user-guide/views-pipelines/overview.mdx | 5 +- .../send-emails-from-workflows.mdx | 2 +- .../use-branches-in-workflows.mdx | 2 +- .../capabilities/workflow-actions.mdx | 40 +- .../capabilities/workflow-branches.mdx | 6 +- .../capabilities/workflow-credits.mdx | 10 +- .../capabilities/workflow-triggers.mdx | 23 +- .../handle-arrays-in-code-actions.mdx | 4 +- .../bring-product-data-in-twenty.mdx | 4 +- .../bring-typeform-submissions-in-twenty.mdx | 2 +- .../generate-pdf-from-twenty.mdx | 87 +- .../generate-quote-or-invoice-from-twenty.mdx | 2 +- .../set-up-a-webhook-trigger.mdx | 2 +- .../auto-reply-to-inbound-emails.mdx | 121 +++ .../display-number-of-emails-received.mdx | 6 +- .../display-related-record-data.mdx | 4 +- .../crm-automations/formula-fields.mdx | 4 +- .../notify-teammates-of-note-to-review.mdx | 6 +- .../send-email-alerts-with-tasks-due.mdx | 2 +- .../workflow-troubleshooting.mdx | 2 +- .../how-tos/need-more-help/workflows-faq.mdx | 22 +- .../l/fr/user-guide/workflows/overview.mdx | 1 - .../frontend-development/style-guide.mdx | 2 +- .../l/it/developers/extend/api.mdx | 2 +- .../extend/apps/config/application.mdx | 64 ++ .../extend/apps/config/install-hooks.mdx | 206 +++++ .../extend/apps/config/overview.mdx | 51 ++ .../extend/apps/config/public-assets.mdx | 67 ++ .../developers/extend/apps/config/roles.mdx | 94 +++ .../extend/apps/data/extending-objects.mdx | 50 ++ .../developers/extend/apps/data/objects.mdx | 104 +++ .../developers/extend/apps/data/overview.mdx | 97 +++ .../developers/extend/apps/data/relations.mdx | 160 ++++ .../extend/apps/getting-started/concepts.mdx | 101 +++ .../apps/getting-started/local-server.mdx | 87 ++ .../getting-started/project-structure.mdx | 61 ++ .../apps/getting-started/quick-start.mdx | 176 ++++ .../apps/getting-started/scaffolding.mdx | 58 ++ .../apps/getting-started/troubleshooting.mdx | 14 + .../extend/apps/layout/command-menu-items.mdx | 148 ++++ .../extend/apps/layout/front-components.mdx | 545 ++++++++++++ .../apps/layout/navigation-menu-items.mdx | 44 + .../extend/apps/layout/overview.mdx | 56 ++ .../extend/apps/layout/page-layouts.mdx | 132 +++ .../developers/extend/apps/layout/views.mdx | 97 +++ .../extend/apps/logic/connections.mdx | 192 +++++ .../extend/apps/logic/logic-functions.mdx | 514 +++++++++++ .../developers/extend/apps/logic/overview.mdx | 55 ++ .../extend/apps/logic/skills-and-agents.mdx | 138 +++ .../developers/extend/apps/operations/cli.mdx | 105 +++ .../extend/apps/operations/overview.mdx | 32 + .../extend/apps/operations/publishing.mdx | 294 +++++++ .../apps/operations/sync-and-recovery.mdx | 111 +++ .../extend/apps/operations/testing.mdx | 301 +++++++ .../developers/extend/capabilities/apis.mdx | 2 +- .../self-host/capabilities/docker-compose.mdx | 8 +- .../self-host/capabilities/key-rotation.mdx | 60 ++ .../self-host/capabilities/setup.mdx | 17 +- .../self-host/capabilities/upgrade-guide.mdx | 12 + packages/twenty-docs/l/it/navigation.json | 22 +- .../l/it/user-guide/ai/capabilities/mcp.mdx | 7 +- .../permissions-access-control.mdx | 2 +- .../l/it/user-guide/ai/how-tos/ai-faq.mdx | 2 +- .../l/it/user-guide/ai/overview.mdx | 2 +- .../billing/capabilities/pricing-plans.mdx | 22 +- .../user-guide/calendar-emails/overview.mdx | 27 +- .../dashboards/how-tos/dashboards-faq.mdx | 2 +- .../capabilities/field-mapping.mdx | 12 +- .../capabilities/file-formats.mdx | 2 +- .../how-tos/migrating-from-other-crms.mdx | 2 +- .../migrating-from-self-hosted-to-cloud.mdx | 2 +- .../data-model/capabilities/fields.mdx | 4 + .../capabilities/permissions.mdx | 30 +- .../how-tos/permissions-faq.mdx | 6 +- .../capabilities/domains-settings.mdx | 15 +- .../capabilities/member-management.mdx | 2 +- .../settings/how-tos/settings-faq.mdx | 4 +- .../l/it/user-guide/settings/overview.mdx | 2 +- .../show-expected-amount-in-pipeline.mdx | 4 +- .../how-tos/track-time-in-stage.mdx | 2 +- .../capabilities/workflow-actions.mdx | 2 +- .../how-tos/need-more-help/workflows-faq.mdx | 2 +- .../backend-development/custom-objects.mdx | 6 +- .../backend-development/server-commands.mdx | 8 + .../backend-development/zapier.mdx | 4 +- .../capabilities/bug-and-requests.mdx | 1 + .../best-practices-front.mdx | 13 +- .../folder-architecture-front.mdx | 3 +- .../frontend-commands.mdx | 3 +- .../frontend-development/hotkeys.mdx | 6 +- .../frontend-development/style-guide.mdx | 11 +- .../frontend-development/work-with-figma.mdx | 4 +- .../contribute/capabilities/local-setup.mdx | 196 ++--- .../l/ko/developers/contribute/commands.mdx | 77 ++ .../l/ko/developers/contribute/contribute.mdx | 1 - .../ko/developers/contribute/style-guide.mdx | 176 ++++ .../l/ko/developers/extend/api.mdx | 55 ++ .../extend/apps/config/application.mdx | 64 ++ .../extend/apps/config/install-hooks.mdx | 206 +++++ .../extend/apps/config/overview.mdx | 51 ++ .../extend/apps/config/public-assets.mdx | 67 ++ .../developers/extend/apps/config/roles.mdx | 94 +++ .../extend/apps/data/extending-objects.mdx | 50 ++ .../developers/extend/apps/data/objects.mdx | 104 +++ .../developers/extend/apps/data/overview.mdx | 97 +++ .../developers/extend/apps/data/relations.mdx | 160 ++++ .../extend/apps/getting-started/concepts.mdx | 101 +++ .../apps/getting-started/local-server.mdx | 87 ++ .../getting-started/project-structure.mdx | 61 ++ .../apps/getting-started/quick-start.mdx | 176 ++++ .../apps/getting-started/scaffolding.mdx | 58 ++ .../apps/getting-started/troubleshooting.mdx | 14 + .../extend/apps/layout/command-menu-items.mdx | 148 ++++ .../extend/apps/layout/front-components.mdx | 545 ++++++++++++ .../apps/layout/navigation-menu-items.mdx | 44 + .../extend/apps/layout/overview.mdx | 56 ++ .../extend/apps/layout/page-layouts.mdx | 132 +++ .../developers/extend/apps/layout/views.mdx | 97 +++ .../extend/apps/logic/connections.mdx | 192 +++++ .../extend/apps/logic/logic-functions.mdx | 514 +++++++++++ .../developers/extend/apps/logic/overview.mdx | 55 ++ .../extend/apps/logic/skills-and-agents.mdx | 138 +++ .../developers/extend/apps/operations/cli.mdx | 105 +++ .../extend/apps/operations/overview.mdx | 32 + .../extend/apps/operations/publishing.mdx | 294 +++++++ .../apps/operations/sync-and-recovery.mdx | 111 +++ .../extend/apps/operations/testing.mdx | 301 +++++++ .../developers/extend/capabilities/apis.mdx | 8 +- .../extend/capabilities/webhooks.mdx | 2 +- .../l/ko/developers/extend/extend.mdx | 13 +- .../l/ko/developers/extend/oauth.mdx | 189 +++++ .../l/ko/developers/extend/webhooks.mdx | 117 +++ .../l/ko/developers/introduction.mdx | 33 +- .../self-host/capabilities/docker-compose.mdx | 15 +- .../self-host/capabilities/key-rotation.mdx | 60 ++ .../self-host/capabilities/setup.mdx | 155 ++-- .../capabilities/troubleshooting.mdx | 7 +- .../self-host/capabilities/upgrade-guide.mdx | 388 ++------- packages/twenty-docs/l/ko/navigation.json | 145 ++-- .../l/ko/twenty-ui/display/app-tooltip.mdx | 123 +-- .../l/ko/twenty-ui/display/checkmark.mdx | 84 +- .../l/ko/twenty-ui/display/chip.mdx | 144 ++-- .../l/ko/twenty-ui/display/icons.mdx | 93 +- .../l/ko/twenty-ui/display/soon-pill.mdx | 1 - .../l/ko/twenty-ui/display/tag.mdx | 57 +- .../l/ko/twenty-ui/input/block-editor.mdx | 36 +- .../l/ko/twenty-ui/input/buttons.mdx | 796 ++++++++++-------- .../l/ko/twenty-ui/input/checkbox.mdx | 65 +- .../l/ko/twenty-ui/input/color-scheme.mdx | 91 +- .../l/ko/twenty-ui/input/icon-picker.mdx | 73 +- .../l/ko/twenty-ui/input/image-input.mdx | 45 +- .../l/ko/twenty-ui/input/radio.mdx | 147 ++-- .../l/ko/twenty-ui/input/select.mdx | 67 +- .../twenty-docs/l/ko/twenty-ui/input/text.mdx | 216 +++-- .../l/ko/twenty-ui/input/toggle.mdx | 54 +- .../l/ko/twenty-ui/introduction.mdx | 1 + .../twenty-docs/l/ko/twenty-ui/navigation.mdx | 1 + .../l/ko/twenty-ui/navigation/breadcrumb.mdx | 56 +- .../l/ko/twenty-ui/navigation/links.mdx | 242 +++--- .../l/ko/twenty-ui/navigation/menu-item.mdx | 701 ++++++++------- .../twenty-ui/navigation/navigation-bar.mdx | 73 +- .../l/ko/twenty-ui/navigation/step-bar.mdx | 46 +- .../l/ko/twenty-ui/progress-bar.mdx | 102 ++- .../user-guide/ai/capabilities/ai-agents.mdx | 2 +- .../user-guide/ai/capabilities/ai-chatbot.mdx | 2 +- .../l/ko/user-guide/ai/capabilities/mcp.mdx | 141 ++++ .../permissions-access-control.mdx | 4 +- .../l/ko/user-guide/ai/how-tos/ai-faq.mdx | 4 +- .../l/ko/user-guide/ai/overview.mdx | 4 +- .../billing/capabilities/credits.mdx | 27 +- .../billing/capabilities/pricing-plans.mdx | 19 +- .../billing/how-tos/billing-faq.mdx | 114 +-- .../l/ko/user-guide/billing/overview.mdx | 2 - .../how-tos/can-i-send-emails-from-twenty.mdx | 2 +- .../user-guide/calendar-emails/overview.mdx | 32 +- .../capabilities/chart-settings.mdx | 16 +- .../dashboards/capabilities/dashboards.mdx | 4 +- .../dashboards/capabilities/widgets.mdx | 16 +- .../dashboards/how-tos/dashboards-faq.mdx | 1 - .../l/ko/user-guide/dashboards/overview.mdx | 10 +- .../capabilities/field-mapping.mdx | 14 +- .../capabilities/file-formats.mdx | 2 +- .../capabilities/import-relations.mdx | 20 +- .../capabilities/uniqueness-constraints.mdx | 4 +- .../how-tos/export-your-data.mdx | 6 +- .../how-tos/fix-import-errors.mdx | 28 +- .../how-tos/import-companies-via-csv.mdx | 18 +- .../how-tos/import-contacts-via-csv.mdx | 26 +- .../how-tos/import-data-via-api.mdx | 18 +- ...port-relations-between-objects-via-csv.mdx | 15 +- .../how-tos/migrating-from-other-crms.mdx | 31 +- .../migrating-from-self-hosted-to-cloud.mdx | 6 +- .../how-tos/prepare-your-csv-files.mdx | 23 +- .../update-existing-records-via-import.mdx | 10 +- .../ko/user-guide/data-migration/overview.mdx | 16 +- .../data-model/capabilities/fields.mdx | 4 + .../capabilities/relation-fields.mdx | 4 +- .../how-tos/create-custom-fields.mdx | 4 +- .../how-tos/create-custom-objects.mdx | 2 +- .../how-tos/create-many-to-many-relations.mdx | 79 +- .../how-tos/create-relation-fields.mdx | 4 +- .../data-model/how-tos/data-model-faq.mdx | 189 +++-- .../l/ko/user-guide/data-model/overview.mdx | 9 +- .../capabilities/what-is-twenty.mdx | 2 +- .../l/ko/user-guide/introduction.mdx | 14 +- .../layout/capabilities/navigation.mdx | 38 + .../layout/capabilities/record-pages.mdx | 61 ++ .../l/ko/user-guide/layout/overview.mdx | 45 + .../capabilities/permissions.mdx | 42 +- .../capabilities/sso-configuration.mdx | 2 +- .../how-tos/permissions-faq.mdx | 164 ++-- .../permissions-access/overview.mdx | 3 - .../capabilities/domains-settings.mdx | 17 +- .../capabilities/member-management.mdx | 6 +- .../capabilities/profile-settings.mdx | 2 +- .../capabilities/updates-settings.mdx | 2 +- .../capabilities/workspace-settings.mdx | 2 +- .../settings/how-tos/settings-faq.mdx | 230 ++--- .../l/ko/user-guide/settings/overview.mdx | 3 +- .../capabilities/table-views.mdx | 2 +- .../capabilities/view-settings.mdx | 4 +- .../create-a-table-view-with-grouping.mdx | 2 +- .../how-tos/set-up-a-sales-pipeline.mdx | 2 +- .../show-expected-amount-in-pipeline.mdx | 6 +- .../how-tos/track-time-in-stage.mdx | 8 +- .../user-guide/views-pipelines/overview.mdx | 5 +- .../send-emails-from-workflows.mdx | 2 +- .../use-branches-in-workflows.mdx | 2 +- .../capabilities/workflow-actions.mdx | 40 +- .../capabilities/workflow-branches.mdx | 4 +- .../capabilities/workflow-credits.mdx | 10 +- .../capabilities/workflow-triggers.mdx | 23 +- .../handle-arrays-in-code-actions.mdx | 4 +- .../bring-product-data-in-twenty.mdx | 4 +- .../bring-typeform-submissions-in-twenty.mdx | 2 +- .../generate-pdf-from-twenty.mdx | 87 +- .../generate-quote-or-invoice-from-twenty.mdx | 2 +- .../set-up-a-webhook-trigger.mdx | 2 +- .../auto-reply-to-inbound-emails.mdx | 121 +++ .../display-number-of-emails-received.mdx | 6 +- .../display-related-record-data.mdx | 4 +- .../crm-automations/formula-fields.mdx | 4 +- .../notify-teammates-of-note-to-review.mdx | 6 +- .../send-email-alerts-with-tasks-due.mdx | 2 +- .../workflow-troubleshooting.mdx | 2 +- .../how-tos/need-more-help/workflows-faq.mdx | 22 +- .../l/ko/user-guide/workflows/overview.mdx | 1 - .../capabilities/field-mapping.mdx | 12 +- .../capabilities/file-formats.mdx | 2 +- .../update-existing-records-via-import.mdx | 4 +- .../capabilities/file-formats.mdx | 2 +- .../handle-arrays-in-code-actions.mdx | 2 +- .../capabilities/field-mapping.mdx | 2 +- .../capabilities/file-formats.mdx | 2 +- .../how-tos/prepare-your-csv-files.mdx | 2 +- .../frontend-development/style-guide.mdx | 2 +- .../twenty-docs/l/tr/twenty-ui/input/text.mdx | 2 +- .../how-tos/prepare-your-csv-files.mdx | 2 +- .../twenty-docs/l/zh/twenty-ui/input/text.mdx | 2 +- .../capabilities/file-formats.mdx | 2 +- .../bring-product-data-in-twenty.mdx | 16 +- 704 files changed, 38268 insertions(+), 9002 deletions(-) create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/config/application.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/config/overview.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/config/public-assets.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/config/roles.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/data/extending-objects.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/data/overview.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/data/relations.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/getting-started/concepts.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/getting-started/local-server.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/getting-started/project-structure.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/layout/front-components.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/layout/overview.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/layout/page-layouts.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/logic/connections.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/logic/skills-and-agents.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/operations/overview.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/operations/publishing.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx create mode 100644 packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx create mode 100644 packages/twenty-docs/l/ar/developers/self-host/capabilities/key-rotation.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/config/application.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/config/overview.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/config/public-assets.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/config/roles.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/data/extending-objects.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/data/overview.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/data/relations.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/getting-started/concepts.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/getting-started/local-server.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/layout/front-components.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/layout/overview.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/layout/page-layouts.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/logic/connections.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/logic/skills-and-agents.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/operations/overview.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx create mode 100644 packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx create mode 100644 packages/twenty-docs/l/cs/developers/self-host/capabilities/key-rotation.mdx create mode 100644 packages/twenty-docs/l/es/developers/contribute/commands.mdx create mode 100644 packages/twenty-docs/l/es/developers/contribute/style-guide.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/api.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/config/application.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/config/overview.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/config/public-assets.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/config/roles.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/data/extending-objects.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/data/overview.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/data/relations.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/getting-started/concepts.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/getting-started/local-server.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/layout/overview.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/layout/page-layouts.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/logic/connections.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/logic/skills-and-agents.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/operations/overview.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/oauth.mdx create mode 100644 packages/twenty-docs/l/es/developers/extend/webhooks.mdx create mode 100644 packages/twenty-docs/l/es/developers/self-host/capabilities/key-rotation.mdx create mode 100644 packages/twenty-docs/l/es/user-guide/ai/capabilities/mcp.mdx create mode 100644 packages/twenty-docs/l/es/user-guide/layout/capabilities/navigation.mdx create mode 100644 packages/twenty-docs/l/es/user-guide/layout/capabilities/record-pages.mdx create mode 100644 packages/twenty-docs/l/es/user-guide/layout/overview.mdx create mode 100644 packages/twenty-docs/l/es/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails.mdx create mode 100644 packages/twenty-docs/l/fr/developers/contribute/commands.mdx create mode 100644 packages/twenty-docs/l/fr/developers/contribute/style-guide.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/api.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/config/application.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/config/install-hooks.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/config/overview.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/config/public-assets.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/config/roles.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/data/extending-objects.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/data/objects.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/data/overview.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/data/relations.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/getting-started/concepts.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/getting-started/local-server.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/getting-started/project-structure.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/getting-started/quick-start.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/getting-started/scaffolding.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/getting-started/troubleshooting.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/layout/command-menu-items.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/layout/front-components.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/layout/navigation-menu-items.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/layout/overview.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/layout/page-layouts.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/layout/views.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/logic/connections.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/logic/logic-functions.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/logic/overview.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/logic/skills-and-agents.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/operations/cli.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/operations/overview.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/operations/publishing.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/operations/sync-and-recovery.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/apps/operations/testing.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/oauth.mdx create mode 100644 packages/twenty-docs/l/fr/developers/extend/webhooks.mdx create mode 100644 packages/twenty-docs/l/fr/developers/self-host/capabilities/key-rotation.mdx create mode 100644 packages/twenty-docs/l/fr/user-guide/ai/capabilities/mcp.mdx create mode 100644 packages/twenty-docs/l/fr/user-guide/layout/capabilities/navigation.mdx create mode 100644 packages/twenty-docs/l/fr/user-guide/layout/capabilities/record-pages.mdx create mode 100644 packages/twenty-docs/l/fr/user-guide/layout/overview.mdx create mode 100644 packages/twenty-docs/l/fr/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/config/application.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/config/install-hooks.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/config/overview.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/config/public-assets.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/config/roles.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/data/extending-objects.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/data/objects.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/data/overview.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/data/relations.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/getting-started/concepts.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/getting-started/local-server.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/getting-started/project-structure.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/getting-started/quick-start.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/getting-started/scaffolding.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/getting-started/troubleshooting.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/layout/command-menu-items.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/layout/front-components.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/layout/navigation-menu-items.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/layout/overview.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/layout/page-layouts.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/layout/views.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/logic/connections.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/logic/logic-functions.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/logic/overview.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/logic/skills-and-agents.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/operations/cli.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/operations/overview.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/operations/publishing.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/operations/sync-and-recovery.mdx create mode 100644 packages/twenty-docs/l/it/developers/extend/apps/operations/testing.mdx create mode 100644 packages/twenty-docs/l/it/developers/self-host/capabilities/key-rotation.mdx create mode 100644 packages/twenty-docs/l/ko/developers/contribute/commands.mdx create mode 100644 packages/twenty-docs/l/ko/developers/contribute/style-guide.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/api.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/config/application.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/config/install-hooks.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/config/overview.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/config/public-assets.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/config/roles.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/data/extending-objects.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/data/objects.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/data/overview.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/data/relations.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/getting-started/concepts.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/getting-started/local-server.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/getting-started/project-structure.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/getting-started/quick-start.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/getting-started/scaffolding.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/getting-started/troubleshooting.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/layout/command-menu-items.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/layout/front-components.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/layout/navigation-menu-items.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/layout/overview.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/layout/page-layouts.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/layout/views.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/logic/connections.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/logic/logic-functions.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/logic/overview.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/logic/skills-and-agents.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/operations/cli.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/operations/overview.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/operations/publishing.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/operations/sync-and-recovery.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/apps/operations/testing.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/oauth.mdx create mode 100644 packages/twenty-docs/l/ko/developers/extend/webhooks.mdx create mode 100644 packages/twenty-docs/l/ko/developers/self-host/capabilities/key-rotation.mdx create mode 100644 packages/twenty-docs/l/ko/user-guide/ai/capabilities/mcp.mdx create mode 100644 packages/twenty-docs/l/ko/user-guide/layout/capabilities/navigation.mdx create mode 100644 packages/twenty-docs/l/ko/user-guide/layout/capabilities/record-pages.mdx create mode 100644 packages/twenty-docs/l/ko/user-guide/layout/overview.mdx create mode 100644 packages/twenty-docs/l/ko/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails.mdx diff --git a/packages/twenty-docs/docs.json b/packages/twenty-docs/docs.json index 939eff51cb..e512d59155 100644 --- a/packages/twenty-docs/docs.json +++ b/packages/twenty-docs/docs.json @@ -469,10 +469,10 @@ "language": "fr", "tabs": [ { - "tab": "Getting Started", + "tab": "Prise en main", "groups": [ { - "group": "Welcome", + "group": "Bienvenue", "pages": [ "getting-started/introduction", "getting-started/key-features", @@ -480,7 +480,7 @@ ] }, { - "group": "Core Concepts", + "group": "Concepts clés", "pages": [ "getting-started/core-concepts/data-model", "getting-started/core-concepts/layout", @@ -498,7 +498,7 @@ "tab": "Guide de l'utilisateur", "groups": [ { - "group": "Overview", + "group": "Vue d'ensemble", "pages": [ "l/fr/user-guide/introduction" ] @@ -509,7 +509,7 @@ "pages": [ "l/fr/user-guide/data-model/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/data-model/capabilities/objects", "l/fr/user-guide/data-model/capabilities/fields", @@ -535,7 +535,7 @@ "pages": [ "l/fr/user-guide/data-migration/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/data-migration/capabilities/file-formats", "l/fr/user-guide/data-migration/capabilities/field-mapping", @@ -567,7 +567,7 @@ "pages": [ "l/fr/user-guide/calendar-emails/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/calendar-emails/capabilities/mailbox", "l/fr/user-guide/calendar-emails/capabilities/calendar" @@ -592,7 +592,7 @@ "pages": [ "l/fr/user-guide/workflows/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/workflows/capabilities/workflow-triggers", "l/fr/user-guide/workflows/capabilities/workflow-actions", @@ -618,7 +618,7 @@ "l/fr/user-guide/workflows/how-tos/crm-automations/display-related-record-data", "l/fr/user-guide/workflows/how-tos/crm-automations/closed-won-automations", "l/fr/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities", - "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" + "l/fr/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" ] }, { @@ -655,7 +655,7 @@ "pages": [ "l/fr/user-guide/ai/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/ai/capabilities/ai-chatbot", "l/fr/user-guide/ai/capabilities/ai-agents", @@ -671,16 +671,16 @@ ] }, { - "group": "Layout", + "group": "Disposition", "icon": "table-columns", "pages": [ - "user-guide/layout/overview", + "l/fr/user-guide/layout/overview", { - "group": "Reference", + "group": "Référence", "pages": [ - "user-guide/layout/capabilities/navigation", + "l/fr/user-guide/layout/capabilities/navigation", { - "group": "Views", + "group": "Vues", "pages": [ "l/fr/user-guide/views-pipelines/capabilities/table-views", "l/fr/user-guide/views-pipelines/capabilities/kanban-views", @@ -690,11 +690,11 @@ "l/fr/user-guide/views-pipelines/capabilities/view-settings" ] }, - "user-guide/layout/capabilities/record-pages" + "l/fr/user-guide/layout/capabilities/record-pages" ] }, { - "group": "How-Tos", + "group": "Guides pratiques", "pages": [ "l/fr/user-guide/views-pipelines/how-tos/create-a-table-view-with-grouping", "l/fr/user-guide/views-pipelines/how-tos/create-a-kanban-view-for-projects", @@ -713,7 +713,7 @@ "pages": [ "l/fr/user-guide/dashboards/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/dashboards/capabilities/dashboards", "l/fr/user-guide/dashboards/capabilities/widgets", @@ -735,7 +735,7 @@ "pages": [ "l/fr/user-guide/permissions-access/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/permissions-access/capabilities/permissions", "l/fr/user-guide/permissions-access/capabilities/sso-configuration" @@ -755,7 +755,7 @@ "pages": [ "l/fr/user-guide/billing/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/billing/capabilities/pricing-plans", "l/fr/user-guide/billing/capabilities/credits" @@ -775,7 +775,7 @@ "pages": [ "l/fr/user-guide/settings/overview", { - "group": "Reference", + "group": "Référence", "pages": [ "l/fr/user-guide/settings/capabilities/workspace-settings", "l/fr/user-guide/settings/capabilities/member-management", @@ -799,72 +799,72 @@ "tab": "Développeurs", "groups": [ { - "group": "Overview", + "group": "Vue d'ensemble", "pages": [ "l/fr/developers/introduction" ] }, { - "group": "Apps", + "group": "Applications", "pages": [ { - "group": "Getting Started", + "group": "Prise en main", "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" + "l/fr/developers/extend/apps/getting-started/quick-start", + "l/fr/developers/extend/apps/getting-started/concepts", + "l/fr/developers/extend/apps/getting-started/project-structure", + "l/fr/developers/extend/apps/getting-started/local-server", + "l/fr/developers/extend/apps/getting-started/scaffolding", + "l/fr/developers/extend/apps/getting-started/troubleshooting" ] }, { - "group": "Config", + "group": "Configuration", "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" + "l/fr/developers/extend/apps/config/overview", + "l/fr/developers/extend/apps/config/application", + "l/fr/developers/extend/apps/config/roles", + "l/fr/developers/extend/apps/config/install-hooks", + "l/fr/developers/extend/apps/config/public-assets" ] }, { - "group": "Data", + "group": "Données", "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" + "l/fr/developers/extend/apps/data/overview", + "l/fr/developers/extend/apps/data/objects", + "l/fr/developers/extend/apps/data/extending-objects", + "l/fr/developers/extend/apps/data/relations" ] }, { - "group": "Logic", + "group": "Logique", "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" + "l/fr/developers/extend/apps/logic/overview", + "l/fr/developers/extend/apps/logic/logic-functions", + "l/fr/developers/extend/apps/logic/skills-and-agents", + "l/fr/developers/extend/apps/logic/connections" ] }, { - "group": "Layout", + "group": "Disposition", "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" + "l/fr/developers/extend/apps/layout/overview", + "l/fr/developers/extend/apps/layout/views", + "l/fr/developers/extend/apps/layout/navigation-menu-items", + "l/fr/developers/extend/apps/layout/page-layouts", + "l/fr/developers/extend/apps/layout/front-components", + "l/fr/developers/extend/apps/layout/command-menu-items" ] }, { - "group": "Operations", + "group": "Opérations", "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" + "l/fr/developers/extend/apps/operations/overview", + "l/fr/developers/extend/apps/operations/cli", + "l/fr/developers/extend/apps/operations/sync-and-recovery", + "l/fr/developers/extend/apps/operations/testing", + "l/fr/developers/extend/apps/operations/publishing" ] } ] @@ -872,9 +872,9 @@ { "group": "API", "pages": [ - "developers/extend/api", - "developers/extend/webhooks", - "developers/extend/oauth" + "l/fr/developers/extend/api", + "l/fr/developers/extend/webhooks", + "l/fr/developers/extend/oauth" ] }, { @@ -890,8 +890,8 @@ "group": "Contribuer", "pages": [ "l/fr/developers/contribute/capabilities/local-setup", - "developers/contribute/commands", - "developers/contribute/style-guide" + "l/fr/developers/contribute/commands", + "l/fr/developers/contribute/style-guide" ] } ] @@ -1241,63 +1241,63 @@ "group": "التطبيقات", "pages": [ { - "group": "Getting Started", + "group": "البدء", "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" + "l/ar/developers/extend/apps/getting-started/quick-start", + "l/ar/developers/extend/apps/getting-started/concepts", + "l/ar/developers/extend/apps/getting-started/project-structure", + "l/ar/developers/extend/apps/getting-started/local-server", + "l/ar/developers/extend/apps/getting-started/scaffolding", + "l/ar/developers/extend/apps/getting-started/troubleshooting" ] }, { - "group": "Config", + "group": "التهيئة", "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" + "l/ar/developers/extend/apps/config/overview", + "l/ar/developers/extend/apps/config/application", + "l/ar/developers/extend/apps/config/roles", + "l/ar/developers/extend/apps/config/install-hooks", + "l/ar/developers/extend/apps/config/public-assets" ] }, { - "group": "Data", + "group": "بيانات", "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" + "l/ar/developers/extend/apps/data/overview", + "l/ar/developers/extend/apps/data/objects", + "l/ar/developers/extend/apps/data/extending-objects", + "l/ar/developers/extend/apps/data/relations" ] }, { - "group": "Logic", + "group": "المنطق", "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" + "l/ar/developers/extend/apps/logic/overview", + "l/ar/developers/extend/apps/logic/logic-functions", + "l/ar/developers/extend/apps/logic/skills-and-agents", + "l/ar/developers/extend/apps/logic/connections" ] }, { - "group": "Layout", + "group": "التخطيط", "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" + "l/ar/developers/extend/apps/layout/overview", + "l/ar/developers/extend/apps/layout/views", + "l/ar/developers/extend/apps/layout/navigation-menu-items", + "l/ar/developers/extend/apps/layout/page-layouts", + "l/ar/developers/extend/apps/layout/front-components", + "l/ar/developers/extend/apps/layout/command-menu-items" ] }, { - "group": "Operations", + "group": "العمليات", "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" + "l/ar/developers/extend/apps/operations/overview", + "l/ar/developers/extend/apps/operations/cli", + "l/ar/developers/extend/apps/operations/sync-and-recovery", + "l/ar/developers/extend/apps/operations/testing", + "l/ar/developers/extend/apps/operations/publishing" ] } ] @@ -1674,63 +1674,63 @@ "group": "Aplikace", "pages": [ { - "group": "Getting Started", + "group": "Začínáme", "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" + "l/cs/developers/extend/apps/getting-started/quick-start", + "l/cs/developers/extend/apps/getting-started/concepts", + "l/cs/developers/extend/apps/getting-started/project-structure", + "l/cs/developers/extend/apps/getting-started/local-server", + "l/cs/developers/extend/apps/getting-started/scaffolding", + "l/cs/developers/extend/apps/getting-started/troubleshooting" ] }, { - "group": "Config", + "group": "Konfigurace", "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" + "l/cs/developers/extend/apps/config/overview", + "l/cs/developers/extend/apps/config/application", + "l/cs/developers/extend/apps/config/roles", + "l/cs/developers/extend/apps/config/install-hooks", + "l/cs/developers/extend/apps/config/public-assets" ] }, { "group": "Data", "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" + "l/cs/developers/extend/apps/data/overview", + "l/cs/developers/extend/apps/data/objects", + "l/cs/developers/extend/apps/data/extending-objects", + "l/cs/developers/extend/apps/data/relations" ] }, { - "group": "Logic", + "group": "Logika", "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" + "l/cs/developers/extend/apps/logic/overview", + "l/cs/developers/extend/apps/logic/logic-functions", + "l/cs/developers/extend/apps/logic/skills-and-agents", + "l/cs/developers/extend/apps/logic/connections" ] }, { - "group": "Layout", + "group": "Rozvržení", "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" + "l/cs/developers/extend/apps/layout/overview", + "l/cs/developers/extend/apps/layout/views", + "l/cs/developers/extend/apps/layout/navigation-menu-items", + "l/cs/developers/extend/apps/layout/page-layouts", + "l/cs/developers/extend/apps/layout/front-components", + "l/cs/developers/extend/apps/layout/command-menu-items" ] }, { - "group": "Operations", + "group": "Operace", "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" + "l/cs/developers/extend/apps/operations/overview", + "l/cs/developers/extend/apps/operations/cli", + "l/cs/developers/extend/apps/operations/sync-and-recovery", + "l/cs/developers/extend/apps/operations/testing", + "l/cs/developers/extend/apps/operations/publishing" ] } ] @@ -2201,10 +2201,10 @@ "language": "es", "tabs": [ { - "tab": "Getting Started", + "tab": "Primeros pasos", "groups": [ { - "group": "Welcome", + "group": "Bienvenido", "pages": [ "getting-started/introduction", "getting-started/key-features", @@ -2212,7 +2212,7 @@ ] }, { - "group": "Core Concepts", + "group": "Conceptos clave", "pages": [ "getting-started/core-concepts/data-model", "getting-started/core-concepts/layout", @@ -2230,7 +2230,7 @@ "tab": "Guía de usuario", "groups": [ { - "group": "Overview", + "group": "Resumen", "pages": [ "l/es/user-guide/introduction" ] @@ -2241,7 +2241,7 @@ "pages": [ "l/es/user-guide/data-model/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/data-model/capabilities/objects", "l/es/user-guide/data-model/capabilities/fields", @@ -2267,7 +2267,7 @@ "pages": [ "l/es/user-guide/data-migration/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/data-migration/capabilities/file-formats", "l/es/user-guide/data-migration/capabilities/field-mapping", @@ -2299,7 +2299,7 @@ "pages": [ "l/es/user-guide/calendar-emails/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/calendar-emails/capabilities/mailbox", "l/es/user-guide/calendar-emails/capabilities/calendar" @@ -2324,7 +2324,7 @@ "pages": [ "l/es/user-guide/workflows/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/workflows/capabilities/workflow-triggers", "l/es/user-guide/workflows/capabilities/workflow-actions", @@ -2350,7 +2350,7 @@ "l/es/user-guide/workflows/how-tos/crm-automations/display-related-record-data", "l/es/user-guide/workflows/how-tos/crm-automations/closed-won-automations", "l/es/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities", - "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" + "l/es/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" ] }, { @@ -2387,7 +2387,7 @@ "pages": [ "l/es/user-guide/ai/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/ai/capabilities/ai-chatbot", "l/es/user-guide/ai/capabilities/ai-agents", @@ -2403,16 +2403,16 @@ ] }, { - "group": "Layout", + "group": "Diseño", "icon": "table-columns", "pages": [ - "user-guide/layout/overview", + "l/es/user-guide/layout/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ - "user-guide/layout/capabilities/navigation", + "l/es/user-guide/layout/capabilities/navigation", { - "group": "Views", + "group": "Vistas", "pages": [ "l/es/user-guide/views-pipelines/capabilities/table-views", "l/es/user-guide/views-pipelines/capabilities/kanban-views", @@ -2422,11 +2422,11 @@ "l/es/user-guide/views-pipelines/capabilities/view-settings" ] }, - "user-guide/layout/capabilities/record-pages" + "l/es/user-guide/layout/capabilities/record-pages" ] }, { - "group": "How-Tos", + "group": "Guías prácticas", "pages": [ "l/es/user-guide/views-pipelines/how-tos/create-a-table-view-with-grouping", "l/es/user-guide/views-pipelines/how-tos/create-a-kanban-view-for-projects", @@ -2445,7 +2445,7 @@ "pages": [ "l/es/user-guide/dashboards/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/dashboards/capabilities/dashboards", "l/es/user-guide/dashboards/capabilities/widgets", @@ -2467,7 +2467,7 @@ "pages": [ "l/es/user-guide/permissions-access/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/permissions-access/capabilities/permissions", "l/es/user-guide/permissions-access/capabilities/sso-configuration" @@ -2487,7 +2487,7 @@ "pages": [ "l/es/user-guide/billing/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/billing/capabilities/pricing-plans", "l/es/user-guide/billing/capabilities/credits" @@ -2507,7 +2507,7 @@ "pages": [ "l/es/user-guide/settings/overview", { - "group": "Reference", + "group": "Referencia", "pages": [ "l/es/user-guide/settings/capabilities/workspace-settings", "l/es/user-guide/settings/capabilities/member-management", @@ -2531,72 +2531,72 @@ "tab": "Desarrolladores", "groups": [ { - "group": "Overview", + "group": "Resumen", "pages": [ "l/es/developers/introduction" ] }, { - "group": "Apps", + "group": "Aplicaciones", "pages": [ { - "group": "Getting Started", + "group": "Primeros pasos", "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" + "l/es/developers/extend/apps/getting-started/quick-start", + "l/es/developers/extend/apps/getting-started/concepts", + "l/es/developers/extend/apps/getting-started/project-structure", + "l/es/developers/extend/apps/getting-started/local-server", + "l/es/developers/extend/apps/getting-started/scaffolding", + "l/es/developers/extend/apps/getting-started/troubleshooting" ] }, { - "group": "Config", + "group": "Configuración", "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" + "l/es/developers/extend/apps/config/overview", + "l/es/developers/extend/apps/config/application", + "l/es/developers/extend/apps/config/roles", + "l/es/developers/extend/apps/config/install-hooks", + "l/es/developers/extend/apps/config/public-assets" ] }, { - "group": "Data", + "group": "Datos", "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" + "l/es/developers/extend/apps/data/overview", + "l/es/developers/extend/apps/data/objects", + "l/es/developers/extend/apps/data/extending-objects", + "l/es/developers/extend/apps/data/relations" ] }, { - "group": "Logic", + "group": "Lógica", "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" + "l/es/developers/extend/apps/logic/overview", + "l/es/developers/extend/apps/logic/logic-functions", + "l/es/developers/extend/apps/logic/skills-and-agents", + "l/es/developers/extend/apps/logic/connections" ] }, { - "group": "Layout", + "group": "Diseño", "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" + "l/es/developers/extend/apps/layout/overview", + "l/es/developers/extend/apps/layout/views", + "l/es/developers/extend/apps/layout/navigation-menu-items", + "l/es/developers/extend/apps/layout/page-layouts", + "l/es/developers/extend/apps/layout/front-components", + "l/es/developers/extend/apps/layout/command-menu-items" ] }, { - "group": "Operations", + "group": "Operaciones", "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" + "l/es/developers/extend/apps/operations/overview", + "l/es/developers/extend/apps/operations/cli", + "l/es/developers/extend/apps/operations/sync-and-recovery", + "l/es/developers/extend/apps/operations/testing", + "l/es/developers/extend/apps/operations/publishing" ] } ] @@ -2604,9 +2604,9 @@ { "group": "API", "pages": [ - "developers/extend/api", - "developers/extend/webhooks", - "developers/extend/oauth" + "l/es/developers/extend/api", + "l/es/developers/extend/webhooks", + "l/es/developers/extend/oauth" ] }, { @@ -2622,8 +2622,8 @@ "group": "Contribuir", "pages": [ "l/es/developers/contribute/capabilities/local-setup", - "developers/contribute/commands", - "developers/contribute/style-guide" + "l/es/developers/contribute/commands", + "l/es/developers/contribute/style-guide" ] } ] @@ -2973,63 +2973,63 @@ "group": "App", "pages": [ { - "group": "Getting Started", + "group": "Per iniziare", "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" + "l/it/developers/extend/apps/getting-started/quick-start", + "l/it/developers/extend/apps/getting-started/concepts", + "l/it/developers/extend/apps/getting-started/project-structure", + "l/it/developers/extend/apps/getting-started/local-server", + "l/it/developers/extend/apps/getting-started/scaffolding", + "l/it/developers/extend/apps/getting-started/troubleshooting" ] }, { - "group": "Config", + "group": "Configurazione", "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" + "l/it/developers/extend/apps/config/overview", + "l/it/developers/extend/apps/config/application", + "l/it/developers/extend/apps/config/roles", + "l/it/developers/extend/apps/config/install-hooks", + "l/it/developers/extend/apps/config/public-assets" ] }, { - "group": "Data", + "group": "Dati", "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" + "l/it/developers/extend/apps/data/overview", + "l/it/developers/extend/apps/data/objects", + "l/it/developers/extend/apps/data/extending-objects", + "l/it/developers/extend/apps/data/relations" ] }, { - "group": "Logic", + "group": "Logica", "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" + "l/it/developers/extend/apps/logic/overview", + "l/it/developers/extend/apps/logic/logic-functions", + "l/it/developers/extend/apps/logic/skills-and-agents", + "l/it/developers/extend/apps/logic/connections" ] }, { "group": "Layout", "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" + "l/it/developers/extend/apps/layout/overview", + "l/it/developers/extend/apps/layout/views", + "l/it/developers/extend/apps/layout/navigation-menu-items", + "l/it/developers/extend/apps/layout/page-layouts", + "l/it/developers/extend/apps/layout/front-components", + "l/it/developers/extend/apps/layout/command-menu-items" ] }, { - "group": "Operations", + "group": "Operazioni", "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" + "l/it/developers/extend/apps/operations/overview", + "l/it/developers/extend/apps/operations/cli", + "l/it/developers/extend/apps/operations/sync-and-recovery", + "l/it/developers/extend/apps/operations/testing", + "l/it/developers/extend/apps/operations/publishing" ] } ] @@ -3500,10 +3500,10 @@ "language": "ko", "tabs": [ { - "tab": "Getting Started", + "tab": "시작하기", "groups": [ { - "group": "Welcome", + "group": "환영합니다", "pages": [ "getting-started/introduction", "getting-started/key-features", @@ -3511,7 +3511,7 @@ ] }, { - "group": "Core Concepts", + "group": "핵심 개념", "pages": [ "getting-started/core-concepts/data-model", "getting-started/core-concepts/layout", @@ -3529,7 +3529,7 @@ "tab": "사용자 안내서", "groups": [ { - "group": "Overview", + "group": "개요", "pages": [ "l/ko/user-guide/introduction" ] @@ -3540,7 +3540,7 @@ "pages": [ "l/ko/user-guide/data-model/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/data-model/capabilities/objects", "l/ko/user-guide/data-model/capabilities/fields", @@ -3566,7 +3566,7 @@ "pages": [ "l/ko/user-guide/data-migration/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/data-migration/capabilities/file-formats", "l/ko/user-guide/data-migration/capabilities/field-mapping", @@ -3598,7 +3598,7 @@ "pages": [ "l/ko/user-guide/calendar-emails/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/calendar-emails/capabilities/mailbox", "l/ko/user-guide/calendar-emails/capabilities/calendar" @@ -3623,7 +3623,7 @@ "pages": [ "l/ko/user-guide/workflows/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/workflows/capabilities/workflow-triggers", "l/ko/user-guide/workflows/capabilities/workflow-actions", @@ -3649,7 +3649,7 @@ "l/ko/user-guide/workflows/how-tos/crm-automations/display-related-record-data", "l/ko/user-guide/workflows/how-tos/crm-automations/closed-won-automations", "l/ko/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities", - "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" + "l/ko/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails" ] }, { @@ -3686,7 +3686,7 @@ "pages": [ "l/ko/user-guide/ai/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/ai/capabilities/ai-chatbot", "l/ko/user-guide/ai/capabilities/ai-agents", @@ -3702,16 +3702,16 @@ ] }, { - "group": "Layout", + "group": "레이아웃", "icon": "table-columns", "pages": [ - "user-guide/layout/overview", + "l/ko/user-guide/layout/overview", { - "group": "Reference", + "group": "참고", "pages": [ - "user-guide/layout/capabilities/navigation", + "l/ko/user-guide/layout/capabilities/navigation", { - "group": "Views", + "group": "보기", "pages": [ "l/ko/user-guide/views-pipelines/capabilities/table-views", "l/ko/user-guide/views-pipelines/capabilities/kanban-views", @@ -3721,11 +3721,11 @@ "l/ko/user-guide/views-pipelines/capabilities/view-settings" ] }, - "user-guide/layout/capabilities/record-pages" + "l/ko/user-guide/layout/capabilities/record-pages" ] }, { - "group": "How-Tos", + "group": "사용 방법", "pages": [ "l/ko/user-guide/views-pipelines/how-tos/create-a-table-view-with-grouping", "l/ko/user-guide/views-pipelines/how-tos/create-a-kanban-view-for-projects", @@ -3744,7 +3744,7 @@ "pages": [ "l/ko/user-guide/dashboards/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/dashboards/capabilities/dashboards", "l/ko/user-guide/dashboards/capabilities/widgets", @@ -3766,7 +3766,7 @@ "pages": [ "l/ko/user-guide/permissions-access/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/permissions-access/capabilities/permissions", "l/ko/user-guide/permissions-access/capabilities/sso-configuration" @@ -3786,7 +3786,7 @@ "pages": [ "l/ko/user-guide/billing/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/billing/capabilities/pricing-plans", "l/ko/user-guide/billing/capabilities/credits" @@ -3806,7 +3806,7 @@ "pages": [ "l/ko/user-guide/settings/overview", { - "group": "Reference", + "group": "참고", "pages": [ "l/ko/user-guide/settings/capabilities/workspace-settings", "l/ko/user-guide/settings/capabilities/member-management", @@ -3830,72 +3830,72 @@ "tab": "개발자", "groups": [ { - "group": "Overview", + "group": "개요", "pages": [ "l/ko/developers/introduction" ] }, { - "group": "Apps", + "group": "앱", "pages": [ { - "group": "Getting Started", + "group": "시작하기", "pages": [ - "developers/extend/apps/getting-started/quick-start", - "developers/extend/apps/getting-started/concepts", - "developers/extend/apps/getting-started/project-structure", - "developers/extend/apps/getting-started/local-server", - "developers/extend/apps/getting-started/scaffolding", - "developers/extend/apps/getting-started/troubleshooting" + "l/ko/developers/extend/apps/getting-started/quick-start", + "l/ko/developers/extend/apps/getting-started/concepts", + "l/ko/developers/extend/apps/getting-started/project-structure", + "l/ko/developers/extend/apps/getting-started/local-server", + "l/ko/developers/extend/apps/getting-started/scaffolding", + "l/ko/developers/extend/apps/getting-started/troubleshooting" ] }, { - "group": "Config", + "group": "설정", "pages": [ - "developers/extend/apps/config/overview", - "developers/extend/apps/config/application", - "developers/extend/apps/config/roles", - "developers/extend/apps/config/install-hooks", - "developers/extend/apps/config/public-assets" + "l/ko/developers/extend/apps/config/overview", + "l/ko/developers/extend/apps/config/application", + "l/ko/developers/extend/apps/config/roles", + "l/ko/developers/extend/apps/config/install-hooks", + "l/ko/developers/extend/apps/config/public-assets" ] }, { - "group": "Data", + "group": "데이터", "pages": [ - "developers/extend/apps/data/overview", - "developers/extend/apps/data/objects", - "developers/extend/apps/data/extending-objects", - "developers/extend/apps/data/relations" + "l/ko/developers/extend/apps/data/overview", + "l/ko/developers/extend/apps/data/objects", + "l/ko/developers/extend/apps/data/extending-objects", + "l/ko/developers/extend/apps/data/relations" ] }, { - "group": "Logic", + "group": "로직", "pages": [ - "developers/extend/apps/logic/overview", - "developers/extend/apps/logic/logic-functions", - "developers/extend/apps/logic/skills-and-agents", - "developers/extend/apps/logic/connections" + "l/ko/developers/extend/apps/logic/overview", + "l/ko/developers/extend/apps/logic/logic-functions", + "l/ko/developers/extend/apps/logic/skills-and-agents", + "l/ko/developers/extend/apps/logic/connections" ] }, { - "group": "Layout", + "group": "레이아웃", "pages": [ - "developers/extend/apps/layout/overview", - "developers/extend/apps/layout/views", - "developers/extend/apps/layout/navigation-menu-items", - "developers/extend/apps/layout/page-layouts", - "developers/extend/apps/layout/front-components", - "developers/extend/apps/layout/command-menu-items" + "l/ko/developers/extend/apps/layout/overview", + "l/ko/developers/extend/apps/layout/views", + "l/ko/developers/extend/apps/layout/navigation-menu-items", + "l/ko/developers/extend/apps/layout/page-layouts", + "l/ko/developers/extend/apps/layout/front-components", + "l/ko/developers/extend/apps/layout/command-menu-items" ] }, { - "group": "Operations", + "group": "작업", "pages": [ - "developers/extend/apps/operations/overview", - "developers/extend/apps/operations/cli", - "developers/extend/apps/operations/sync-and-recovery", - "developers/extend/apps/operations/testing", - "developers/extend/apps/operations/publishing" + "l/ko/developers/extend/apps/operations/overview", + "l/ko/developers/extend/apps/operations/cli", + "l/ko/developers/extend/apps/operations/sync-and-recovery", + "l/ko/developers/extend/apps/operations/testing", + "l/ko/developers/extend/apps/operations/publishing" ] } ] @@ -3903,9 +3903,9 @@ { "group": "API", "pages": [ - "developers/extend/api", - "developers/extend/webhooks", - "developers/extend/oauth" + "l/ko/developers/extend/api", + "l/ko/developers/extend/webhooks", + "l/ko/developers/extend/oauth" ] }, { @@ -3921,8 +3921,8 @@ "group": "기여", "pages": [ "l/ko/developers/contribute/capabilities/local-setup", - "developers/contribute/commands", - "developers/contribute/style-guide" + "l/ko/developers/contribute/commands", + "l/ko/developers/contribute/style-guide" ] } ] diff --git a/packages/twenty-docs/l/ar/developers/extend/api.mdx b/packages/twenty-docs/l/ar/developers/extend/api.mdx index c93c47e27d..5f1f6dc6ba 100644 --- a/packages/twenty-docs/l/ar/developers/extend/api.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/api.mdx @@ -37,7 +37,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; Authorization: Bearer YOUR_API_KEY ``` -أنشئ مفتاح API من **الإعدادات → واجهات برمجة التطبيقات وخطافات الويب → + Create key**. انسخه فورًا — يُعرَض مرة واحدة فقط. يمكن تقييد نطاق المفاتيح بدور محدد ضمن **الإعدادات → الأدوار → علامة التبويب Assignment** للحد مما يمكنها الوصول إليه. +أنشئ مفتاح API من **الإعدادات → واجهات برمجة التطبيقات وخطافات الويب → + Create key**. انسخه فورًا — يُعرَض مرة واحدة فقط. يمكن تقييد نطاق المفاتيح بدور محدد ضمن **الإعدادات → الأعضاء → الأدوار → علامة التبويب Assignment** للحد مما يمكنها الوصول إليه. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/config/application.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/config/application.mdx new file mode 100644 index 0000000000..553739b0fc --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/config/application.mdx @@ -0,0 +1,64 @@ +--- +title: تكوين التطبيق +description: عرّف هوية تطبيقك، والدور الافتراضي، والمتغيرات، وبيانات التعريف لسوق التطبيقات باستخدام defineApplication. +icon: rocket +--- + +يجب أن يحتوي كل تطبيق على استدعاء واحد فقط لـ `defineApplication`. يحدّد ما يلي: + +* **الهوية** — المعرّف الشامل، واسم العرض، والوصف. +* **الأذونات** — الدور الذي تعمل بموجبه دوال المنطق والمكوّنات الأمامية الخاصة به. +* **المتغيرات** *(اختياري)* — أزواج مفتاح–قيمة تُتاح لكودك كمتغيرات بيئة. +* **خطافات ما قبل التثبيت/ما بعد التثبيت** *(اختياري)* — راجع [Logic Functions](/l/ar/developers/extend/apps/logic/logic-functions). + +```ts src/application-config.ts +import { defineApplication } from 'twenty-sdk/define'; + +export default defineApplication({ + universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d', + displayName: 'My Twenty App', + description: 'My first Twenty app', + applicationVariables: { + DEFAULT_RECIPIENT_NAME: { + universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', + description: 'Default recipient name for postcards', + value: 'Jane Doe', + isSecret: false, + }, + }, +}); +``` + +الملاحظات: + +* حقول `universalIdentifier` هي معرّفات حتمية تملكها أنت. أنشِئها مرة واحدة واحتفظ بها ثابتة عبر عمليات المزامنة. +* `applicationVariables` تصبح متغيرات بيئة لوظائفك ومكوّناتك الأمامية. في وظائف المنطق (على جانب الخادم)، تكون متاحة على شكل `process.env.VARIABLE_NAME`. في المكوّنات الأمامية، استخدم `getApplicationVariable('VARIABLE_NAME')` من `twenty-sdk/front-component`. يتم حقن المتغيّرات المعلَّمة بـ `isSecret: true` في وظائف المنطق فقط. المكوّنات الأمامية تتلقّى المتغيّرات غير السرّية فقط. +* يتم اكتشاف الدور الافتراضي تلقائيًا من ملف الدور المميز بـ [`defineApplicationRole()`](/l/ar/developers/extend/apps/config/roles) — لست بحاجة إلى الإشارة إليه من `defineApplication()`. +* يتم اكتشاف دوال ما قبل التثبيت وما بعده تلقائيًا أثناء بناء البيان — لا حاجة للإشارة إليها في `defineApplication()`. +* لا يزال تمرير `defaultRoleUniversalIdentifier` بشكل صريح مدعومًا من أجل التوافق مع الإصدارات السابقة، ولكنه مُهمل لصالح `defineApplicationRole()`. + +## الدور الافتراضي للوظيفة + +يتحكم الدور المعلن باستخدام [`defineApplicationRole()`](/l/ar/developers/extend/apps/config/roles) في ما يمكن لوظائف منطق التطبيق ومكوّنات الواجهة الوصول إليه: + +* رمز وقت التشغيل المحقون باسم `TWENTY_APP_ACCESS_TOKEN` مستمد من هذا الدور. +* يكون عميل واجهة برمجة التطبيقات مضبوط الأنواع مقيّدًا بالأذونات الممنوحة لذلك الدور. +* اتبع مبدأ أقل امتياز: صرّح فقط عن الأذونات التي تحتاجها دوالك. + +عند إنشاء هيكل لتطبيق جديد، ينشئ CLI ملف دور مبدئي في `src/roles/default-role.ts`. راجع [Roles & Permissions](/l/ar/developers/extend/apps/config/roles) للاطلاع على المرجع الكامل. + +## بيانات التعريف لسوق التطبيقات + +إذا كنت تخطط لـ [نشر تطبيقك](/l/ar/developers/extend/apps/operations/publishing)، فإن هذه الحقول الاختيارية تتحكّم في كيفية ظهوره في السوق: + +| الحقل | الوصف | +| ------------------ | ------------------------------------------------------------------------------------------------------------ | +| `author` | اسم المؤلف أو الشركة | +| `category` | فئة التطبيق لتصفية سوق التطبيقات | +| `logoUrl` | مسار شعار تطبيقك (مثلًا، `public/logo.png`) | +| `screenshots` | مصفوفة لمسارات لقطات الشاشة (مثلًا، `public/screenshot-1.png`) | +| `aboutDescription` | وصف ماركداون أطول لعلامة التبويب "حول". إذا لم يتم تضمينه، يستخدم السوق ملف `README.md` الخاص بالحزمة من npm | +| `websiteUrl` | رابط إلى موقعك الإلكتروني | +| `termsUrl` | رابط إلى شروط الخدمة | +| `emailSupport` | عنوان البريد الإلكتروني للدعم | +| `issueReportUrl` | رابط إلى متتبّع المشاكل | diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx new file mode 100644 index 0000000000..58a76268bb --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/config/install-hooks.mdx @@ -0,0 +1,206 @@ +--- +title: خطافات التثبيت +description: شغّل منطقًا قبل التثبيت أو بعده — لتهيئة البيانات، أو نسخ السجلات احتياطيًا، أو التحقّق من صحة الترقية. +icon: wrench +--- + +خطافات التثبيت هي دوال منطقية خاصة تعمل أثناء دورة حياة التثبيت أو الترقية. تستخدم نفس وقت تشغيل المعالج مثل [دوال المنطق](/l/ar/developers/extend/apps/logic/logic-functions) العادية وتتلقى `InstallPayload`، ولكن يتم التصريح عنها بدوال تعريف خاصة بها — `definePostInstallLogicFunction()` و`definePreInstallLogicFunction()` — وتعمل خارج نموذج المشغّل المعتاد (HTTP، وcron، وأحداث قاعدة البيانات). + +يمكن لكل تطبيق تعريف دالة واحدة على الأكثر لما قبل التثبيت ودالة واحدة على الأكثر لما بعد التثبيت. سيُنتِج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة من أيٍّ منهما. + +``` +┌─────────────────────────────────────────────────────────────┐ +│ install flow │ +│ │ +│ upload package → [pre-install] → metadata migration → │ +│ generate SDK → [post-install] │ +│ │ +│ old schema visible new schema visible │ +└─────────────────────────────────────────────────────────────┘ +``` + + + + +تعمل دالة ما بعد التثبيت تلقائيًا بمجرد انتهاء تثبيت تطبيقك على مساحة عمل. ينفّذه الخادم **بعد** مزامنة البيانات الوصفية للتطبيق وإنشاء عميل SDK، بحيث تكون مساحة العمل جاهزة تمامًا للاستخدام ويكون المخطط الجديد مطبَّقًا. تشمل حالات الاستخدام النموذجية تهيئة البيانات الافتراضية، وإنشاء السجلات الأولية، وتكوين إعدادات مساحة العمل، أو توفير الموارد على خدمات جهات خارجية. + +```ts src/logic-functions/post-install.ts +import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; + +const handler = async (payload: InstallPayload): Promise => { + console.log('Post install logic function executed successfully!', payload.previousVersion); +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210', + name: 'post-install', + description: 'Runs after installation to set up the application.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + shouldRunSynchronously: false, + handler, +}); +``` + +يمكنك أيضًا تنفيذ دالة ما بعد التثبيت يدويًا في أي وقت باستخدام CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec --postInstall +``` + +النقاط الرئيسية: +* تستخدم دوال ما بعد التثبيت `definePostInstallLogicFunction()` — إصدارًا متخصصًا يستبعد إعدادات المُشغِّل (`cronTriggerSettings` و`databaseEventTriggerSettings` و`httpRouteTriggerSettings` و`toolTriggerSettings` و`workflowActionTriggerSettings`). +* يتلقى المعالج `InstallPayload` يحتوي على `{ previousVersion?: string; newVersion: string }` — حيث إن `newVersion` هو الإصدار الجاري تثبيته، و`previousVersion` هو الإصدار الذي كان مُثبّتًا سابقًا (أو `undefined` عند التثبيت الأولي). استخدم هذه القيم للتمييز بين عمليات التثبيت الجديدة والترقيات ولتشغيل منطق الترحيل الخاص بالإصدار. +* **موعد تشغيل الخطاف**: في عمليات التثبيت الجديدة فقط، افتراضيًا. مرّر `shouldRunOnVersionUpgrade: true` إذا كنت تريد تشغيله أيضًا عند ترقية التطبيق من إصدار سابق. عند إغفاله، تكون القيمة الافتراضية للعلم `false`، وتتجاوز الترقيات هذا الخطاف. +* **نموذج التنفيذ — غير متزامن افتراضيًا، والتزامني اختياري**: يتحكّم العلم `shouldRunSynchronously` في كيفية تنفيذ ما بعد التثبيت. + * `shouldRunSynchronously: false` *(الإعداد الافتراضي)* — يتم **إدراج الخطاف في قائمة الرسائل** مع `retryLimit: 3` ويعمل بشكل غير متزامن داخل عامل عمل. يعود ردّ التثبيت بمجرد وضع المهمة في الطابور، لذا فإن معالجًا بطيئًا أو متعطلًا لا يحجب المستدعي. سيُجرِّب العامل إعادة المحاولة حتى ثلاث مرات. **استخدم هذا للمهام طويلة التشغيل** — بَذر مجموعات بيانات كبيرة، استدعاء واجهات برمجة تطبيقات خارجية بطيئة، تهيئة موارد خارجية، أو أي شيء قد يتجاوز نافذة استجابة HTTP المعقولة. + * `shouldRunSynchronously: true` — يُنفّذ الخطاف **ضمن تدفّق التثبيت مباشرةً** (نفس المنفِّذ كما قبل التثبيت). يَحجُب طلب التثبيت حتى ينتهي المعالج، وإذا رمى استثناءً، سيتلقى مستدعي التثبيت `POST_INSTALL_ERROR`. لا توجد محاولات إعادة تلقائية. **استخدم هذا للمهام السريعة التي يجب إكمالها قبل الاستجابة** — مثل إظهار خطأ تحقق للمستخدم، أو إعداد سريع سيعتمد عليه العميل مباشرةً بعد عودة نداء التثبيت. ضع في اعتبارك أن ترحيل البيانات الوصفية يكون قد طُبِّق بالفعل عند تشغيل ما بعد التثبيت، لذلك فإن فشل الوضع المتزامن **لا** يعيد التغييرات على المخطط إلى الوراء — بل يكتفي بإبراز الخطأ. +* تأكّد من أن معالجك قابل للتنفيذ المتكرر دون آثار جانبية. في الوضع غير المتزامن قد تُعيد قائمة الانتظار المحاولة حتى ثلاث مرات؛ وفي أي من الوضعين قد يعمل الخطاف مجددًا أثناء الترقيات عند ضبط `shouldRunOnVersionUpgrade: true`. +* متغيرات البيئة `APPLICATION_ID` و`APP_ACCESS_TOKEN` و`API_URL` متاحة داخل المعالج (كما في أي دالة منطق أخرى)، لذا يمكنك استدعاء واجهة Twenty API باستخدام رمز وصول للتطبيق مقيّد بنطاق تطبيقك. +* يُسمح بدالة ما بعد التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة. +* تُرفَق خصائص الدالة `universalIdentifier` و`shouldRunOnVersionUpgrade` و`shouldRunSynchronously` تلقائيًا ببيان التطبيق ضمن الحقل `postInstallLogicFunction` أثناء عملية البناء — ولا تحتاج إلى الإشارة إليها في [`defineApplication()`](/l/ar/developers/extend/apps/config/application). +* تم تعيين مهلة افتراضية إلى 300 ثانية (5 دقائق) للسماح بمهام الإعداد الأطول مثل تهيئة البيانات. +* **لا يُنفَّذ في وضع التطوير**: عند تسجيل تطبيق محليًا (عبر `yarn twenty dev`)، يتجاوز الخادم تدفّق التثبيت بالكامل ويُزامن الملفات مباشرةً عبر مراقِب CLI — لذا لن يعمل ما بعد التثبيت في وضع التطوير مطلقًا، بغضّ النظر عن `shouldRunSynchronously`. استخدم `yarn twenty dev:function:exec --postInstall` لتشغيله يدويًا على مساحة عمل قيد التشغيل. + + + + +تعمل دالة ما قبل التثبيت تلقائيًا أثناء التثبيت، **قبل تطبيق ترحيل البيانات الوصفية لمساحة العمل**. تتشارك نفس بنية الحمولة مع ما بعد التثبيت (`InstallPayload`)، لكنها موضوعة أبكر في تدفّق التثبيت كي تجهّز حالة يعتمد عليها الترحيل القادم — ومن الاستخدامات الشائعة: نسخ البيانات احتياطيًا، التحقق من التوافق مع المخطط الجديد، أو أرشفة السجلات التي ستُعاد هيكلتها أو ستُحذف. + +```ts src/logic-functions/pre-install.ts +import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; + +const handler = async (payload: InstallPayload): Promise => { + console.log('Pre install logic function executed successfully!', payload.previousVersion); +}; + +export default definePreInstallLogicFunction({ + universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab', + name: 'pre-install', + description: 'Runs before installation to prepare the application.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: true, + handler, +}); +``` + +يمكنك أيضًا تنفيذ دالة ما قبل التثبيت يدويًا في أي وقت باستخدام CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec --preInstall +``` + +النقاط الرئيسية: +* تستخدم دوال ما قبل التثبيت `definePreInstallLogicFunction()` — نفس الإعدادات المتخصصة كما في ما بعد التثبيت، لكنها مرتبطة بموضع مختلف ضمن دورة الحياة. +* يتلقّى كلٌّ من معالجي ما قبل التثبيت وما بعد التثبيت النوع نفسه `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. استورده مرة واحدة وأعد استخدامه لكلا الخطافين. +* **موعد تشغيل الخطاف**: موضوع مباشرةً قبل ترحيل البيانات الوصفية لمساحة العمل (`synchronizeFromManifest`). قبل التنفيذ، يُشغِّل الخادم مزامنة "pared-down sync" ذات طابع إضافي فقط تقوم بتسجيل دالة ما قبل التثبيت للإصدار **الجديد** في البيانات الوصفية لمساحة العمل — دون لمس أي شيء آخر — ثم يُنفّذها. لأن هذه المزامنة «إضافية فقط»، تبقى كائنات وحقول وبيانات الإصدار السابق سليمة عند تشغيل معالجك: يمكنك قراءة حالة ما قبل الترحيل ونسخها احتياطيًا بأمان. +* **نموذج التنفيذ**: يُنفَّذ ما قبل التثبيت **بشكل متزامن** و**يحجب عملية التثبيت**. إذا رمى المعالج استثناءً، تُلغى عملية التثبيت قبل تطبيق أي تغييرات على المخطط — وتبقى مساحة العمل على الإصدار السابق بحالة متّسقة. هذا مقصود: ما قبل التثبيت هو فرصتك الأخيرة لرفض ترقية تنطوي على مخاطر. +* كما هو الحال مع ما بعد التثبيت، يُسمح بدالة ما قبل التثبيت واحدة فقط لكل تطبيق. تُربَط تلقائيًا ببيان التطبيق تحت `preInstallLogicFunction` أثناء عملية البناء. +* **لا يُنفَّذ في وضع التطوير**: كما في ما بعد التثبيت — يتم تجاوز تدفّق التثبيت بالكامل للتطبيقات المسجّلة محليًا، لذا لن يعمل ما قبل التثبيت مطلقًا عند `yarn twenty dev`. استخدم `yarn twenty dev:function:exec --preInstall` لتشغيله يدويًا. + + + + +كلا الخطافين جزء من تدفّق التثبيت نفسه ويتلقّيان نفس `InstallPayload`. الاختلاف يكمن في **موعد** تشغيلهما نسبةً إلى ترحيل البيانات الوصفية لمساحة العمل، وهذا يغيّر البيانات التي يمكنهما التعامل معها بأمان. + +ما قبل التثبيت دائمًا **متزامن** (يحجب التثبيت ويمكنه إحباطه). ما بعد التثبيت **غير متزامن افتراضيًا** — يُدرج على عامل مع محاولات إعادة تلقائية — لكن يمكن التبديل إلى تنفيذ متزامن عبر `shouldRunSynchronously: true`. راجع الأكورديون `definePostInstallLogicFunction` أعلاه لمعرفة متى تستخدم كل وضع. + +**استخدم `post-install` لأي شيء يتطلّب وجود المخطط الجديد.** وهذا هو السيناريو الشائع: + +* بَذر بيانات افتراضية (إنشاء سجلات أولية وعروض افتراضية ومحتوى تجريبي) للكائنات والحقول المضافة حديثًا. +* تسجيل خطافات الويب مع خدمات أطراف ثالثة بعد أن حصل التطبيق على بيانات الاعتماد الخاصة به. +* استدعاء واجهة برمجة التطبيقات الخاصة بك لإكمال إعداد يعتمد على البيانات الوصفية المتزامنة. +* منطق قابل للتنفيذ المتكرر دون آثار جانبية لتحقيق "تأكّد من وجود هذا" والذي ينبغي مواءمة الحالة في كل ترقية — بالاقتران مع `shouldRunOnVersionUpgrade: true`. + +مثال — بَذر سجل `PostCard` افتراضي بعد التثبيت: + +```ts src/logic-functions/post-install.ts +import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +import { createClient } from './generated/client'; + +const handler = async ({ previousVersion }: InstallPayload): Promise => { + if (previousVersion) return; // fresh installs only + + const client = createClient(); + await client.postCard.create({ + data: { title: 'Welcome to Postcard', content: 'Your first card!' }, + }); +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210', + name: 'post-install', + description: 'Seeds a welcome post card after install.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + handler, +}); +``` + +**استخدم `pre-install` عندما قد يُتلف الترحيل أو يدمّر البيانات الحالية.** لأن ما قبل التثبيت يعمل مقابل المخطط *السابق* وفشله يُرجِع الترقية إلى الوراء، فهو المكان المناسب لأي شيء محفوف بالمخاطر: + +* **نسخ البيانات احتياطيًا قبل حذفها أو إعادة هيكلتها** — مثل إزالة حقل في v2 وتحتاج إلى نسخ قيمه إلى حقل آخر أو تصديرها إلى التخزين قبل تشغيل الترحيل. +* **أرشفة السجلات التي سيبطلها قيد جديد** — مثل أن يصبح حقل ما `NOT NULL` وتحتاج أولًا إلى حذف الصفوف ذات القيم الفارغة أو إصلاحها. +* **التحقق من التوافق ورفض الترقية إذا تعذّر ترحيل البيانات الحالية بسلاسة** — ارمِ من داخل المعالج وسيُلغى التثبيت دون تطبيق أي تغييرات. هذا أكثر أمانًا من اكتشاف عدم التوافق في منتصف الترحيل. +* **إعادة تسمية البيانات أو إعادة تعيين مفاتيحها** قبل تغيير في المخطط قد يؤدي إلى فقدان الارتباط. + +مثال — أرشف السجلات قبل ترحيل هدّام: + +```ts src/logic-functions/pre-install.ts +import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +import { createClient } from './generated/client'; + +const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => { + // Only the 1.x → 2.x upgrade drops the legacy `notes` field. + if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) { + return; + } + + const client = createClient(); + const legacyRecords = await client.postCard.findMany({ + where: { notes: { isNotNull: true } }, + }); + + if (legacyRecords.length === 0) return; + + // Copy legacy `notes` into the new `description` field before the migration + // drops the `notes` column. If this fails, the upgrade is aborted and the + // workspace stays on v1 with all data intact. + await Promise.all( + legacyRecords.map((record) => + client.postCard.update({ + where: { id: record.id }, + data: { description: record.notes }, + }), + ), + ); +}; + +export default definePreInstallLogicFunction({ + universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab', + name: 'pre-install', + description: 'Backs up legacy notes into description before the v2 migration.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: true, + handler, +}); +``` + +**قاعدة عامة:** + +| ترغب في... | استخدام | +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | +| بذر بيانات افتراضية، تهيئة مساحة العمل، تسجيل موارد خارجية | `post-install` | +| تشغيل بذر طويل الأمد أو استدعاءات أطراف ثالثة لا ينبغي أن تحجب استجابة التثبيت | `post-install` (الإعداد الافتراضي — `shouldRunSynchronously: false`، مع محاولات إعادة من العامل) | +| تشغيل إعداد سريع سيعتمد عليه المستدعي مباشرةً بعد عودة نداء التثبيت | `post-install` مع `shouldRunSynchronously: true` | +| قراءة البيانات أو نسخها احتياطيًا والتي قد يفقدها الترحيل القادم | `pre-install` | +| رفض ترقية قد تُفسد البيانات الحالية | `pre-install` (ارمِ من المعالج) | +| تنفيذ مواءمة في كل ترقية | `post-install` مع `shouldRunOnVersionUpgrade: true` | +| تنفيذ إعداد لمرة واحدة في التثبيت الأول فقط | `post-install` مع `shouldRunOnVersionUpgrade: false` (الإعداد الافتراضي) | + + +إذا ساورك الشك، فاجعل الافتراضي هو **post-install**. الجأ إلى ما قبل التثبيت فقط عندما يكون الترحيل نفسه هدّامًا وتحتاج إلى التقاط الحالة السابقة قبل أن تزول. + + + + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/config/overview.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/config/overview.mdx new file mode 100644 index 0000000000..651fb95188 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/config/overview.mdx @@ -0,0 +1,51 @@ +--- +title: نظرة عامة +description: قم بتهيئة التطبيق نفسه — هويته، والأذونات الافتراضية، وما الذي يعمل في وقت التثبيت. +icon: screwdriver-wrench +--- + +طبقة **الإعدادات (config layer)** لتطبيق Twenty هي ما يصف التطبيق *للمنصة* — هويته، والأذونات التي يمتلكها، والكود الذي يعمل أثناء التثبيت أو الترقية. هذه التصريحات لا تضيف أشكال بيانات جديدة أو سلوكًا وقت التشغيل؛ بل تخبر Twenty *من هو التطبيق* و*كيفية إعداده*. + +```text +┌────────────────────────────────────────────────────────┐ +│ Application — identity, default role, variables, │ +│ marketplace metadata │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Role — what the app's logic functions can read │ │ +│ │ and write (referenced by Application) │ │ +│ └──────────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────┘ + │ + ▼ (at install / upgrade time) + ┌──────────────────────────────────┐ + │ Pre-install hook │ before metadata migration + └──────────────────────────────────┘ + ┌──────────────────────────────────┐ + │ Post-install hook │ after metadata migration + └──────────────────────────────────┘ +``` + +## في هذا القسم + + + + `defineApplication` — الهوية، الدور الافتراضي، المتغيرات، والبيانات الوصفية لسوق التطبيقات. + + + `defineRole` — حدِّد ما يمكن لوظائف منطق التطبيق قراءته وكتابته. + + + `definePreInstallLogicFunction` و`definePostInstallLogicFunction` — نسخ البيانات احتياطيًا، تهيئة القيم الافتراضية، والتحقق من صحة الترقيات. + + + +## كيفية ترابط الأجزاء + +* **التطبيق (Application)** هو نقطة الدخول. يحتوي كل تطبيق على استدعاء واحد فقط `defineApplication()`، ويشير إلى **دور (Role)** واحد باعتباره الدور الافتراضي له. +* يتحكم **الدور** في ما يمكن لوظائف منطق التطبيق ومكوّنات الواجهة الأمامية قراءته وكتابته. اتبع مبدأ أقل امتياز ممكن: امنح فقط الصلاحيات التي يحتاجها الكود فعليًا. +* تعمل **خطافات التثبيت** أثناء التثبيت أو الترقية — ما قبل التثبيت قبل ترحيل البيانات الوصفية (كي تتمكن من رفض ترقية محفوفة بالمخاطر)، وما بعد التثبيت بعد الترحيل (كي تتمكن من تهيئة بيانات افتراضية وفق المخطط الجديد). + + +تشارك خطافات التثبيت وقت تشغيل [وظيفة المنطق](/l/ar/developers/extend/apps/logic/logic-functions) — نفس توقيع المعالج (handler signature)، ونفس متغيرات البيئة، ونفس عميل واجهة برمجة التطبيقات (typed API client) — لكنها تُصرّح باستخدام دوال تعريف خاصة بها وتوجد خارج نموذج المشغلات العادي (HTTP، و cron، وأحداث قاعدة البيانات). + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/config/public-assets.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/config/public-assets.mdx new file mode 100644 index 0000000000..f1d8e01cff --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/config/public-assets.mdx @@ -0,0 +1,67 @@ +--- +title: الأصول العامة +description: وزّع الملفات الثابتة — الصور والأيقونات والخطوط — مع تطبيقك عبر مجلد public/. +icon: folder-open +--- + +يحتوي مجلد `public/` في جذر تطبيقك على ملفات ثابتة — صور وأيقونات وخطوط وأي أصول أخرى يحتاجها تطبيقك وقت التشغيل. تُدرج هذه الملفات تلقائيًا في عمليات البناء، وتُزامَن أثناء وضع التطوير، وتُرفَع إلى الخادم. + +الملفات الموضوعة في `public/` هي: + +* **متاحة للعامة** — بمجرد مزامنتها إلى الخادم، تُقدَّم الأصول عبر عنوان URL عام. لا حاجة إلى مصادقة للوصول إليها. +* **متاحة في المكوّنات الأمامية** — استخدم عناوين الأصول لعرض الصور أو الأيقونات أو أي وسائط داخل مكوّنات React لديك. +* **متاحة في الدوال المنطقية** — أشِر إلى عناوين الأصول في رسائل البريد الإلكتروني أو استجابات واجهات البرمجة أو أي منطق على جهة الخادم. +* **مستخدمة لبيانات تعريف السوق** — يشير حقلا `logoUrl` و`screenshots` في `defineApplication()` إلى ملفات من هذا المجلد (مثل `public/logo.png`). تُعرَض هذه عند نشر تطبيقك في السوق. +* **تُزامَن تلقائيًا في وضع التطوير** — عند إضافة ملف في `public/` أو تحديثه أو حذفه، تتم مزامنته إلى الخادم تلقائيًا. لا حاجة لإعادة التشغيل. +* **مضمَّنة في عمليات البناء** — يقوم `yarn twenty dev:build` بتجميع جميع الأصول العامة ضمن مخرجات التوزيع. + +## الوصول إلى الأصول العامة باستخدام `getPublicAssetUrl` + +استخدم المساعد `getPublicAssetUrl` من `twenty-sdk` للحصول على العنوان الكامل لملف في دليل `public/` لديك. يعمل ذلك في كلٍ من الدوال المنطقية والمكوّنات الأمامية. + +**في دالة منطقية:** + +```ts src/logic-functions/send-invoice.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const handler = async (): Promise => { + const logoUrl = getPublicAssetUrl('logo.png'); + const invoiceUrl = getPublicAssetUrl('templates/invoice.png'); + + // Fetch the file content (no auth required — public endpoint) + const response = await fetch(invoiceUrl); + const buffer = await response.arrayBuffer(); + + return { logoUrl, size: buffer.byteLength }; +}; + +export default defineLogicFunction({ + universalIdentifier: 'a1b2c3d4-...', + name: 'send-invoice', + description: 'Sends an invoice with the app logo', + timeoutSeconds: 10, + handler, +}); +``` + +**في مكوّن أمامي:** + +```tsx src/front-components/company-card.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const CompanyCard = () => { + const logoUrl = getPublicAssetUrl('logo.png'); + + return App logo; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'company-card', + component: CompanyCard, +}); +``` + +وسيطة `path` نسبية إلى مجلد `public/` الخاص بتطبيقك. كلٌّ من `getPublicAssetUrl('logo.png')` و`getPublicAssetUrl('public/logo.png')` يُحلاّن إلى العنوان نفسه — تتم إزالة بادئة `public/` تلقائيًا إن وُجدت. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/config/roles.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/config/roles.mdx new file mode 100644 index 0000000000..11879785db --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/config/roles.mdx @@ -0,0 +1,94 @@ +--- +title: الأدوار والصلاحيات +description: حدِّد الكائنات والحقول التي يمكن لوظائف منطق تطبيقك ومكوّنات الواجهة الأمامية قراءتها وكتابتها. +icon: shield-halved +--- + +**الدور** هو مجموعة من الأذونات: الكائنات التي يمكن لتطبيق ما قراءتها أو كتابتها، والحقول التي يمكنه رؤيتها، والقدرات على مستوى المنصّة التي يمكنه استخدامها. ترث جميع وظائف منطق كل تطبيق ومكوّنات الواجهة الأمامية الأذونات الخاصة بالدور المُعلَّم باستخدام `defineApplicationRole()` (انظر [دور الدالة الافتراضي](#the-default-function-role) أدناه). + +```ts src/roles/restricted-company-role.ts +import { + defineRole, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + SystemPermissionFlag, +} from 'twenty-sdk/define'; + +export default defineRole({ + universalIdentifier: '2c80f640-2083-4803-bb49-003e38279de6', + label: 'My new role', + description: 'A role that can be used in your workspace', + canReadAllObjectRecords: false, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canUpdateAllSettings: false, + canBeAssignedToAgents: false, + canBeAssignedToUsers: false, + canBeAssignedToApiKeys: false, + objectPermissions: [ + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + ], + fieldPermissions: [ + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + fieldUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name + .universalIdentifier, + canReadFieldValue: false, + canUpdateFieldValue: false, + }, + ], + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS], +}); +``` + +## الدور الافتراضي للوظيفة + +عند إنشاء هيكل لتطبيق جديد، ينشئ CLI ملف دور افتراضي مُصرَّحًا به باستخدام `defineApplicationRole()`: + +```ts src/roles/default-role.ts +import { defineApplicationRole } from 'twenty-sdk/define'; + +export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = + 'b648f87b-1d26-4961-b974-0908fd991061'; + +export default defineApplicationRole({ + universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, + label: 'Default function role', + description: 'Default role for function Twenty client', + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canUpdateAllSettings: false, + canBeAssignedToAgents: false, + canBeAssignedToUsers: false, + canBeAssignedToApiKeys: false, + objectPermissions: [], + fieldPermissions: [], + permissionFlagUniversalIdentifiers: [], +}); +``` + +تُعد `defineApplicationRole()` غلافًا بسيطًا حول `defineRole()` يشير إلى الدور المستخدم كإعداد افتراضي لتطبيقك وقت التثبيت. يتطابق التحقق من الصحة مع `defineRole`، لكن خط تجميع البناء يربط تلقائيًا قيمة `universalIdentifier` بصفة `defaultRoleUniversalIdentifier` في بيان التطبيق (manifest)، وبالتالي لا تحتاج إلى الإشارة إليه من [`defineApplication`](/l/ar/developers/extend/apps/config/application) بنفسك. + +الملاحظات: + +* يُسمح بوجود **استدعاء واحد فقط** لـ `defineApplicationRole(...)` لكل تطبيق — سيفشل إنشاء بيان التطبيق (manifest) إذا عثر على أكثر من واحد. +* استخدم `defineRole()` (وليس `defineApplicationRole()`) لأي أدوار **إضافية** يأتي بها تطبيقك. +* لا يزال تعيين `defaultRoleUniversalIdentifier` صراحةً على `defineApplication()` مدعومًا للتوافق مع الإصدارات السابقة، ولكنه مُهمَل لصالح `defineApplicationRole()`. + +## أفضل الممارسات + +* ابدأ من الدور المُنشأ تلقائيًا، ثم قم بتقييده تدريجيًا — إذ يمنح الإعداد الافتراضي صلاحيات قراءة واسعة، وهو ما نادرًا ما تريده في بيئة الإنتاج. +* استبدل `objectPermissions` و`fieldPermissions` بالكائنات والحقول الدقيقة التي تحتاجها وظائفك فعليًا. +* `permissionFlagUniversalIdentifiers` تتحكم في الوصول إلى القدرات على مستوى المنصة. اجعلها في حدّها الأدنى. +* اطّلع على مثال عملي: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/data/extending-objects.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/data/extending-objects.mdx new file mode 100644 index 0000000000..6bf27aa5b1 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/data/extending-objects.mdx @@ -0,0 +1,50 @@ +--- +title: توسيع الكائنات +description: أضِف حقولًا إلى كائنات Twenty القياسية (Person، Company، …) أو إلى كائنات من تطبيقات أخرى باستخدام defineField. +icon: wand-magic-sparkles +--- + +استخدم `defineField()` لإضافة حقل إلى كائن لا تملكه — كائن Twenty قياسي مثل Person أو Company، أو كائن يتم توفيره بواسطة تطبيق آخر مُثبَّت. على خلاف الحقول المضمّنة داخل [`defineObject`](/l/ar/developers/extend/apps/data/objects)، تتطلّب الحقول المستقلة `objectUniversalIdentifier` لتحديد الكائن الذي تقوم بتوسيعه. + +```ts src/fields/company-loyalty-tier.field.ts +import { defineField, FieldType } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890', + objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object + name: 'loyaltyTier', + type: FieldType.SELECT, + label: 'Loyalty Tier', + icon: 'IconStar', + options: [ + { value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' }, + { value: 'SILVER', label: 'Silver', position: 1, color: 'gray' }, + { value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' }, + ], +}); +``` + +## النقاط الرئيسية + +* `objectUniversalIdentifier` يحدّد الكائن الهدف. بالنسبة لكائنات Twenty القياسية، استورد الثابت من `twenty-sdk`: + + ```ts + import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define'; + + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier + // … + ``` + +* عند تعريف الحقول بشكل مضمّن **داخل `defineObject()`**، **لا** تحتاج إلى `objectUniversalIdentifier` — إذ يُورَّث من الكائن الأب. + +* `defineField()` هي الطريقة الوحيدة لإضافة حقول إلى كائنات لم تُنشئها باستخدام `defineObject()`. + +* موقع الملف متروك لك. المتعارف عليه هو `src/fields/\.field.ts`، لكن حزمة SDK تكتشف الحقول في أي مكان داخل `src/`. + +* لإضافة علامة تبويب إلى تخطيط صفحة قياسي (مثل صفحة تفاصيل Task أو Company)، استخدم [`definePageLayoutTab`](/l/ar/developers/extend/apps/layout/page-layouts#definepagelayouttab) مع `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS` من `twenty-sdk/define`. + +## إضافة علاقة إلى كائن موجود + +لإضافة حقل علاقة (مثل ربط الكائن المخصّص بكائن قياسي `Person`)، استخدم `defineField()` مع `FieldType.RELATION`. النمط هو نفسه الخاص بالعلاقات المضمّنة لكن مع تعيين `objectUniversalIdentifier` صراحةً. اطّلع على [Relations](/l/ar/developers/extend/apps/data/relations) للنمط ثنائي الاتجاه. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx new file mode 100644 index 0000000000..060759fe48 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/data/objects.mdx @@ -0,0 +1,104 @@ +--- +title: كائنات +description: عرّف أنواعًا جديدة من السجلات — جداول مخصصة بحقولها الخاصة — باستخدام defineObject. +icon: جدول +--- + +تُعد **الكائنات** المخصصة أنواع سجلات جديدة يضيفها تطبيقك إلى مساحة العمل — مثل بطاقة بريدية، أو فاتورة، أو اشتراك، أو أي شيء خاص بالمجال الذي تعمل فيه. يعلن كل كائن عن مخططه (الحقول، والعلاقات، والقيم الافتراضية) ومعرّف عالمي ثابت يستمر عبر عمليات المزامنة والنشر. + +```ts src/objects/post-card.object.ts +import { defineObject, FieldType } from 'twenty-sdk/define'; + +enum PostCardStatus { + DRAFT = 'DRAFT', + SENT = 'SENT', + DELIVERED = 'DELIVERED', + RETURNED = 'RETURNED', +} + +export default defineObject({ + universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05', + nameSingular: 'postCard', + namePlural: 'postCards', + labelSingular: 'Post Card', + labelPlural: 'Post Cards', + description: 'A post card object', + icon: 'IconMail', + fields: [ + { + universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b', + name: 'content', + type: FieldType.TEXT, + label: 'Content', + description: "Postcard's content", + icon: 'IconAbc', + }, + { + universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac', + name: 'recipientName', + type: FieldType.FULL_NAME, + label: 'Recipient name', + icon: 'IconUser', + }, + { + universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266', + name: 'recipientAddress', + type: FieldType.ADDRESS, + label: 'Recipient address', + icon: 'IconHome', + }, + { + universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e', + name: 'status', + type: FieldType.SELECT, + label: 'Status', + icon: 'IconSend', + defaultValue: `'${PostCardStatus.DRAFT}'`, + options: [ + { value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' }, + { value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' }, + { value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' }, + { value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' }, + ], + }, + { + universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433', + name: 'deliveredAt', + type: FieldType.DATE_TIME, + label: 'Delivered at', + icon: 'IconCheck', + isNullable: true, + defaultValue: null, + }, + ], +}); +``` + +## النقاط الرئيسية + +* `universalIdentifier` يجب أن يكون فريدًا وثابتًا عبر عمليات النشر. +* يتطلب كل حقل `name` و`type` و`label` ومعرّف `universalIdentifier` ثابتًا خاصًا به. +* المصفوفة `fields` اختيارية — يمكنك تعريف كائنات بدون حقول مخصصة. +* لا تحتاج الحقول المضمّنة المُعرَّفة هنا إلى `objectUniversalIdentifier` — إذ تُورَّث من الكائن الأب. استخدم [`defineField()`](/l/ar/developers/extend/apps/data/extending-objects) لإضافة حقول إلى كائنات لا تمتلكها. +* يمكنك إنشاء كائنات جديدة باستخدام `yarn twenty dev:add object`، والذي يرشدك خلال التسمية والحقول والعلاقات. راجع [Architecture → Scaffolding entities](/l/ar/developers/extend/apps/getting-started/scaffolding). + + +**تُضاف الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، ينشئ Twenty حقولًا قياسية مثل `id` و`name` و`createdAt` و`updatedAt` و`createdBy` و`updatedBy` و`deletedAt` من أجلك. لا تحتاج إلى تعريفها في مصفوفة `fields` — أضف فقط حقولك المخصصة. يمكنك تجاوز حقلًا افتراضيًا بتعريف حقل يحمل الاسم نفسه، لكن هذا نادرًا ما يكون فكرة جيدة. + + +## القيم الافتراضية + +يجب تضمين القيم النصية الافتراضية بين علامات اقتباس أحادية **داخل** السلسلة — `defaultValue: "'Draft'"`، وليس `defaultValue: "Draft"`. لهذا السبب يستخدم الحقل `status` أعلاه `` `'${PostCardStatus.DRAFT}'` ``. + +السلاسل غير المحاطة بعلامات اقتباس محجوزة للقيم الافتراضية المحسوبة، والتي يتم تقييمها عند إنشاء سجل: + +* `'uuid'` — يُنشِئ UUID (لحقول `UUID`) +* `'now'` — الطابع الزمني الحالي (لحقول `DATE_TIME`) + +ينطبق نفس الاصطلاح على الحقول الفرعية النصية للقيم الافتراضية المركّبة (على سبيل المثال `{ source: "'MANUAL'" }` في حقل `ACTOR`) وكذلك على قيم `SELECT`/`MULTI_SELECT`. سيتسبّب ترك قيمة افتراضية نصية حرفية بدون علامات اقتباس في ظهور تحذير عند إنشاء التطبيق. + +## ماذا بعد؟ + +* **اربط هذا الكائن بغيره من الكائنات** — راجع صفحة [Relations](/l/ar/developers/extend/apps/data/relations) لمعرفة نمط العلاقة ثنائية الاتجاه. +* **أضف حقولًا إلى الكائنات التابعة لتطبيقات أخرى** — راجع [Extending Objects](/l/ar/developers/extend/apps/data/extending-objects) لمعرفة المزيد حول `defineField()`. +* **اعرض هذا الكائن في واجهة المستخدم** — راجع [Views](/l/ar/developers/extend/apps/layout/views) و[Navigation Menu Items](/l/ar/developers/extend/apps/layout/navigation-menu-items) لإظهاره في الشريط الجانبي. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/data/overview.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/data/overview.mdx new file mode 100644 index 0000000000..7b9ca6e56c --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/data/overview.mdx @@ -0,0 +1,97 @@ +--- +title: نظرة عامة +description: شكّل البيانات التي يضيفها تطبيقك إلى مساحة العمل — الكائنات والحقول والعلاقات. +icon: database +--- + +طبقة **البيانات** في تطبيق Twenty هي البيانات التي *يضيفها* تطبيقك إلى مساحة العمل — أنواع السجلات الجديدة التي يصرّح عنها، والأعمدة التي يضيفها إلى الكائنات الموجودة، وكيف ترتبط هذه السجلات ببعضها البعض. + +```text +┌──────────────────────────────────────────────────┐ +│ Object — a record type, e.g. PostCard │ +│ ├─ Field (name, type, label) │ +│ ├─ Field │ +│ └─ Relation (link to another object) │ +└──────────────────────────────────────────────────┘ + │ + ├── lives in your app, OR + │ + ▼ +┌──────────────────────────────────────────────────┐ +│ Standard / other apps' objects │ +│ └─ Field added by your app via defineField │ +└──────────────────────────────────────────────────┘ +``` + +## في هذا القسم + + + + `defineObject` — صرّح عن أنواع سجلات جديدة مع حقولها الخاصة. + + + `defineField` — أضِف حقولًا إلى كائنات قياسية أو كائنات تطبيقات أخرى. + + + اتصالات ثنائية الاتجاه من نوع `MANY_TO_ONE` / `ONE_TO_MANY` بين الكائنات. + + + +## الكيانات بنظرة سريعة + +| كيان | الغرض | مُعرَّفة باستخدام | +| ---------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------- | +| **الكائن** | نوع سجل مخصص جديد (مثل PostCard أو Invoice) مع حقوله الخاصة | `defineObject()` | +| **الحقل** | عمود على كائن. يمكن للحقول المستقلة توسيع الكائنات التي لم تنشئها (مثل إضافة `loyaltyTier` إلى Company) | `defineField()` | +| **علاقة** | ارتباط ثنائي الاتجاه بين كائنين — يُصرّح عن كلا الجانبين كحقول | `defineField()` مع `FieldType.RELATION` | +| **الفهرس** | فهرس قاعدة بيانات لتسريع استعلام متكرر على أحد الكائنات لديك | `defineIndex()` | + +يكتشف SDK هذه العناصر عبر تحليل AST أثناء وقت البناء، لذا تنظيم الملفات يعود إليك — القاعدة المتّبعة هي `src/objects/` و `src/fields/` و `src/indexes/`. معرّفات UUID ثابتة من نوع `universalIdentifier` تربط كل شيء معًا عبر عمليات النشر. + +## الفهارس (اختياري) + +يمكن للتطبيقات إرفاق الفهارس مع الكائنات الخاصة بها للحفاظ على سرعة الاستعلامات المتكررة. أكثر الحالات شيوعًا هي عمود حالة أو مفتاح أجنبي تقوم بقراءته بشكل متكرر. + +```ts src/indexes/post-card-status.index.ts +import { defineIndex } from 'twenty-sdk/define'; + +import { + POST_CARD_UNIVERSAL_IDENTIFIER, + STATUS_FIELD_UNIVERSAL_IDENTIFIER, +} from '../objects/post-card.object'; + +export default defineIndex({ + universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff0', + objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + fields: [ + { + universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff1', + fieldUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + }, + ], +}); +``` + +### الفهارس الفريدة + +تقبل `defineIndex` الخيار `isUnique: true` لكلٍّ من التفرد على عمود واحد أو على عدّة أعمدة. هذه هي البنية الموصى بها — إن `defineField({ isUnique: true })` مهملة وسيتم إزالتها في إصدار قادم. + +```ts +defineIndex({ + universalIdentifier: '…', + objectUniversalIdentifier: PERSON_UNIVERSAL_IDENTIFIER, + isUnique: true, + fields: [{ universalIdentifier: '…', fieldUniversalIdentifier: EMAIL_FIELD_UNIVERSAL_IDENTIFIER }], +}); +``` + +### قيود أخرى + +* تظل عبارات `WHERE` الجزئية تحت تحكم المشرف — لا يمكن للتطبيقات التصريح بها. +* يتم تقييد كل كائن بعدد 10 فهارس مخصصة (لا تُحتسب فهارس الإطار نفسه). + +رتّب مصفوفة `fields` بالطريقة التي ينبغي على Postgres استخدامها — العمود في أقصى اليسار أولًا، مثل دليل الهاتف. الفهارس ليست مجانية: كل عملية كتابة في الجدول تقوم بتحديثها. أضِف واحدًا فقط عندما يكون لديك استعلام يحتاج إليه. + + +هل تبحث عن **Application Config** أو **Roles & Permissions**؟ هذه تصف التطبيق نفسه بدلًا من البيانات التي يضيفها — وتوجد ضمن [Config](/l/ar/developers/extend/apps/config/overview). هل تبحث عن **Connections** (Linear, GitHub, Slack OAuth)؟ هذه وُجِدت ليتم استدعاؤها *من* دوال المنطق وتوجد ضمن [Logic](/l/ar/developers/extend/apps/logic/connections). + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/data/relations.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/data/relations.mdx new file mode 100644 index 0000000000..f1c784e827 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/data/relations.mdx @@ -0,0 +1,160 @@ +--- +title: العلاقات +description: وصِل الكائنات معًا بعلاقات MANY_TO_ONE / ONE_TO_MANY ثنائية الاتجاه. +icon: diagram-project +--- + +تربط العلاقات كائنين معًا. في Twenty، تكون العلاقات دائمًا **ثنائية الاتجاه** — لكل علاقة جانبَان، ويُصرَّح عن كل جانب كحقل يُشير إلى الآخر. + +| نوع العلاقة | الوصف | هل لديه مفتاح خارجي؟ | +| ------------- | ------------------------------------------------------ | ---------------------- | +| `MANY_TO_ONE` | تشير العديد من سجلات هذا الكائن إلى سجل واحد من الهدف | نعم (`joinColumnName`) | +| `ONE_TO_MANY` | يحتوي سجل واحد من هذا الكائن على العديد من سجلات الهدف | لا (الجانب العكسي) | + +## كيف تعمل العلاقات + +تتطلّب كل علاقة **حقلين** يشيران إلى بعضهما البعض: + +1. جانب **MANY_TO_ONE** — يوجد على الكائن الذي يحمل المفتاح الخارجي. +2. جانب **ONE_TO_MANY** — يوجد على الكائن الذي يملك المجموعة. + +يستخدم كلا الحقلين `FieldType.RELATION` ويُحيل كلٌ منهما إلى الآخر عبر `relationTargetFieldMetadataUniversalIdentifier`. + +## مثال: البطاقة البريدية لديها العديد من المستلمين + +يمكن إرسال `PostCard` إلى العديد من سجلات `PostCardRecipient`. ينتمي كل مستلم إلى بطاقة بريدية واحدة بالضبط. + +**الخطوة 1: عرّف جانب ONE_TO_MANY على PostCard** (جانب "الواحد"): + +```ts src/fields/post-card-recipients-on-post-card.field.ts +import { defineField, FieldType, RelationType } from 'twenty-sdk/define'; +import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; +import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; + +// Export so the other side can reference it +export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111'; +// Import from the other side +import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field'; + +export default defineField({ + universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'postCardRecipients', + label: 'Post Card Recipients', + icon: 'IconUsers', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); +``` + +**الخطوة 2: عرّف جانب MANY_TO_ONE على PostCardRecipient** (جانب "العديد" — يحمل المفتاح الخارجي): + +```ts src/fields/post-card-on-post-card-recipient.field.ts +import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define'; +import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; +import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; + +// Export so the other side can reference it +export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222'; +// Import from the other side +import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field'; + +export default defineField({ + universalIdentifier: POST_CARD_FIELD_ID, + objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'postCard', + label: 'Post Card', + icon: 'IconMail', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.CASCADE, + joinColumnName: 'postCardId', + }, +}); +``` + + +**الاستيرادات الدائرية:** كلا حقلي العلاقة يُشير كلٌّ منهما إلى `universalIdentifier` الخاص بالآخر. لتجنّب مشكلات الاستيراد الدائري، صدِّر معرّفات الحقول كثوابت مسمّاة من كل ملف، واستورِدها في الملف الآخر. يقوم نظام البناء بحلّها في وقت التجميع. + + +## الربط مع الكائنات القياسية + +لإنشاء علاقة مع كائن Twenty مضمّن (Person, Company, etc.)، استخدم `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`: + +```ts src/fields/person-on-self-hosting-user.field.ts +import { + defineField, + FieldType, + RelationType, + OnDeleteAction, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; +import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object'; + +export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333'; +export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444'; + +export default defineField({ + universalIdentifier: PERSON_FIELD_ID, + objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'person', + label: 'Person', + description: 'Person matching with the self hosting user', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'personId', + }, +}); +``` + +## خصائص حقل العلاقة + +| الخاصية | مطلوب | الوصف | +| ------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------- | +| `type` | نعم | يجب أن يكون `FieldType.RELATION` | +| `relationTargetObjectMetadataUniversalIdentifier` | نعم | قيمة `universalIdentifier` للكائن الهدف | +| `relationTargetFieldMetadataUniversalIdentifier` | نعم | قيمة `universalIdentifier` للحقل المطابق على الكائن الهدف | +| `universalSettings.relationType` | نعم | `RelationType.MANY_TO_ONE` أو `RelationType.ONE_TO_MANY` | +| `universalSettings.onDelete` | MANY_TO_ONE فقط | ماذا يحدث عند حذف السجل المشار إليه: `CASCADE`، `SET_NULL`، `RESTRICT`، أو `NO_ACTION` | +| `universalSettings.joinColumnName` | MANY_TO_ONE فقط | اسم عمود قاعدة البيانات للمفتاح الخارجي (مثل `postCardId`) | + +## حقول العلاقات المضمَّنة + +يمكنك أيضًا إعلان علاقة مباشرة داخل [`defineObject`](/l/ar/developers/extend/apps/data/objects). عند كونها مضمَّنة، احذِف `objectUniversalIdentifier` — إذ يُورَّث من الكائن الأب: + +```ts +export default defineObject({ + universalIdentifier: '...', + nameSingular: 'postCardRecipient', + // ... + fields: [ + { + universalIdentifier: POST_CARD_FIELD_ID, + type: FieldType.RELATION, + name: 'postCard', + label: 'Post Card', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.CASCADE, + joinColumnName: 'postCardId', + }, + }, + // … other fields + ], +}); +``` diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/concepts.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/concepts.mdx new file mode 100644 index 0000000000..f335413133 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/concepts.mdx @@ -0,0 +1,101 @@ +--- +title: المفاهيم +description: كيفية عمل تطبيقات Twenty — نموذج الكيان، العزل (sandboxing)، ودورة حياة التثبيت. +icon: sitemap +--- + +تطبيقات Twenty هي حزم TypeScript توسّع مساحة عملك بكائنات مخصّصة، ومنطق، ومكوّنات واجهة مستخدم (UI)، وقدرات ذكاء اصطناعي. تعمل على منصة Twenty مع عزل كامل وضوابط الأذونات. + +## كيف تعمل التطبيقات + +التطبيق عبارة عن مجموعة من **الكيانات** يتم إعلانها باستخدام دوال `defineEntity()` من حزمة `twenty-sdk`. يكتشف SDK هذه التصريحات عبر تحليل AST وقت البناء وينتج **ملف بيان** — وصفًا كاملًا لما يضيفه تطبيقك إلى مساحة العمل. تتحقق هذه الدوال من تكوينك وقت البناء وتوفّر إكمالًا تلقائيًا في بيئة التطوير وأمان الأنواع. + +``` +your-app/ +├── src/ +│ ├── application-config.ts ← defineApplication (required, one per app) +│ ├── roles/ ← defineRole +│ ├── objects/ ← defineObject +│ ├── fields/ ← defineField +│ ├── logic-functions/ ← defineLogicFunction +│ ├── front-components/ ← defineFrontComponent +│ ├── skills/ ← defineSkill +│ ├── agents/ ← defineAgent +│ ├── views/ ← defineView +│ ├── navigation-menu-items/ ← defineNavigationMenuItem +│ └── page-layouts/ ← definePageLayout +├── public/ ← Static assets (images, icons) +└── package.json +``` + + + **تنظيم الملفات متروك لك.** يعتمد اكتشاف الكيانات على AST — يعثر SDK على استدعاءات `export default defineEntity(...)` بغض النظر عن مكان وجود الملف. بنية المجلدات أعلاه هي اصطلاح وليست متطلبًا. + + +## أنواع الكيانات + +| كيان | الغرض | وثائق | +| ---------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------- | +| **تطبيق** | هوية التطبيق، الدور الافتراضي، والمتغيرات | [تهيئة التطبيق](/l/ar/developers/extend/apps/config/application) | +| **دور** | مجموعات الأذونات للكائنات والحقول | [الأدوار والأذونات](/l/ar/developers/extend/apps/config/roles) | +| **الكائن** | أنواع سجلات مخصّصة مع حقول | [الكائنات](/l/ar/developers/extend/apps/data/objects) | +| **الحقل** | إضافة حقول إلى الكائنات من تطبيقات أخرى | [توسيع الكائنات](/l/ar/developers/extend/apps/data/extending-objects) | +| **علاقة** | روابط ثنائية الاتجاه بين الكائنات | [العلاقات](/l/ar/developers/extend/apps/data/relations) | +| **دالة منطقية** | TypeScript على جانب الخادم مع مشغّلات | [الوظائف المنطقية](/l/ar/developers/extend/apps/logic/logic-functions) | +| **مهارة** | تعليمات قابلة لإعادة الاستخدام لوكلاء الذكاء الاصطناعي | [المهارات والوكلاء](/l/ar/developers/extend/apps/logic/skills-and-agents) | +| **وكيل** | مساعدو الذكاء الاصطناعي بموجهات مخصّصة | [المهارات والوكلاء](/l/ar/developers/extend/apps/logic/skills-and-agents) | +| **موفر الاتصال** | بيانات اعتماد OAuth لواجهات برمجة التطبيقات التابعة لجهات خارجية | [الاتصالات](/l/ar/developers/extend/apps/logic/connections) | +| **عرض** | عروض قوائم السجلات المكوّنة مسبقًا | [العروض](/l/ar/developers/extend/apps/layout/views) | +| **عنصر قائمة التنقّل** | عناصر الشريط الجانبي المخصّصة | [عناصر قائمة التنقّل](/l/ar/developers/extend/apps/layout/navigation-menu-items) | +| **تخطيط الصفحة** | علامات التبويب وعناصر الواجهة في صفحة تفاصيل السجل | [تخطيطات الصفحات](/l/ar/developers/extend/apps/layout/page-layouts) | +| **مكوّن أمامي** | واجهة مستخدم React معزولة داخل Twenty | [المكوّنات الأمامية](/l/ar/developers/extend/apps/layout/front-components) | +| **عنصر قائمة الأوامر** | إجراءات سريعة ومدخلات Cmd+K | [عناصر قائمة الأوامر](/l/ar/developers/extend/apps/layout/command-menu-items) | + +## العزل + +* **الدوال المنطقية** تعمل في عمليات Node.js معزولة على الخادم. لا تصل إلى البيانات إلا عبر عميل API مضبوط الأنواع، ومقيَّد بأذونات دور التطبيق. +* **المكوّنات الأمامية** تعمل ضمن Web Workers باستخدام Remote DOM — معزولة عن الصفحة الرئيسية لكنها تعرض عناصر DOM الأصلية (وليس iframes). تتواصل مع Twenty عبر واجهة API للمضيف تعتمد تمرير الرسائل. +* **الأذونات** تُطبَّق على مستوى واجهة API. يُشتق رمز وقت التشغيل (`TWENTY_APP_ACCESS_TOKEN`) من الدور المعرَّف في `defineApplication()`. + +## دورة حياة التطبيق + +``` +┌─────────────────────────────────────────────────────────┐ +│ Development │ +│ npx create-twenty-app → yarn twenty dev (live sync) │ +├─────────────────────────────────────────────────────────┤ +│ Build & Deploy │ +│ yarn twenty dev:build → yarn twenty app:publish │ +├─────────────────────────────────────────────────────────┤ +│ Install flow │ +│ upload → [pre-install] → metadata migration → │ +│ generate SDK → [post-install] │ +├─────────────────────────────────────────────────────────┤ +│ Publish │ +│ npm publish → appears in Twenty marketplace │ +└─────────────────────────────────────────────────────────┘ +``` + +* **`yarn twenty dev`** — يراقب ملفات المصدر لديك ويزامن التغييرات مباشرةً إلى خادم Twenty متصل. يُعاد توليد عميل API مضبوط الأنواع تلقائيًا عند تغيّر المخطط. +* **`yarn twenty dev:build`** — يجمّع TypeScript، ويضمّن الدوال المنطقية والمكوّنات الأمامية باستخدام esbuild، وينتج ملف بيان. +* **خطّافات ما قبل/ما بعد التثبيت** — دوال اختيارية تعمل أثناء التثبيت. راجع [خطّافات التثبيت](/l/ar/developers/extend/apps/config/install-hooks) للتفاصيل. + +## الخطوات التالية + + + + هوية التطبيق، الدور الافتراضي، وخطّافات التثبيت. + + + الكائنات، الحقول، والعلاقات ثنائية الاتجاه. + + + دوال منطقية، مهارات، وكلاء، واتصالات OAuth. + + + العروض، التنقّل، تخطيطات الصفحات، ومكوّنات الواجهة الأمامية. + + + سطر الأوامر (CLI)، الاختبار، المستودعات البعيدة (remotes)، التكامل المستمر (CI)، ونشر تطبيقك. + + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/local-server.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/local-server.mdx new file mode 100644 index 0000000000..1371fc3620 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/local-server.mdx @@ -0,0 +1,87 @@ +--- +title: الخادم المحلي +description: إدارة خادم Twenty المحلي المستند إلى Docker — بدء التشغيل، الإيقاف، الترقية، مثيل اختبار متوازٍ، وإعداد SDK اليدوي. +icon: server +--- + +## إدارة الخادم المحلي + +استخدم `yarn twenty docker:*` للتحكّم في حاوية Twenty المحلية: + +| أمر | ماذا يفعل | +| -------------------------------------- | -------------------------------------------------- | +| `yarn twenty docker:start` | بدء تشغيل الخادم (يسحب الصورة إذا لزم الأمر) | +| `yarn twenty docker:start 2.2.0` | بدء إصدار محدد من الخادم | +| `yarn twenty docker:start --port 3030` | بدء التشغيل على منفذ مخصّص | +| `yarn twenty docker:stop` | إيقاف الخادم (مع الحفاظ على البيانات) | +| `yarn twenty docker:status` | عرض عنوان URL والإصدار وبيانات اعتماد تسجيل الدخول | +| `yarn twenty docker:logs` | بث سجلات الخادم | +| `yarn twenty docker:reset` | مسح البيانات والبدء من جديد | +| `yarn twenty docker:upgrade` | سحب أحدث صورة `twenty-app-dev` | +| `yarn twenty docker:upgrade 2.2.0` | الترقية إلى إصدار محدد | + +تظل البيانات محفوظة عبر عمليات إعادة التشغيل في وحدتي تخزين Docker (`twenty-app-dev-data` لـ PostgreSQL، و`twenty-app-dev-storage` للملفات). استخدم `reset` لمسح كل شيء. + +## تثبيت إصدار الخادم + +عند عدم تمرير أي إصدار، يقوم `docker:start` باشتقاق الإصدار من النطاق `engines.twenty` في ملف `package.json` لتطبيقك — وهو نفس النطاق الذي يتحقق منه الخادم عند تثبيت تطبيقك. يشغّل أحدث صورة منشورة لـ `twenty-app-dev` التي تُلبي هذا النطاق، مع الرجوع إلى `latest` عندما يكون الحقل غير موجودًا أو لا يتطابق أي إصدار منشور: + +```json filename="package.json" +{ + "engines": { + "twenty": ">=2.2.0" + } +} +``` + +مرِّر إصدارًا بشكلٍ صريح لتجاوز النطاق لتشغيلٍ واحد فقط: `yarn twenty docker:start 2.3.0`. إذا كانت هناك حاوية موجودة بالفعل على إصدار مختلف، يقوم `docker:start` بترقيتها في مكانها (مع إعادة إنشاء الحاوية مع الحفاظ على وحدات تخزين بياناتك). + +## ترقية صورة الخادم + +يقوم `yarn twenty docker:upgrade` بسحب أحدث صورة، ومقارنة التجزئات، ولا يعيد إنشاء الحاوية إلا إذا حدث تغيير فعلي. تظل وحدات التخزين محفوظة — ويتم استبدال الحاوية فقط. إذا تم سحب صورة جديدة وكانت الحاوية تعمل، فستبدأ عملية الترقية تلقائيًا حاوية جديدة؛ شغّل بعد ذلك `yarn twenty docker:start` للانتظار حتى تصبح سليمة. + +```bash filename="Terminal" +yarn twenty docker:upgrade # Latest +yarn twenty docker:upgrade 2.2.0 # Specific version +``` + +تحقّق من الإصدار الجاري باستخدام `yarn twenty docker:status` (يعرض قيمة `APP_VERSION` المضمنة في الحاوية). + +## تشغيل مثيل اختبار متوازٍ + +مرّر `--test` إلى أي أمر `docker:*` لإدارة مثيل ثانٍ معزول تمامًا — مفيد لاختبارات التكامل أو للتجربة من دون لمس بيانات التطوير الرئيسية لديك: + +| أمر | ماذا يفعل | +| ----------------------------------- | ----------------------------------------- | +| `yarn twenty docker:start --test` | بدء مثيل الاختبار (المنفذ الافتراضي 2021) | +| `yarn twenty docker:stop --test` | إيقافه | +| `yarn twenty docker:status --test` | عرض حالته | +| `yarn twenty docker:logs --test` | بث سجلاته | +| `yarn twenty docker:reset --test` | مسح بياناته | +| `yarn twenty docker:upgrade --test` | ترقية صورته | + +يملك مثيل الاختبار حاويته الخاصة (`twenty-app-dev-test`) ووحدات التخزين الخاصة به (`twenty-app-dev-test-data`، `twenty-app-dev-test-storage`) وتهيئته — ويعمل جنبًا إلى جنب مع مثيلك الرئيسي بدون تعارضات. اجمع `--test` مع `--port` لتجاوز المنفذ 2021. + +## إعداد يدوي (بدون أداة توليد الهيكل) + +تجاوز أداة توليد الهيكل إذا كنت تضيف SDK إلى مشروع قائم: + +```bash filename="Terminal" +yarn add twenty-sdk twenty-client-sdk +``` + +أضِف النص البرمجي إلى `package.json`: + +```json filename="package.json" +{ + "scripts": { + "twenty": "twenty" + } +} +``` + +يمكنك الآن تشغيل `yarn twenty dev`، و`yarn twenty docker:start`، والبقية. + + +لا تثبّت `twenty-sdk` عالميًا — ثبِّته لكل مشروع بحيث يستخدم كل تطبيق إصداره الخاص. + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/project-structure.mdx new file mode 100644 index 0000000000..64f4972a9f --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/project-structure.mdx @@ -0,0 +1,61 @@ +--- +title: هيكل المشروع +description: ما الذي يوجد داخل تطبيق Twenty المُهيكل مسبقًا — الملفات والمجلدات، وما الذي يفعله كلٌّ منها. +icon: folder-tree +--- + +يبدو التطبيق الجديد الذي يتم إنشاؤه بواسطة `npx create-twenty-app` كما يلي: + +```text filename="my-twenty-app/" +my-twenty-app/ + package.json + src/ + application-config.ts # Required — your app's entry point + default-role.ts # Permissions for logic functions + constants/ + universal-identifiers.ts # Auto-generated UUIDs and metadata + __tests__/ + setup-test.ts + app-install.integration-test.ts + .github/workflows/ci.yml # GitHub Actions + public/ # Static assets + vitest.config.ts # Test runner config + tsconfig.json, tsconfig.spec.json + .nvmrc, .yarnrc.yml, .oxlintrc.json + README.md, LLMS.md +``` + +## الملفات الرئيسية + +| ملف / مجلد | الغرض | +| ---------------------------------------- | ------------------------------------------------------------------- | +| `src/application-config.ts` | **مطلوب.** ملف الإعداد الرئيسي لتطبيقك. | +| `src/default-role.ts` | دور افتراضي يتحكّم بما يمكن لدوال المنطق الوصول إليه. | +| `src/constants/universal-identifiers.ts` | معرّفات UUID وبيانات تعريف يتم توليدها تلقائيًا (اسم العرض، الوصف). | +| `src/__tests__/` | اختبارات تكامل (إعداد + اختبار مثال). | +| `public/` | أصول ثابتة (صور، خطوط) تُقدَّم مع تطبيقك. | + + +**تنظيم الملفات متروك لك.** المجلدات المذكورة أعلاه هي أعراف متَّبعة — يكتشف SDK الكيانات عبر تحليل AST على استدعاءات `export default defineEntity(...)` بغض النظر عن مكان وجود الملف. + + +## التبعيات + +ينبغي أن تكون حزمتا SDK الخاصتان بـ Twenty ضمن `devDependencies`، وليس ضمن `dependencies`: + +```json filename="package.json" +{ + "dependencies": {}, + "devDependencies": { + "twenty-client-sdk": "^2.13.0", + "twenty-sdk": "^2.13.0" + } +} +``` + +* توفّر **`twenty-sdk`** أداة `twenty` CLI وأدوات البناء/إنشاء الهياكل (scaffolding). يعمل فقط أثناء التطوير ووقت البناء، ولا يتم استيراده أبدًا في وقت تشغيل تطبيقك المنشور. +* يتم استيراد **`twenty-client-sdk`** بواسطة كود تطبيقك (`CoreApiClient`، `MetadataApiClient`، `RestApiClient`)؛ لكن Twenty توفّره في وقت التشغيل — حيث تحصل عليه دوال المنطق من طبقة SDK مُولَّدة، وتحصل عليه مكوّنات الواجهة من وحدات يتم تقديمها من الخادم. يُستخدَم الإصدار المثبّت لديك فقط لفحص الأنواع (typechecking) ولبناء النشر (deploy-time build)، لذا لا يلزم أبدًا أن يتم تضمينه في حزمة النشر. + +الاحتفاظ بأي من الحزمتين ضمن `dependencies` يؤدي إلى سحبها داخل حزمة وقت تشغيل التطبيق المثبّت، حيث تكون عبئًا زائدًا بلا فائدة. يُطلق `twenty build` تحذيرًا عندما تكون أيٌّ منهما ما تزال مدرجة ضمن `dependencies`. + +أضِف تبعيات وقت التشغيل الخاصة بتطبيقك (المكتبات التي تستوردها دوال المنطق لديك فعلًا في وقت التشغيل) ضمن `dependencies` كالمعتاد. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx new file mode 100644 index 0000000000..3d28211656 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/quick-start.mdx @@ -0,0 +1,176 @@ +--- +title: البدء السريع +icon: rocket +description: أنشئ أول تطبيق Twenty خلال دقائق. +--- + +## المتطلبات الأساسية + +* **Node.js 24+** — [تنزيل](https://nodejs.org/) +* **Yarn 4** — يأتي مع Node.js عبر Corepack. قم بتمكينه: `corepack enable` +* **Docker** — [تنزيل](https://www.docker.com/products/docker-desktop/). مطلوب لتشغيل خادم Twenty محليًا. تخطَّ ذلك إذا كان لديك Twenty يعمل في مكان آخر. + +يتكوّن إنشاء تطبيق Twenty من ثلاث مراحل. تقوم أداة توليد الهيكل بدمجها في أمر واحد لمسار الاستخدام المثالي، لكن كل مرحلة تمثّل مفهومًا منفصلًا — وعند حدوث فشل، فإن معرفة المرحلة التي أنت فيها تُخبرك بما ينبغي إصلاحه. + +| المرحلة | ماذا تفعل | الأداة | النتيجة | +| ------------------- | ---------------------------------- | ----------------------------- | ------------------------------- | +| **1. تهيئة الهيكل** | توليد الشفرة المصدرية للتطبيق | `npx create-twenty-app` | مشروع TypeScript على القرص | +| **2. تشغيل خادم** | بدء تشغيل خادم Twenty للمزامنة معه | Docker + `yarn twenty server` | مثيل Twenty قيد التشغيل | +| **3. مزامنة** | قم بمزامنة شفرتك مباشرةً مع الخادم | `yarn twenty dev` | تظهر تغييراتك في واجهة المستخدم | + +--- + +## المرحلة 1 — تهيئة هيكل مشروعك + +أنشئ تطبيقًا جديدًا من القالب: + +```bash filename="Terminal" +npx create-twenty-app@latest my-twenty-app +``` + +ستتم مطالبتك باسم ووصف — اضغط **Enter** للقيم الافتراضية. يُنشئ هذا مشروع TypeScript في `my-twenty-app/` يتضمن ملف بداية `application-config.ts`، ودورًا افتراضيًا، وسير عمل CI، واختبار تكامل. + +**بعد هذه المرحلة:** سيكون لديك الشفرة المصدرية لتطبيق على جهازك. ليس قيد التشغيل بعد — وهذه هي المرحلة 2. + +--- + +## المرحلة 2 — تشغيل خادم Twenty محلي + +يحتاج تطبيقك إلى خادم Twenty للمزامنة معه. الخادم هو مثيل Twenty كامل — واجهة مستخدم، واجهة برمجة تطبيقات GraphQL، PostgreSQL — يعمل محليًا داخل Docker. ترفع شفرتك المحلية تعريفاتها إلى ذلك الخادم، مما يجعلها تظهر في واجهة المستخدم. + +تقترح أداة توليد الهيكل تشغيل خادم لك: + +> **هل ترغب في إعداد مثيل محلي من Twenty؟** + +* **نعم (موصى به)** — ستسحب صورة Docker `twentycrm/twenty-app-dev` وتبدأ تشغيلها على المنفذ `2020`. تأكّد أولًا من أن Docker قيد التشغيل. +* **لا** — اختر هذا إذا كان لديك بالفعل خادم Twenty تريد الاتصال به. يمكنك ربطه لاحقًا باستخدام `yarn twenty remote:add`. + +
+ هل يجب بدء المثيل المحلي؟ +
+ +بمجرد أن يصبح الخادم جاهزًا، سيفتح المتصفح لإجراء تسجيل الدخول. استخدم حساب العرض التوضيحي المُجهَّز مسبقًا: + +* **البريد الإلكتروني:** `tim@apple.dev` +* **كلمة المرور:** `tim@apple.dev` + +
+ شاشة تسجيل الدخول إلى Twenty +
+ +انقر **Authorize** في الشاشة التالية — يمنح هذا واجهة سطر الأوامر CLI حق الوصول إلى مساحة العمل الخاصة بك. + +
+ شاشة تفويض واجهة الأوامر (CLI) الخاصة بـ Twenty +
+ +ستؤكّد الطرفية أن كل شيء قد تم إعداده. + +
+ تم إنشاء هيكل التطبيق بنجاح +
+ +**بعد هذه المرحلة:** لديك خادم Twenty قيد التشغيل على [http://localhost:2020](http://localhost:2020)، مع تفويض CLI لديك للمزامنة معه. + + +إذا لم يكن Docker مثبتًا أو قيد التشغيل، فستخبرك أداة توليد الهيكل بأمر البدء المناسب لنظام التشغيل لديك. عند تشغيل Docker، يمكنك المتابعة باستخدام `yarn twenty docker:start` — لا حاجة لإعادة إنشاء الهيكل. + + +--- + +## المرحلة 3 — مزامنة تغييراتك + +هذه هي الحلقة الداخلية التي ستقضي معظم وقتك فيها. + +```bash filename="Terminal" +cd my-twenty-app +yarn twenty dev +``` + +يراقب هذا المجلد `src/`، ويُعيد البناء عند كل تغيير، ويزامن الناتج إلى الخادم. حرّر ملفًا، واحفظه، وخلال بضع ثوانٍ سينعكس التغيير على الخادم. سترى لوحة حالة مباشرة في الطرفية. + +للحصول على مخرجات أكثر تفصيلاً (سجلات البناء، طلبات المزامنة، تتبعات الأخطاء)، أضِف `--verbose`. + +
+ مخرجات الطرفية في وضع التطوير +
+ +افتح [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). يفترض أن ترى تطبيقك ضمن **Your Apps**. + +
+ قائمة "Your Apps" تعرض "My twenty app" +
+ +انقر **My twenty app** لعرض **تسجيل التطبيق** — وهو سجل على مستوى الخادم يصف تطبيقك (الاسم، المعرّف، بيانات اعتماد OAuth، المصدر). يمكن تثبيت تسجيل واحد عبر عدة مساحات عمل على الخادم نفسه. + +
+ تفاصيل تسجيل التطبيق +
+ +انقر **View installed app** لعرض التثبيت في مساحة العمل. تعرض علامة التبويب **About** الإصدار وخيارات الإدارة. + +
+ التطبيق المثبت +
+ +**بعد هذه المرحلة:** لديك دورة تطوير حيّة. حرّر أي ملف في `src/` وسيظهر في واجهة المستخدم. + +### مزامنة لمرة واحدة لـ CI والبرامج النصية + +مرّر `--once` لتشغيل عملية بناء واحدة + مزامنة واحدة ثم الخروج — نفس خط الأنابيب، من دون مراقِب: + +```bash filename="Terminal" +yarn twenty dev --once +``` + +| أمر | السلوك | متى يُستخدم | +| ---------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `yarn twenty dev` | يراقب ويعيد المزامنة عند كل تغيير. يستمر في العمل حتى توقفه. | تطوير محلي تفاعلي. | +| `yarn twenty dev --once` | بناء واحد + مزامنة واحدة، يخرج برمز `0` عند النجاح، و`1` عند الفشل. | CI، وخطافات ما قبل الالتزام، ووكلاء الذكاء الاصطناعي، وسير عمل مكتوب بنصوص. | +| `yarn twenty dev --once --dry-run` | يبني تغييرات البيانات الوصفية ويطبعها **من دون تطبيقها**. | فحص ما الذي سيُغيِّره التزامن قبل تطبيقه. | + +كلا الوضعين يحتاجان إلى جهة بعيدة موثَّقة. راجع قسم [المزامنة والاستعادة](/l/ar/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) للحصول على المزيد من المعلومات حول `--dry-run`. + +### خيارات وضع التطوير + +| خيار | الوصف | +| ------------------------------------- | ------------------------------------------------------------------------------------- | +| `--once` | قم بالإنشاء والمزامنة مرة واحدة، ثم اخرج. | +| `--dry-run` | باستخدام `--once`، يمكنك معاينة تغييرات البيانات الوصفية دون تطبيقها. لا يكتب أي شيء. | +| `--debounceMs \` | اضبط مهلة إزالة الارتداد لتغييرات الملفات بالميلي ثانية (القيمة الافتراضية: `2000`). | +| `--verbose` / `--debug` | إظهار سجلات إنشاء تفصيلية، وطلبات المزامنة، وتتبع الأخطاء. | + +## ما الذي يمكنك بناؤه + +تتكون التطبيقات من **كيانات** — يُعرَّف كل منها كملف TypeScript يحتوي على `export default` واحد: + +| كيان | ماذا يفعل | +| ---------------------- | --------------------------------------------------------------------------------------------- | +| **الكائنات والحقول** | نماذج بيانات مخصّصة (بطاقة بريدية، فاتورة، إلخ) بحقول ذات أنواع محددة | +| **الوظائف المنطقية** | TypeScript على جانب الخادم يتم تشغيله عبر مسارات HTTP، أو جداول cron، أو أحداث قاعدة البيانات | +| **المكوّنات الأمامية** | مكوّنات React تُعرَض داخل واجهة مستخدم Twenty (اللوحة الجانبية، الودجات، قائمة الأوامر) | +| **المهارات والوكلاء** | قدرات الذكاء الاصطناعي — تعليمات قابلة لإعادة الاستخدام ومساعدون مستقلون ذاتيًا | +| **طرق العرض والتنقّل** | طرق عرض قوائم مُعدّة مسبقًا وعناصر قائمة الشريط الجانبي | +| **تخطيطات الصفحات** | صفحات تفاصيل سجلات مخصصة تتضمن علامات تبويب وعناصر واجهة | + +مرجع كامل: [المفاهيم](/l/ar/developers/extend/apps/getting-started/concepts). + +## الخطوات التالية + + + + هوية التطبيق، الدور الافتراضي، وخطّافات التثبيت، والأصول العامة. + + + الكائنات، الحقول، والعلاقات ثنائية الاتجاه. + + + الوظائف المنطقية، المهارات، الوكلاء، واتصالات OAuth. + + + العروض، التنقل، تخطيطات الصفحات، ومكوّنات الواجهة الأمامية. + + + سطر الأوامر (CLI)، الاختبار، الوجهات البعيدة، التكامل المستمر (CI)، ونشر تطبيقك. + + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx new file mode 100644 index 0000000000..924a3971c2 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/scaffolding.mdx @@ -0,0 +1,58 @@ +--- +title: إنشاء القوالب +description: أنشئ ملفات الكيانات بشكل تفاعلي باستخدام yarn twenty dev:add — بما في ذلك الكائنات والحقول والعروض والدوال المنطقية والمزيد. +icon: wand-magic-sparkles +--- + +بدلًا من إنشاء ملفات الكيانات يدويًا، استخدم أداة القوالب التفاعلية: + +```bash filename="Terminal" +yarn twenty dev:add +``` + +ستطلب منك اختيار نوع الكيان وتُرشدك عبر الحقول المطلوبة، ثم تُنشئ ملفًا جاهزًا للاستخدام يحتوي على `universalIdentifier` ثابت واستدعاء `defineEntity()` الصحيح. + +يمكنك أيضًا تمرير نوع الكيان مباشرة لتخطي المطالبة الأولى: + +```bash filename="Terminal" +yarn twenty dev:add object +yarn twenty dev:add logicFunction +yarn twenty dev:add frontComponent +``` + +## أنواع الكيانات المتاحة + +| نوع الكيان | أمر | الملف المُولَّد | +| ------------------ | ---------------------------------------- | ------------------------------------------------------- | +| كائن | `yarn twenty dev:add object` | `src/objects/\.ts` | +| الحقل | `yarn twenty dev:add field` | `src/fields/\.ts` | +| دالة منطقية | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | +| مكوّن أمامي | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | +| دور | `yarn twenty dev:add role` | `src/roles/\.ts` | +| مهارة | `yarn twenty dev:add skill` | `src/skills/\.ts` | +| وكيل | `yarn twenty dev:add agent` | `src/agents/\.ts` | +| عرض | `yarn twenty dev:add view` | `src/views/\.ts` | +| عنصر قائمة التنقّل | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | +| تخطيط الصفحة | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | + +## ما الذي تُنشئه أداة القوالب + +لكل نوع كيان قالب خاص به. على سبيل المثال، يسأل `yarn twenty dev:add object` عن: + +1. **الاسم (مفرد)** — مثل `invoice` +2. **الاسم (جمع)** — مثل `invoices` +3. **التسمية (مفرد)** — تُستمد تلقائيًا من الاسم (مثل `Invoice`) +4. **التسمية (جمع)** — تُملأ تلقائيًا (مثل `Invoices`) +5. **إنشاء عرض وعنصر تنقّل؟** — إذا أجبت بنعم، فستُنشئ أداة القوالب أيضًا عرضًا مطابقًا ورابط شريط جانبي للكائن الجديد. + +أنواع الكيانات الأخرى لها مطالبات أبسط — فمعظمها يطلب اسمًا فقط. + +نوع الكيان `field` أكثر تفصيلاً: يطلب اسم الحقل وتسمية الحقل ونوعه (من قائمة بكل أنواع الحقول المتاحة مثل `TEXT` و`NUMBER` و`SELECT` و`RELATION` وغيرها)، ومعرّف `universalIdentifier` للكائن الهدف. + +## مسار خرج مخصّص + +استخدم العلم `--path` لوضع الملف المُولَّد في موقع مخصّص: + +```bash filename="Terminal" +yarn twenty dev:add logicFunction --path src/custom-folder +``` diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx new file mode 100644 index 0000000000..0cc4bce849 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/getting-started/troubleshooting.mdx @@ -0,0 +1,14 @@ +--- +title: استكشاف الأخطاء وإصلاحها +description: مشكلات التشغيل الأول الشائعة — Docker، إصدار Node، Yarn، والتبعيات. +icon: wrench +--- + +* **أخطاء Docker** — تأكّد من أن Docker Desktop (أو الـ daemon) قيد التشغيل قبل `yarn twenty docker:start`. ستعرض رسالة الخطأ أمر البدء المناسب لنظام التشغيل لديك. +* **إصدار Node غير صحيح** — نحتاج 24 أو أحدث. تحقّق باستخدام `node -v`. +* **Yarn 4 غير موجود** — شغّل `corepack enable`. +* **تبعيات تالفة** — `rm -rf node_modules && yarn install`. +* **أخطاء `twenty-sdk` بعد الترقية إلى v2.8.0** — تم نقله من `dependencies` إلى `devDependencies` في الإصدار v2.8.0. انظر إلى [بنية المشروع → التبعيات](/l/ar/developers/extend/apps/getting-started/project-structure#dependencies). +* **`twenty build` يُصدر تحذيرًا بشأن `twenty-client-sdk` تحت `dependencies`** — يتم توفيره في وقت التشغيل بواسطة Twenty، لذلك يجب نقله إلى `devDependencies` جنبًا إلى جنب مع `twenty-sdk`. انظر إلى [بنية المشروع → التبعيات](/l/ar/developers/extend/apps/getting-started/project-structure#dependencies). + +هل علِقت؟ اطلب المساعدة على [خادم Twenty على Discord](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx new file mode 100644 index 0000000000..a60734ee07 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/command-menu-items.mdx @@ -0,0 +1,148 @@ +--- +title: عناصر قائمة الأوامر +description: اعرض مكوّنات الواجهة الأمامية كإجراءات سريعة ومدخلات في قائمة الأوامر (Cmd+K) باستخدام ‎defineCommandMenuItem‎. +icon: الطرفية +--- + +يُعَدّ **عنصر قائمة الأوامر** الجسر بين المستخدم و[مكوّن الواجهة الأمامية](/l/ar/developers/extend/apps/layout/front-components). يسجّل هذا العنصر المكوّن في قائمة الأوامر في Twenty‏ (Cmd+K)، وبشكل اختياري، كزر إجراء سريع مُثبَّت في الزاوية العلوية اليمنى من الصفحة. + +```ts src/command-menu-items/open-dashboard.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + label: 'Open Dashboard', + shortLabel: 'Dashboard', + icon: 'IconLayoutDashboard', + isPinned: true, + availabilityType: 'GLOBAL', + frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', +}); +``` + +## حقول التكوين + +| الحقل | مطلوب | الوصف | +| --------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `universalIdentifier` | نعم | معرّف فريد ثابت للأمر | +| `label` | نعم | التسمية الكاملة المعروضة في قائمة الأوامر (Cmd+K) | +| `frontComponentUniversalIdentifier` | نعم | قيمة `universalIdentifier` للمكوّن الأمامي الذي يفتحه هذا الأمر | +| `shortLabel` | لا | تسمية أقصر تُعرَض على زر الإجراء السريع المثبّت | +| `icon` | لا | اسم الأيقونة المعروض بجانب التسمية (مثل `'IconBolt'` و`'IconSend'`) | +| `isPinned` | لا | عند كونها `true`، يعرض الأمر كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة | +| `availabilityType` | لا | تتحكّم في مكان ظهور الأمر: `'GLOBAL'` (متاح دائمًا)، و`'RECORD_SELECTION'` (فقط عند تحديد سجلات)، أو `'FALLBACK'` (يُعرَض عند عدم تطابق أي أوامر أخرى) | +| `availabilityObjectUniversalIdentifier` | لا | تقييد الأمر بصفحات نوع كائن معيّن (مثل سجلات Company فقط) | +| `conditionalAvailabilityExpression` | لا | تعبير منطقي يتحكّم ديناميكيًا في الظهور (انظر أدناه) | + +## أوامر بدون واجهة + +يُعَدّ عنصر قائمة الأوامر المقترن بـ[مكوّن واجهة أمامية بدون واجهة](/l/ar/developers/extend/apps/layout/front-components#headless-vs-non-headless) الطريقة القياسية لتوفير إجراء بنقرة واحدة — لتشغيل الشفرة أو التنقّل أو التأكيد ثم التنفيذ. تغطي صفحة مكوّنات الواجهة الأمامية [مكوّنات الأوامر في SDK](/l/ar/developers/extend/apps/layout/front-components#sdk-command-components) ‎(`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`)‎ التي تتعامل مع نمط الإجراء-ثم-إلغاء التركيب. + +تدفق نموذجي: + +```tsx src/front-components/run-action.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const RunAction = () => { + const execute = async () => { + const client = new CoreApiClient(); + await client.mutation({ + createTask: { + __args: { data: { title: 'Created by my app' } }, + id: true, + }, + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', + name: 'run-action', + description: 'Creates a task from the command menu', + component: RunAction, + isHeadless: true, +}); +``` + +```ts src/command-menu-items/run-action.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', + label: 'Run my action', + icon: 'IconPlayerPlay', + frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', +}); +``` + +## تعابير الإتاحة الشرطية + +يتيح لك الحقل `conditionalAvailabilityExpression` التحكّم في وقت ظهور الأمر بناءً على سياق الصفحة الحالي. استورد متغيّرات ومشغّلات مضبوطة الأنواع من `twenty-sdk` لبناء التعابير: + +```ts src/command-menu-items/bulk-update.command-menu-item.ts +import { + defineCommandMenuItem, + objectPermissions, + everyEquals, +} from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: '...', + label: 'Bulk Update', + availabilityType: 'RECORD_SELECTION', + frontComponentUniversalIdentifier: '...', + conditionalAvailabilityExpression: everyEquals( + objectPermissions, + 'canUpdateObjectRecords', + true, + ), +}); +``` + + + `RECORD_SELECTION` تعني بالفعل وجود تحديد غير فارغ — استخدم `numberOfSelectedRecords` فقط لعرض الأعداد المحددة (على سبيل المثال `>= 2`). + + +### متغيّرات السياق + +تُمثّل هذه المتغيّرات الحالة الحالية للصفحة: + +| المتغيّر | النوع | الوصف | +| ------------------------------ | --------- | --------------------------------------------------------------- | +| `pageType` | `string` | نوع الصفحة الحالي (مثل `'RecordIndexPage'` و`'RecordShowPage'`) | +| `isInSidePanel` | `boolean` | ما إذا كان المكوّن معروضًا في لوحة جانبية | +| `numberOfSelectedRecords` | `number` | عدد السجلات المحدّدة حاليًا | +| `isSelectAll` | `boolean` | ما إذا كان "تحديد الكل" مفعّلًا | +| `selectedRecords` | `array` | كائنات السجلات المحدّدة | +| `favoriteRecordIds` | `array` | معرّفات السجلات المفضّلة | +| `objectPermissions` | `object` | الأذونات الخاصة بنوع الكائن الحالي | +| `targetObjectReadPermissions` | `object` | أذونات القراءة للكائن الهدف | +| `targetObjectWritePermissions` | `object` | أذونات الكتابة للكائن الهدف | +| `featureFlags` | `object` | أعلام الميزات المفعَّلة | +| `objectMetadataItem` | `object` | بيانات التعريف لنوع الكائن الحالي | +| `hasAnySoftDeleteFilterOnView` | `boolean` | ما إذا كان العرض الحالي يحتوي على مرشّح حذف منطقي | + +### المُشغِّلات + +جمّع المتغيّرات في تعابير منطقية: + +| المُشغِّل | الوصف | +| ----------------------------------- | ------------------------------------------------------------------ | +| `isDefined(value)` | `true` إذا لم تكن القيمة null/undefined | +| `isNonEmptyString(value)` | `true` إذا كانت القيمة سلسلة غير فارغة | +| `includes(array, value)` | `true` إذا كانت المصفوفة تحتوي على القيمة | +| `includesEvery(array, prop, value)` | `true` إذا كانت خاصية كل عنصر تتضمن القيمة | +| `every(array, prop)` | `true` إذا كانت الخاصية تُقيَّم كقيمة صادقة في كل عنصر | +| `everyDefined(array, prop)` | `true` إذا كانت الخاصية معرّفة في كل عنصر | +| `everyEquals(array, prop, value)` | `true` إذا كانت الخاصية تساوي القيمة في كل عنصر | +| `some(array, prop)` | `true` إذا كانت الخاصية تُقيَّم كقيمة صادقة في عنصر واحد على الأقل | +| `someDefined(array, prop)` | `true` إذا كانت الخاصية معرّفة في عنصر واحد على الأقل | +| `someEquals(array, prop, value)` | `true` إذا كانت الخاصية تساوي القيمة في عنصر واحد على الأقل | +| `someNonEmptyString(array, prop)` | `true` إذا كانت الخاصية سلسلة غير فارغة في عنصر واحد على الأقل | +| `none(array, prop)` | `true` إذا كانت الخاصية تُقيَّم كقيمة زائفة في كل عنصر | +| `noneDefined(array, prop)` | `true` إذا كانت الخاصية غير معرّفة في كل عنصر | +| `noneEquals(array, prop, value)` | `true` إذا لم تكن الخاصية تساوي القيمة في أي عنصر | diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/front-components.mdx new file mode 100644 index 0000000000..7c83aab6eb --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/front-components.mdx @@ -0,0 +1,545 @@ +--- +title: المكوّنات الأمامية +description: أنشئ مكونات React تُعرَض داخل واجهة مستخدم Twenty ضمن بيئة معزولة (sandbox). +icon: window-maximize +--- + +المكوّنات الأمامية هي مكوّنات React تُعرَض مباشرة داخل واجهة مستخدم Twenty. تعمل ضمن **Web Worker** معزول باستخدام Remote DOM — تكون شيفرتك في صندوق عزل لكنها تُعرَض أصيلًا داخل الصفحة، وليس ضمن iframe. + +## أين يمكن استخدام مكوّنات الواجهة الأمامية + +يمكن عرض مكوّنات الواجهة الأمامية في موقعين داخل Twenty: + +* **اللوحة الجانبية** — المكوّنات غير عديمة الرأس تفتح في اللوحة الجانبية اليمنى. هذا هو السلوك الافتراضي عندما يتم تشغيل مكوّن واجهة أمامية من قائمة الأوامر. +* **الويدجت (لوحات المعلومات وصفحات السجلات)** — يمكن تضمين مكوّنات الواجهة الأمامية كويدجت داخل [تخطيطات الصفحات](/l/ar/developers/extend/apps/layout/page-layouts). عند تكوين لوحة معلومات أو تخطيط صفحة سجل، يمكن للمستخدمين إضافة ويدجت لمكوّن واجهة أمامية. + +مكوّن الواجهة الأمامية بمفرده لا يمكن الوصول إليه من واجهة المستخدم — تحتاج إلى *عرضه*. هناك طريقتان للقيام بذلك: + +* **إقرانه مع [عنصر قائمة الأوامر](/l/ar/developers/extend/apps/layout/command-menu-items)** — يقوم بتسجيله في قائمة الأوامر (Cmd+K) واختياريًا كإجراء سريع مُثبّت. +* **تضمينه كويدجت في [تخطيط صفحة](/l/ar/developers/extend/apps/layout/page-layouts)** — يضعه في صفحة تفاصيل السجل أو لوحة المعلومات. + +## مثال أساسي + +أسرع طريقة لرؤية مكوّن الواجهة الأمامية أثناء العمل هي إقرانه مع [`defineCommandMenuItem`](/l/ar/developers/extend/apps/layout/command-menu-items)، بحيث يظهر كزر إجراء سريع في الزاوية العلوية اليمنى من الصفحة: + +```tsx src/front-components/hello-world.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; + +const HelloWorld = () => { + return ( +
+

Hello from my app!

+

This component renders inside Twenty.

+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', + name: 'hello-world', + description: 'A simple front component', + component: HelloWorld, +}); +``` + +```ts src/command-menu-items/hello-world.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345', + shortLabel: 'Hello', + label: 'Hello World', + icon: 'IconBolt', + isPinned: true, + availabilityType: 'GLOBAL', + frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', +}); +``` + +بعد المزامنة باستخدام `yarn twenty dev` (أو تشغيل الأمر لمرة واحدة `yarn twenty dev --once`)، يظهر الإجراء السريع في الزاوية العلوية اليمنى من الصفحة: + +
+ زر إجراء سريع في الزاوية العلوية اليمنى +
+ +انقره لعرض المكوّن مضمنًا داخل الصفحة. + +## حقول التكوين + +| الحقل | مطلوب | الوصف | +| --------------------- | ----- | ------------------------------------------------------------- | +| `universalIdentifier` | نعم | معرّف فريد ثابت لهذا المكوّن | +| `component` | نعم | دالة مكوّن React | +| `name` | لا | الاسم المعروض | +| `description` | لا | وصف لما يفعله المكوّن | +| `isHeadless` | لا | عيّنه على `true` إذا كان المكوّن بلا واجهة مرئية (انظر أدناه) | + +## وضع مكوّن أمامي على صفحة + +إضافةً إلى الأوامر، يمكنك تضمين مكوّن أمامي مباشرةً في صفحة سجل عبر إضافته كودجت في **تخطيط صفحة**. لمزيد من التفاصيل، راجع [تخطيطات الصفحات](/l/ar/developers/extend/apps/layout/page-layouts). + +## عديم الرأس مقابل غير عديم الرأس + +تأتي مكوّنات الواجهة الأمامية بوضعَي عرض يتحكّم بهما الخيار `isHeadless`: + +**غير عديم الرأس (افتراضي)** — يعرض المكوّن واجهة مستخدم مرئية. عند تشغيله من قائمة الأوامر يفتح في اللوحة الجانبية. هذا هو السلوك الافتراضي عندما تكون `isHeadless` تساوي `false` أو يتم تجاهلها. + +**عديم الرأس (`isHeadless: true`)** — يتم تركيب المكوّن بشكل غير مرئي في الخلفية. لا يفتح اللوحة الجانبية. تم تصميم المكوّنات عديمة الرأس لإجراءات تنفّذ منطقًا ثم تُزيل تركيبها ذاتيًا — على سبيل المثال، تشغيل مهمة غير متزامنة، أو الانتقال إلى صفحة، أو إظهار نافذة تأكيد منبثقة. تتوافق بشكل طبيعي مع مكوّنات Command في SDK الموصوفة أدناه. + +```tsx src/front-components/sync-tracker.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component'; +import { useEffect } from 'react'; + +const SyncTracker = () => { + const recordId = useRecordId(); + + useEffect(() => { + enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' }); + }, [recordId]); + + return null; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'sync-tracker', + description: 'Tracks record views silently', + isHeadless: true, + component: SyncTracker, +}); +``` + +نظرًا لأن المكوّن يُرجع `null`، فإن Twenty يتخطّى عرض حاوية له — ولن تظهر مساحة فارغة في التخطيط. لا يزال لدى المكوّن إمكانية الوصول إلى جميع الخطافات وواجهة برمجة الاتصال مع المضيف. + +## مكوّنات Command في SDK + +توفر حزمة `twenty-sdk` أربعة مكوّنات مساعدة من نوع Command مصممة للمكوّنات عديمة الرأس في الواجهة الأمامية. كل مكوّن ينفّذ إجراءً عند التركيب، ويتعامل مع الأخطاء بعرض إشعار Snackbar، ويزيل تركيب مكوّن الواجهة الأمامية تلقائيًا عند الانتهاء. + +استوردها من `twenty-sdk/command`: + +* **`Command`** — يشغّل رد نداء غير متزامن عبر الخاصية `execute`. +* **`CommandLink`** — ينتقل إلى مسار في التطبيق. الخصائص: `to`، `params`، `queryParams`، `options`. +* **`CommandModal`** — يفتح نافذة تأكيد منبثقة. إذا أكّد المستخدم، ينفّذ رد النداء `execute`. الخصائص: `title`، `subtitle`، `execute`، `confirmButtonText`، `confirmButtonAccent`. +* **`CommandOpenSidePanelPage`** — يفتح صفحة محدّدة في اللوحة الجانبية. الخصائص: `page`، `pageTitle`، `pageIcon`. + +فيما يلي مثال كامل لمكوّن واجهة أمامية عديم الرأس يستخدم `Command` لتشغيل إجراء من قائمة الأوامر: + +```tsx src/front-components/run-action.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const RunAction = () => { + const execute = async () => { + const client = new CoreApiClient(); + + await client.mutation({ + createTask: { + __args: { data: { title: 'Created by my app' } }, + id: true, + }, + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', + name: 'run-action', + description: 'Creates a task from the command menu', + component: RunAction, + isHeadless: true, +}); +``` + +```ts src/command-menu-items/run-action.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', + label: 'Run my action', + icon: 'IconPlayerPlay', + frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', +}); +``` + +ومثال يستخدم `CommandModal` لطلب التأكيد قبل التنفيذ: + +```tsx src/front-components/delete-draft.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { CommandModal } from 'twenty-sdk/command'; + +const DeleteDraft = () => { + const execute = async () => { + // perform the deletion + }; + + return ( + + ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', + name: 'delete-draft', + description: 'Deletes a draft with confirmation', + component: DeleteDraft, + isHeadless: true, +}); +``` + +## استدعاء دالة منطقية + +تعمل مكونات الواجهة الأمامية في المتصفح داخل Web Worker معزول، بينما تعمل [الدوال المنطقية](/l/ar/developers/extend/apps/logic/logic-functions) على جانب الخادم. لا توجد استدعاءات مباشرة ضمن العملية بين الاثنين — بدلاً من ذلك، يصل مكون الواجهة الأمامية إلى الدالة المنطقية عبر HTTP. + +يتم إتاحة الدالة المنطقية المُعلَنة باستخدام `httpRouteTriggerSettings` تحت نقطة النهاية `/s/` عند `${TWENTY_API_URL}/s\`. يستدعي مكون الواجهة الأمامية ذلك المسار باستخدام `RestApiClient` من `twenty-client-sdk/rest`، والذي يقوم بالمصادقة باستخدام `TWENTY_APP_ACCESS_TOKEN` الذي تقوم Twenty بحقنه في الـ worker. + +تم تصميم `RestApiClient` خصيصًا لهذا الغرض. يقوم بقراءة `TWENTY_API_URL` و`TWENTY_APP_ACCESS_TOKEN` من بيئة الـ worker، وإرفاق ترويسة `Authorization: Bearer`، وتسلسل وتحليل JSON، وإثارة `RestApiClientError` عندما يكون الرمز المميز أو عنوان URL مفقودًا أو عندما يكون الرد غير 2xx — حتى لا تعيد تنفيذ هذا الـ boilerplate في كل مكون. + +يمكن لمكون واجهة أمامية عديم الرأس تنفيذ الاستدعاء عند التركيب عبر مكون `Command`، ثم إلغاء التركيب تلقائيًا: + +```tsx src/front-components/sync-prs.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { RestApiClient } from 'twenty-client-sdk/rest'; + +const SyncPrs = () => { + const execute = async () => { + const client = new RestApiClient(); + + await client.post('/s/github/fetch-prs', { + owner: 'twentyhq', + repo: 'twenty', + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'sync-prs', + description: 'Triggers the fetch-prs logic function', + isHeadless: true, + component: SyncPrs, +}); +``` + +المسار المُمرَّر إلى العميل هو المسار العام للمسار (route) — قيمة `httpRouteTriggerSettings.path` الخاصة بدالة المنطق (logic function) مع إضافة البادئة `/s`. أبقِ `isAuthRequired: true`؛ يزوّد العميل مكوّنك برمز وصول التطبيق الذي تُصدِره Twenty: + +```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, + }, +}); +``` + + +يتم حقن `TWENTY_API_URL` و`TWENTY_APP_ACCESS_TOKEN` تلقائيًا — انظر [متغيرات التطبيق](#application-variables). نظرًا لأن متغيرات التطبيق السرية لا تُعرَض أبدًا على مكونات الواجهة الأمامية، احتفِظ بمفاتيح واجهة برمجة التطبيقات والمنطق الحساس الآخر داخل الدالة المنطقية، وليس في مكون الواجهة الأمامية. + + +### مرجع RestApiClient + +استورد `RestApiClient` من `twenty-client-sdk/rest`. ينتمي إلى نفس عائلة العملاء مثل `CoreApiClient` و`MetadataApiClient`، لكنه يستهدف مسارات HTTP الخاصة بتطبيقك بدلاً من واجهة GraphQL API. + +| طريقة | الوصف | +| --------------------------------- | -------------------------- | +| `get(path, options?)` | يرسل طلبًا من نوع `GET` | +| `post(path, body?, options?)` | يرسل طلبًا من نوع `POST` | +| `put(path, body?, options?)` | يرسل طلبًا من نوع `PUT` | +| `patch(path, body?, options?)` | يرسل طلبًا من نوع `PATCH` | +| `delete(path, options?)` | يرسل طلبًا من نوع `DELETE` | +| `request(method, path, options?)` | طلب عام بأي طريقة HTTP | + +تدعم `options` كلًا من `headers` و`query` (سجل لمعاملات query-string؛ يتم تخطي القيم nullish) و`AbortSignal` عبر `signal`. يتم تسلسل كائن `body` غير من النوع `FormData` إلى JSON تلقائيًا. عند حدوث `401`، يقوم العميل بتحديث رمز الوصول مرة واحدة عبر المضيف ثم يعيد محاولة الطلب. + +يتم تحديد عنوان URL الأساسي والرمز من بيئة التشغيل بشكل افتراضي. مرِّر معاملات تجاوز (overrides) إلى المُنشئ (constructor) عند الحاجة — على سبيل المثال في الاختبارات: + +```ts +const client = new RestApiClient({ + baseUrl: 'https://api.example.com', + token: 'my-token', +}); +``` + +ترمي الطلبات الفاشلة خطأً من نوع `RestApiClientError` يعرِّض خصائص `status` و`statusText` و`url` بالإضافة إلى `body` بعد تحليله (parsed): + +```tsx +import { RestApiClient, RestApiClientError } from 'twenty-client-sdk/rest'; + +const client = new RestApiClient(); + +try { + const prs = await client.get('/s/github/fetch-prs', { + query: { state: 'open' }, + }); +} catch (error) { + if (error instanceof RestApiClientError) { + console.error(error.status, error.body); + } +} +``` + +## الوصول إلى سياق وقت التشغيل + +داخل مكوّنك، استخدم خطافات SDK للوصول إلى المستخدم الحالي، والسجل، ومثيل المكوّن: + +```tsx src/front-components/record-info.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { + useUserId, + useRecordId, + useFrontComponentId, +} from 'twenty-sdk/front-component'; + +const RecordInfo = () => { + const userId = useUserId(); + const recordId = useRecordId(); + const componentId = useFrontComponentId(); + + return ( +
+

User: {userId}

+

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

+

Component: {componentId}

+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', + name: 'record-info', + component: RecordInfo, +}); +``` + +الخطافات المتاحة: + +| الخطّاف | القيم المعادة | الوصف | +| --------------------------------------------- | --------------------- | ---------------------------------------------------------------------- | +| `useUserId()` | `string` أو `null` | معرّف المستخدم الحالي | +| `useSelectedRecordIds()` | `string[]` | جميع معرّفات السجلات المحددة (مصفوفة فارغة إذا لم يتم تحديد أي منها) | +| `useRecordId()` | `string` أو `null` | **مهمل.** استخدم `useSelectedRecordIds()` بدلاً من ذلك | +| `useFrontComponentId()` | `string` | معرّف مثيل هذا المكوّن | +| `useColorScheme()` | `'light'` أو `'dark'` | نظام الألوان النشط لواجهة المستخدم المضيفة (`System` تم تحديده بالفعل) | +| `useFrontComponentExecutionContext(selector)` | يختلف | الوصول إلى سياق التنفيذ الكامل عبر دالة محدِّد | + +## متغيرات التطبيق + +متغيرات التطبيق المُعرَّفة في [`defineApplication()`](/l/ar/developers/extend/apps/config/application) مع `isSecret: false` تكون متاحة داخل مكوّنات الواجهة عبر أداة `getApplicationVariable`: + +```tsx src/front-components/greeting.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getApplicationVariable } from 'twenty-sdk/front-component'; + +const Greeting = () => { + const recipientName = getApplicationVariable('DEFAULT_RECIPIENT_NAME') ?? 'World'; + + return

Hello, {recipientName}!

; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'greeting', + component: Greeting, +}); +``` + + +المتغيرات السرّية (`isSecret: true`) **لا** يتم كشفها لمكوّنات الواجهة. هي متاحة فقط في [دوال المنطق](/l/ar/developers/extend/apps/logic/logic-functions)، التي تعمل على جهة الخادم. هذا يمنع إرسال القيم الحساسة مثل مفاتيح API إلى المتصفح. + + +متغيرات النظام التالية تكون متاحة دائمًا عبر `process.env`: + +| المتغيّر | الوصف | +| ------------------------- | --------------------------------------- | +| `TWENTY_API_URL` | عنوان URL الأساسي لـ Twenty API | +| `TWENTY_APP_ACCESS_TOKEN` | رمز مميز قصير العمر مُقيَّد بدور تطبيقك | + +## واجهة الاتصال مع المضيف + +يمكن للمكوّنات الأمامية تشغيل التنقّل والنوافذ المنبثقة والإشعارات باستخدام دوال من `twenty-sdk`: + +| دالة | الوصف | +| ----------------------------------------------- | ------------------------------ | +| `navigate(to, params?, queryParams?, options?)` | الانتقال إلى صفحة داخل التطبيق | +| `openSidePanelPage(params)` | فتح لوحة جانبية | +| `closeSidePanel()` | إغلاق اللوحة الجانبية | +| `openCommandConfirmationModal(params)` | عرض مربع حوار تأكيد | +| `enqueueSnackbar(params)` | عرض إشعار توست | +| `unmountFrontComponent()` | إلغاء تركيب المكوّن | +| `updateProgress(progress)` | تحديث مؤشّر التقدّم | + +فيما يلي مثال يستخدم واجهة برمجة تطبيقات المضيف لعرض Snackbar وإغلاق اللوحة الجانبية بعد اكتمال الإجراء: + +```tsx src/front-components/archive-record.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { useRecordId } from 'twenty-sdk/front-component'; +import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const ArchiveRecord = () => { + const recordId = useRecordId(); + + const handleArchive = async () => { + const client = new CoreApiClient(); + + await client.mutation({ + updateTask: { + __args: { id: recordId, data: { status: 'ARCHIVED' } }, + id: true, + }, + }); + + await enqueueSnackbar({ + message: 'Record archived', + variant: 'success', + }); + + await closeSidePanel(); + }; + + return ( +
+

Archive this record?

+ +
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', + name: 'archive-record', + description: 'Archives the current record', + component: ArchiveRecord, +}); +``` + +### العمل مع سجلات متعددة + +استخدم `useSelectedRecordIds()` لمعالجة عدة سجلات محددة. هذا مفيد للعمليات المجمّعة: + +```tsx src/front-components/bulk-export.tsx +import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define'; +import { useSelectedRecordIds } from 'twenty-sdk/front-component'; +import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const BulkExport = () => { + const selectedRecordIds = useSelectedRecordIds(); + + const handleExport = async () => { + const client = new CoreApiClient(); + + for (const recordId of selectedRecordIds) { + await client.mutation({ + updateTask: { + __args: { id: recordId, data: { exported: true } }, + id: true, + }, + }); + } + + await enqueueSnackbar({ + message: `Exported ${selectedRecordIds.length} records`, + variant: 'success', + }); + + await closeSidePanel(); + }; + + return ( +
+

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

+ +
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901', + name: 'bulk-export', + description: 'Export selected records', + component: BulkExport, + command: { + universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902', + label: 'Bulk Export', + availabilityType: 'RECORD_SELECTION', + conditionalAvailabilityExpression: numberOfSelectedRecords > 0, + }, +}); +``` + +## الأصول العامة + +يمكن للمكوّنات الأمامية الوصول إلى ملفات من دليل `public/` للتطبيق باستخدام `getPublicAssetUrl`: + +```tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const Logo = () => Logo; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'logo', + component: Logo, +}); +``` + +راجع [قسم الأصول العامة](/l/ar/developers/extend/apps/config/public-assets) للتفاصيل. + +## التنسيق + +تدعم المكوّنات الأمامية عدة أساليب للتنسيق. يمكنك استخدام: + +* **أنماط مضمنة** — `style={{ color: 'red' }}` +* **مكوّنات Twenty لواجهة المستخدم** — استورد من `twenty-sdk/ui` (Button وTag وStatus وChip وAvatar وغيرها) +* **Emotion** — CSS-in-JS مع `@emotion/react` +* **Styled-components** — أنماط `styled.div` +* **Tailwind CSS** — أصناف مساعدة +* **أي مكتبة CSS-in-JS** متوافقة مع React + +```tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Button, Tag, Status } from 'twenty-sdk/ui'; + +const StyledWidget = () => { + return ( +
+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', + name: 'styled-widget', + component: StyledWidget, +}); +``` diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx new file mode 100644 index 0000000000..43fe180a16 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/navigation-menu-items.mdx @@ -0,0 +1,44 @@ +--- +title: عناصر قائمة التنقّل +description: أضِف إدخالات مخصّصة إلى الشريط الجانبي لمساحة العمل — روابط إلى العروض المحفوظة أو عناوين URL خارجية. +icon: bars +--- + +يُعَدّ **عنصر قائمة التنقّل** إدخالًا في الشريط الجانبي الأيسر. استخدم `defineNavigationMenuItem()` لتوفير روابط شريط جانبي مخصّصة — عادةً واحدًا لكل [عرض](/l/ar/developers/extend/apps/layout/views) توفّره — أو للإشارة إلى عناوين URL خارجية. + +```ts src/navigation-menu-items/example-navigation-menu-item.ts +import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define'; +import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from '../views/example-view'; + +export default defineNavigationMenuItem({ + universalIdentifier: '9327db91-afa1-41b6-bd9d-2b51a26efb4c', + name: 'example-navigation-menu-item', + icon: 'IconList', + color: 'blue', + position: 0, + type: NavigationMenuItemType.VIEW, + viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER, +}); +``` + +## النقاط الرئيسية + +* يحدّد `type` ما الذي يرتبط به عنصر القائمة. كل نوع يقترن بحقل معرّف محدّد: + + | النوع | ماذا يفعل | حقل مطلوب | + | ------------------------------------ | ------------------------------------- | -------------------------------------------------------------------------- | + | `NavigationMenuItemType.VIEW` | يفتح عرضًا محفوظًا | `viewUniversalIdentifier` | + | `NavigationMenuItemType.LINK` | يفتح عنوان URL خارجيًا | `link` | + | `NavigationMenuItemType.FOLDER` | يجمع العناصر المتداخلة تحت تسمية | `name` (وتشير العناصر الفرعية إلى المجلّد عبر `folderUniversalIdentifier`) | + | `NavigationMenuItemType.OBJECT` | يفتح صفحة الفهرس الافتراضية لكائنٍ ما | `targetObjectUniversalIdentifier` | + | `NavigationMenuItemType.PAGE_LAYOUT` | يفتح مخطّط صفحة مستقلًا | `pageLayoutUniversalIdentifier` | + +* `position` يتحكّم في الترتيب ضمن الشريط الجانبي. + +* `icon` و`color` اختياريان ويخصّصان مظهر الإدخال. + +* `folderUniversalIdentifier` متاح أيضًا على أي عنصر لوضعه متداخلًا داخل عنصر أب من النوع `FOLDER`. + + +**مشكلة شائعة:** إنشاء كائن بدون عرض مرتبط + عنصر قائمة تنقّل يجعل ذلك الكائن غير مرئي للمستخدمين. ما لم يكن كائنًا تقنيًا/داخليًا، يجب أن يحتوي كل كائن مخصّص على عرض افتراضي *وعنصر* في الشريط الجانبي يشير إليه. + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/overview.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/overview.mdx new file mode 100644 index 0000000000..dff8c971f8 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/overview.mdx @@ -0,0 +1,56 @@ +--- +title: نظرة عامة +description: ضع تطبيقك داخل واجهة مستخدم Twenty — إدخالات الشريط الجانبي، العروض المحفوظة، علامات تبويب صفحة السجل، ومكوّنات React المعزولة (sandboxed). +icon: table-columns +--- + +تمثّل **طبقة التخطيط** في تطبيق Twenty كل ما يراه المستخدم: مكان ظهور التطبيق في الشريط الجانبي، العروض القائِمية التي يوفّرها، كيفية ترتيب صفحات تفاصيل السجل، وأي مكوّنات React مخصّصة يتم عرضها داخل تلك الصفحات. + +```text + Sidebar Record list Record detail page + ─────── ─────────── ────────────────── + [📋 My View] ────▶ ┌──────────┐ ┌─────────────────────┐ + [📋 Drafts ] │ Companies│ │ Tabs: [Overview ] │ + [📋 Inbox ] │ ──────── │ │ [Notes ] │ + ▲ │ Apple │ │ [Hello ]◀──── definePageLayoutTab + │ │ Acme │ │ │ adds a tab... + └ defineNavi- │ … │ │ ┌────────────────┐ │ + gationMenu- └────▲─────┘ │ │ │ │ + Item points │ │ │ React UI │◀── …with a + to a defineView │ │ │ (sandboxed in │ │ defineFrontComponent + └ defineView │ │ a Worker) │ │ widget inside + picks columns │ └────────────────┘ │ + and filters └─────────────────────┘ +``` + +## في هذا القسم + + + + `defineView` — تكوينات قوائم محفوظة: الأعمدة الظاهرة، وعوامل التصفية، والمجموعات. + + + `defineNavigationMenuItem` — إدخالات في الشريط الجانبي تشير إلى العروض أو عناوين URL الخارجية. + + + `definePageLayout` و`definePageLayoutTab` — علامات التبويب والويدجتات في صفحة تفاصيل السجل. + + + `defineFrontComponent` — مكوّنات React معزولة (sandboxed) يتم عرضها داخل Twenty. + + + `defineCommandMenuItem` — تسجيل مكوّنات الواجهة الأمامية كإدخالات Cmd+K وإجراءات سريعة. + + + +## أين يظهر التطبيق + +| موضع الظهور | ما الذي يتحكّم فيه | كيان | +| ------------------------- | ------------------------------------------------------------------------- | ----------------------------------------- | +| **الشريط الجانبي** | إدخال مخصّص يربط بعرض محفوظ أو عنوان URL خارجي | `defineNavigationMenuItem` | +| **قائمة السجلات** | تكوين محفوظ لكائن — الأعمدة الظاهرة، والترتيب، وعوامل التصفية، والمجموعات | `defineView` | +| **صفحة تفاصيل السجل** | علامات التبويب والويدجتات في صفحة السجل (لكائنك الخاص أو لكائن قياسي) | `definePageLayout`, `definePageLayoutTab` | +| **داخل أي مما سبق** | ويدجت React مخصّص — أزرار، نماذج، لوحات بيانات، تكاملات | `defineFrontComponent` | +| **قائمة الأوامر (Cmd+K)** | إجراء سريع مُثبّت أو أمر مخفي | `defineCommandMenuItem` | + +تعمل مكوّنات الواجهة الأمامية داخل Web Worker معزول باستخدام Remote DOM — يتم عرضها بشكل أصيل داخل الصفحة (وليس داخل iframe)، لكنها لا تستطيع الوصول مباشرةً إلى صفحة المضيف أو إلى DOM. يحدث التواصل مع Twenty من خلال واجهة API للمضيف تعتمد تمرير الرسائل. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/page-layouts.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/page-layouts.mdx new file mode 100644 index 0000000000..58fe0cc2b0 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/page-layouts.mdx @@ -0,0 +1,132 @@ +--- +title: تخطيطات الصفحات +description: خصص صفحات تفاصيل السجل — الألسنة، وعناصر الواجهة (widgets)، وأماكن عرض مكوّنات الواجهة الأمامية (front components) — باستخدام `definePageLayout` و `definePageLayoutTab`. +icon: table-columns +--- + +يتحكم **تخطيط الصفحة** في كيفية ترتيب صفحة تفاصيل السجل: ما هي الألسنة التي تظهر وما عناصر الواجهة (widgets) التي تحتوي عليها. استخدم `definePageLayout()` للتصريح عن تخطيط لكائن تملكه، أو `definePageLayoutTab()` لإضافة لسان واحد إلى تخطيط موجود مسبقًا (سواء كان مِلكك أو تخطيط Twenty قياسيًا). + +| حالة استخدام | كيان | +| -------------------------------------------------------------- | --------------------- | +| عرِّف التخطيط الكامل لصفحة سجل على كائن تملكه | `definePageLayout` | +| أضف لسانًا واحدًا إلى تخطيط موجود (لكائن تملكه أو تخطيط قياسي) | `definePageLayoutTab` | + +## definePageLayout + +استخدم هذا عندما تملك صفحة التفاصيل بالكامل — عادةً لكائن مخصص قمت بتعريفه بنفسك. + +```ts src/page-layouts/example-record-page-layout.ts +import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define'; +import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; +import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; + +export default definePageLayout({ + universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134', + name: 'Example Record Page', + type: 'RECORD_PAGE', + objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, + tabs: [ + { + universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5', + title: 'Hello World', + position: 50, + icon: 'IconWorld', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + widgets: [ + { + universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d', + title: 'Hello World', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], + }, + ], +}); +``` + +### النقاط الرئيسية + +* `type` يكون عادة `'RECORD_PAGE'` لتخصيص عرض التفاصيل لكائن محدّد. +* `objectUniversalIdentifier` يحدّد الكائن الذي ينطبق عليه هذا التخطيط. +* يُعرّف كل `tab` قسمًا من الصفحة مع `title` و`position` و`layoutMode` (`CANVAS` لتخطيط حرّ). +* يمكن لكل `widget` داخل لسان أن يعرض [front component](/l/ar/developers/extend/apps/layout/front-components)، أو قائمة علاقات، أو أنواعًا أخرى من عناصر الواجهة (widgets) المدمجة. +* `position` على الألسنة يتحكّم في ترتيبها. استخدم قيمًا أعلى (مثل 50) لوضع الألسنة المخصّصة بعد الألسنة المدمجة. + +## definePageLayoutTab + +استخدم هذا عندما تريد فقط **إضافة** لسان إلى تخطيط موجود — على سبيل المثال، لسان تحليلات في صفحة Company القياسية، أو لسان ملخص بالذكاء الاصطناعي مرفق بتخطيط الكائن الخاص بك. + +```ts src/page-layouts/example-extra-tab.ts +import { + definePageLayoutTab, + PageLayoutTabLayoutMode, + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; +import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; + +export default definePageLayoutTab({ + universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', + pageLayoutUniversalIdentifier: + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage + .universalIdentifier, + title: 'Hello World', + position: 1000, + icon: 'IconWorld', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + widgets: [ + { + universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002', + title: 'Hello World', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], +}); +``` + +### النقاط الرئيسية + +* إن `pageLayoutUniversalIdentifier` **مطلوب** ويجب أن يشير إلى تخطيط صفحة موجود بالفعل وقت التثبيت — سواء كان تخطيط Twenty قياسيًا أو تخطيطًا معرّفًا بواسطة تطبيقك الخاص. المراجع المتقاطعة بين التطبيقات إلى التخطيطات المملوكة لتطبيق آخر مُثبَّت غير مدعومة حاليًا. عند فقدان التخطيط الأب، يفشل التثبيت مع ظهور خطأ تحقق واضح. + +* بالنسبة لتخطيطات Twenty القياسية، استورد المعرفات من `twenty-sdk/define`: + + ```ts + import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define'; + + // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier + // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier + // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier + // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier + // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier + // … + ``` + + كل إدخال تخطيط يوفّر أيضًا `tabs` الخاصة به و`widgets` التابعة لها، بحيث يمكنك الرجوع إلى أي مستوى: + + ```ts + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.universalIdentifier + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets.fields.universalIdentifier + ``` + + يتوفر أيضًا اسم قصير `STANDARD_PAGE_LAYOUT`: + + ```ts + import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define'; + + STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier; + ``` + +* يكون نطاق `widgets` مقتصرًا على هذا اللسان فقط — فهي تشير إلى [front components](/l/ar/developers/extend/apps/layout/front-components)، والعروض، وما إلى ذلك تمامًا مثل عناصر الواجهة (widgets) المُعرَّفة مضمّنة داخل `definePageLayout`. + +* `position` يتحكّم في الترتيب مقارنةً بعلامات التبويب الموجودة على التخطيط المستهدف. اختر قيمة تضع علامة التبويب الخاصة بك في الموضع الذي تريده بالنسبة إلى علامات التبويب المضمنة. + +* استخدم هذا بدلًا من `definePageLayout` عندما تريد فقط الإضافة إلى تخطيط موجود. استخدم `definePageLayout` عندما تملك التخطيط بالكامل. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx new file mode 100644 index 0000000000..5a0ae63a70 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/layout/views.mdx @@ -0,0 +1,97 @@ +--- +title: العروض +description: قم بتوفير عروض محفوظة مُعدّة مسبقًا — ترتيب الأعمدة، المرشّحات، المجموعات — للكائنات في تطبيقك. +icon: list +--- + +يُعد **العرض** تكوينًا محفوظًا لكيفية عرض سجلات كائن معيّن: ما هي الحقول التي تظهر، وترتيبها، وما إذا كانت مرئية، وأي عوامل تصفية أو مجموعات مُطبَّقة. استخدم `defineView()` لتوفير عروض مُعدّة مسبقًا مع تطبيقك — عادةً عرض فهرس افتراضي لكل كائن مخصص تقوم بإنشائه. + +```ts src/views/example-view.ts +import { defineView, ViewKey } from 'twenty-sdk/define'; +import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; +import { NAME_FIELD_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; + +export default defineView({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + name: 'All example items', + objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, + icon: 'IconList', + key: ViewKey.INDEX, + position: 0, + fields: [ + { + universalIdentifier: 'f926bdb7-6af7-4683-9a09-adbca56c29f0', + fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER, + position: 0, + isVisible: true, + size: 200, + }, + ], +}); +``` + +## النقاط الرئيسية + +* `objectUniversalIdentifier` يحدّد الكائن الذي ينطبق عليه هذا العرض. يمكن أن يكون كائنًا مخصصًا قمتَ بتعريفه أو كائن Twenty قياسيًا. +* يحدّد `key` نوع العرض — يمثّل `ViewKey.INDEX` عرض القائمة الرئيسي للكائن. +* يتحكّم `fields` في الأعمدة التي تظهر وترتيبها. يشير كل حقل إلى `fieldMetadataUniversalIdentifier`. +* يمكنك أيضًا تعريف `filters` و`filterGroups` و`groups` و`fieldGroups` لتكوينات أكثر تقدمًا. +* يتحكّم `position` في الترتيب عند وجود عدة عروض لنفس الكائن. + +## الفلاتر + +يمكن أن تأتي طريقة العرض مع عوامل تصفية مُطبَّقة مسبقًا. لكل عامل تصفية ثلاثة مكونات: **الحقل** الذي تُطبَّق عليه التصفية، و**المعامل** (كيفية المقارنة)، و**القيمة** (ما تتم المقارنة به). يجب أن تتطابق العناصر الثلاثة جميعًا — حيث سيتم رفض استخدام معامل لا ينطبق على نوع الحقل في وقت المزامنة. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### المعاملات المدعومة حسب نوع الحقل + +| نوع الحقل | العوامل المدعومة | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> يمكن لأنواع الحقول ذات الأسماء المتشابهة أن تستخدم عوامل مختلفة تمامًا — حيث يُعد `SELECT` و`MULTI_SELECT` حالة شائعة. + +### شكل القيمة لكل عامل + +حقل `value` هو دائمًا قيمة قابلة للتسلسل إلى JSON، لكن شكله المتوقَّع يعتمد على العامل: + +| عائلة العامل | شكل القيمة | مثال | +| ------------------------------------------------------- | -------------------------------------- | ------------------------ | +| `IS`, `IS_NOT` على `SELECT` | مصفوفة من مفاتيح الخيارات (سلاسل نصية) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` على `MULTI_SELECT` | مصفوفة من مفاتيح الخيارات (سلاسل نصية) | `['TAG_A']` | +| `IS`, `IS_NOT` على `RELATION` | مصفوفة من معرّفات السجلات (uuids) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` على الحقول المشابهة للنص | سلسلة نصية | `'acme'` | +| `IS`, `IS_NOT` على `NUMBER` | سلسلة نصية (القيمة) | `'5'` | +| `IS` على `RATING` / `UUID` | سلسلة نصية (القيمة) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | سلسلة نصية (الحد) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` على `DATE` / `DATE_TIME` | سلسلة نصية بتنسيق ISO 8601 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | سلسلة فارغة | `''` | +| `IS` على `BOOLEAN` | `'true'` أو `'false'` | `'true'` | + +## كيفية ظهور العروض في واجهة المستخدم + +لا يمكن الوصول إلى العرض بمفرده من الشريط الجانبي. لجعله يظهر هناك، قم بربطه مع [عنصر قائمة تنقّل](/l/ar/developers/extend/apps/layout/navigation-menu-items) من النوع `VIEW` يشير إلى قيمة `universalIdentifier` الخاصة بالعرض. هذا هو النمط القياسي: عادةً ما يوفّر كل كائن مخصص عرضًا افتراضيًا + إدخالًا في الشريط الجانبي يفتحه. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic/connections.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic/connections.mdx new file mode 100644 index 0000000000..a22d7b4c78 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/logic/connections.mdx @@ -0,0 +1,192 @@ +--- +title: الاتصالات +description: اسمح لتطبيقك بالتصرف نيابةً عن المستخدم في خدمات الجهات الخارجية عبر OAuth. +icon: plug +--- + +الاتصالات هي بيانات اعتماد يحتفظ بها المستخدم لخدمة خارجية (Linear وGitHub وSlack، ...). يحدّد تطبيقك **كيف** يتم الحصول على تلك بيانات الاعتماد — **موفّر اتصال** — ويستخدمها وقت التشغيل لإجراء استدعاءات مُصادَقة إلى واجهة برمجة تطبيقات الطرف الثالث. + +حاليًا لا يُدعَم سوى OAuth 2.0. ستندمج الأنواع المستقبلية من بيانات الاعتماد (رموز الوصول الشخصية، مفاتيح API، المصادقة الأساسية) مع نفس الواجهة — التطبيقات التي تستخدم بالفعل `defineConnectionProvider({ type: 'oauth', ... })` لن تحتاج إلى الترحيل. + + + + + +يصف موفّر الاتصال عملية المصافحة الخاصة بـ OAuth التي يحتاجها تطبيقك. ينقر المستخدم على "إضافة اتصال" في إعدادات تطبيقك، ويُكمل شاشة موافقة المزوّد، ثم يتم إنشاء صف `ConnectedAccount` في مساحة عمله. + +يتطلّب الإعداد العملي **ملفّين** — موفّر الاتصال، وتصريح `serverVariables` مطابق في `defineApplication` يحتفظ ببيانات اعتماد عميل OAuth. + +```ts src/connection-providers/linear-connection.ts +import { defineConnectionProvider } from 'twenty-sdk/define'; + +export default defineConnectionProvider({ + universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f', + name: 'linear', + displayName: 'Linear', + icon: 'IconBrandLinear', + type: 'oauth', + oauth: { + authorizationEndpoint: 'https://linear.app/oauth/authorize', + tokenEndpoint: 'https://api.linear.app/oauth/token', + scopes: ['read', 'write'], + // These must match keys in `defineApplication.serverVariables` below. + clientIdVariable: 'LINEAR_CLIENT_ID', + clientSecretVariable: 'LINEAR_CLIENT_SECRET', + // Optional: defaults to 'json'. Some providers (Linear, Slack) want + // 'form-urlencoded' for the token request. + tokenRequestContentType: 'form-urlencoded', + // Optional: defaults to true. Disable only if the provider rejects PKCE. + usePkce: false, + // Optional: extra query params on the authorize URL. + // authorizationParams: { prompt: 'consent' }, + // Optional: provider's RFC 7009 token revocation endpoint, called on disconnect. + // revokeEndpoint: 'https://example.com/oauth/revoke', + }, +}); +``` + +```ts src/application.config.ts +import { defineApplication } from 'twenty-sdk/define'; + +export default defineApplication({ + universalIdentifier: '...', + displayName: 'Linear', + description: 'Connect Linear to Twenty.', + // OAuth client credentials live on the app registration (one OAuth app per + // Twenty server, configured by the admin) — not per-workspace. Declare them + // as serverVariables so the admin can fill them in once for all installs. + serverVariables: { + LINEAR_CLIENT_ID: { + description: 'OAuth client ID from your Linear OAuth application.', + isSecret: false, + isRequired: true, + }, + LINEAR_CLIENT_SECRET: { + description: 'OAuth client secret from your Linear OAuth application.', + isSecret: true, + isRequired: true, + }, + }, +}); +``` + +النقاط الرئيسية: + +* `name` هي سلسلة المعرّف الفريدة المستخدمة في `listConnections({ providerName })` (بصيغة kebab-case، ويجب أن تطابق `^[a-z][a-z0-9-]*$`). +* `displayName` يظهر في علامة تبويب إعدادات كل تطبيق وفي قائمة أدوات الذكاء الاصطناعي. +* `clientIdVariable` / `clientSecretVariable` هي **أسماء**، وليست قيماً — ويجب أن تطابق المفاتيح المصرَّح بها في `defineApplication.serverVariables`. يُدخِل مسؤول الخادم القيم الفعلية `client_id` و`client_secret` عبر واجهة تسجيل التطبيق، ولا تُضمَّن أبدًا في مستودعك. +* استخدم `serverVariables` (وليس `applicationVariables`) — بيانات اعتماد OAuth على مستوى الخادم، ويوجد تطبيق OAuth واحد لكل خادم Twenty. +* إلى أن يتم ملء كلا `serverVariables`، تعرض علامة تبويب إعدادات كل تطبيق تلميح "بحاجة إلى مسؤول الخادم" ويكون زر "إضافة اتصال" معطّلًا. +* `type: 'oauth'` هي القيمة الوحيدة المدعومة حاليًا. المميِّز متوافق مع الإصدارات المستقبلية: الأنواع المستقبلية (`'pat'`، `'api-key'`، ...) ستضيف كُتل تهيئة فرعية جديدة إلى جانب `oauth`. + +عنوان URL لردّ النداء الخاص بـ OAuth الذي يحتاج موفّرك إلى إضافته إلى قائمة السماح هو: + +``` +https:///auth/apps/callback +``` + + + + + +داخل معالج دالة منطقية، تُرجِع `listConnections({ providerName })` صفوف `ConnectedAccount` الخاصة بهذا التطبيق للمزوّد المحدَّد، مع رموز وصول محدَّثة. + +```ts src/logic-functions/handlers/create-linear-issue-handler.ts +import { listConnections } from 'twenty-sdk/logic-function'; + +export const createLinearIssueHandler = async (input: { + teamId?: string; + title?: string; +}) => { + if (!input.teamId || !input.title) { + return { success: false, error: 'teamId and title are required' }; + } + + const connections = await listConnections({ providerName: 'linear' }); + + // Workspace-shared credentials win when present; fall back to the first + // user-visibility one. For HTTP-route triggers you typically pick the + // request user's connection via event.userWorkspaceId instead. + const connection = + connections.find((c) => c.visibility === 'workspace') ?? connections[0]; + + if (!connection) { + return { + success: false, + error: + 'Linear is not connected. Open the app settings and click "Add connection".', + }; + } + + // Use connection.accessToken to call the third-party API. + const response = await fetch('https://api.linear.app/graphql', { + method: 'POST', + headers: { + Authorization: `Bearer ${connection.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`, + }), + }); + + return { success: response.ok }; +}; +``` + +يحتوي كل اتصال على: + +| الحقل | الوصف | +| ----------------- | ------------------------------------------------------------------------------------------------- | +| `id` | معرّف صف فريد؛ مرّره إلى `getConnection(id)` لإعادة جلب واحد فقط | +| `visibility` | `'user'` (خاص بعضو واحد في مساحة العمل) أو `'workspace'` (مشترك مع جميع الأعضاء) | +| `scopes` | أذونات OAuth الممنوحة من قِبل المزوّد الأصلي (مختلفة عن `visibility` — ولا علاقة لها به) | +| `userWorkspaceId` | معرّف userWorkspace للمالك — مفيد لاختيار "اتصال مستخدم الطلب" في مشغّلات مسارات HTTP | +| `accessToken` | رمز وصول OAuth حديث (يُحدَّث تلقائيًا إذا انتهت صلاحيته) | +| `name` / `handle` | الاسم المعروض للاتصال (يُستمد تلقائيًا عند ردّ نداء OAuth، وقابل لإعادة التسمية من قِبل المستخدم) | +| `authFailedAt` | يُضبط عند فشل أحدث عملية تحديث؛ يجب على المستخدم إعادة الاتصال | + +النقاط الرئيسية: + +* مرّر `{ providerName }` للتصفية حسب المزوّد؛ واحذفه للحصول على كل الاتصالات التي يملكها هذا التطبيق عبر جميع المزوّدين. +* يقوم الخادم بتحديث رمز الوصول بشفافية قبل الإرجاع. يرى معالجك دائمًا رمزًا صالحًا للاستخدام (أو سيكون `authFailedAt` مُعيّنًا). +* `getConnection(id)` هي المعادِل لصف واحد. + + + + + +عند نقر المستخدم "إضافة اتصال"، سيُطلب منه اختيار مستوى الرؤية: + +* **لي فقط** — بيانات الاعتماد خاصة بالمستخدم الذي قام بالاتصال. ستتمكّن أي دالة منطقية تُستدعى بالنيابة عنه (مشغّل مسار HTTP مع `isAuthRequired: true`) من رؤيتها؛ أمّا مشغّلات cron وأحداث قاعدة البيانات فلا. +* **مشتركة على مستوى مساحة العمل** — يمكن لأي عضو في مساحة العمل استخدام بيانات الاعتماد. يمكن لمشغّلات cron/قاعدة البيانات رؤيتها أيضًا، لأنها لا تملك مستخدم طلب. + +استخدم الخيار المناسب لكل معالج: + +```ts +// HTTP-route trigger — prefer the request user's own connection. +const conn = + connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ?? + connections.find((c) => c.visibility === 'workspace'); + +// Cron trigger — no request user; only shared credentials are sensible. +const conn = connections.find((c) => c.visibility === 'workspace'); +``` + +يُسمح بوجود اتصالات متعددة لكل (مستخدم، مزوّد)، لذا يمكن للمستخدم نفسه امتلاك "Linear شخصي" و"Linear للعمل" جنبًا إلى جنب. + + + + + +بالنسبة لكل موفّر اتصال، يحتاج مسؤول الخادم أولًا إلى تسجيل تطبيق OAuth لدى الطرف الثالث. + +1. انتقل إلى إعدادات المطوّر لدى المزوّد (مثل https://linear.app/settings/api/applications/new). +2. عيّن **Redirect URI** إلى `\/auth/apps/callback`. +3. انسخ **Client ID** و**Client Secret** المُنشأين. +4. افتح التطبيق المُثبَّت في Twenty كمسؤول خادم → عيّن القيم على `serverVariables` المقابلة. +5. بعد ذلك، يمكن لأعضاء مساحة العمل إضافة الاتصالات من قسم **الاتصالات** الخاص بكل تطبيق. + + + + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx new file mode 100644 index 0000000000..8dc9935ed6 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/logic/logic-functions.mdx @@ -0,0 +1,514 @@ +--- +title: الوظائف المنطقية +description: عرّف دوال TypeScript على جانب الخادم مع HTTP وcron ومشغّلات أحداث قاعدة البيانات. +icon: bolt +--- + +دوال المنطق هي دوال TypeScript على جانب الخادم تعمل على منصة Twenty. يمكن تشغيلها بواسطة طلبات HTTP أو جداول cron أو أحداث قاعدة البيانات — كما يمكن إتاحتها كأدوات لوكلاء الذكاء الاصطناعي. + + + + +كل ملف وظيفة يستخدم `defineLogicFunction()` لتصدير تكوين مع معالج ومشغّلات اختيارية. + +```ts src/logic-functions/createPostCard.logic-function.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import type { RoutePayload } from 'twenty-sdk/logic-function'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (params: RoutePayload) => { + const client = new CoreApiClient(); + const body = (params.body ?? {}) as { name?: string }; + const name = body.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'; + + const result = await client.mutation({ + createPostCard: { + __args: { data: { name } }, + id: true, + name: true, + }, + }); + return result; +}; + +export default defineLogicFunction({ + universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', + name: 'create-new-post-card', + timeoutSeconds: 2, + handler, + httpRouteTriggerSettings: { + path: '/post-card/create', + httpMethod: 'POST', + isAuthRequired: true, + }, + /*databaseEventTriggerSettings: { + eventName: 'people.created', + },*/ + /*cronTriggerSettings: { + pattern: '0 0 1 1 *', + },*/ +}); +``` + +أنواع المشغّلات المتاحة: +* **httpRoute**: يعرِض وظيفتك على مسار وطريقة HTTP **تحت نقطة النهاية `/s/`**: +> مثال: `path: '/post-card/create'` يمكن استدعاؤه عبر `https://your-twenty-server.com/s/post-card/create` + + +لاستدعاء دالة منطقية يتم تشغيلها بواسطة مسار من مكون واجهة (بدون واجهة رسومية)، راجع قسم [استدعاء دالة منطقية](/l/ar/developers/extend/apps/layout/front-components#calling-a-logic-function). + +* **cron**: يشغّل وظيفتك على جدول باستخدام تعبير CRON. +* **databaseEvent**: يعمل على أحداث دورة حياة كائنات مساحة العمل. عندما تكون عملية الحدث هي `updated`، يمكن تحديد الحقول المحددة المراد الاستماع إليها في مصفوفة `updatedFields`. إذا تُركت غير معرّفة أو فارغة، فسيؤدي أي تحديث إلى تشغيل الدالة. +> مثال: `person.updated`، `*.created`، `company.*` + + +يمكنك أيضًا تنفيذ دالة يدويًا باستخدام CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}' +``` + +```bash filename="Terminal" +yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf +``` + +يمكنك متابعة السجلات باستخدام: + +```bash filename="Terminal" +yarn twenty dev:function:logs +``` + + +#### حمولة مشغل المسار + +عندما يستدعي مُشغِّل المسار وظيفتك المنطقية، فإنها تتلقّى كائن `RoutePayload` الذي يتبع [صيغة AWS HTTP API v2](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). +استورد نوع `RoutePayload` من `twenty-sdk/logic-function`: + +```ts +import type { RoutePayload } from 'twenty-sdk/logic-function'; + +const handler = async (event: RoutePayload) => { + const { headers, queryStringParameters, pathParameters, body } = event; + const { method, path } = event.requestContext.http; + + return { message: 'Success' }; +}; +``` + +يحتوي نوع `RoutePayload` على البنية التالية: + + | الخاصية | النوع | الوصف | مثال | + | ---------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | + | `headers` | `Record\` | رؤوس HTTP (فقط تلك المدرجة في `forwardedRequestHeaders`) | انظر القسم أدناه | + | `queryStringParameters` | `Record\` | معلمات سلسلة الاستعلام (تُضمّ القيم المتعددة باستخدام فواصل) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | + | `pathParameters` | `Record\` | معلمات المسار المستخرجة من نمط المسار | `/users/:id`, `/users/123` -> `{ id: '123' }` | + | `body` | `object \| null` | جسم الطلب المُحلَّل (JSON) | `{ id: 1 }` -> `{ id: 1 }` | + | `rawBody` | `string \| undefined` | نص الطلب الأصلي بترميز UTF-8، قبل تحليل JSON. مفيد للتحقق من تواقيع خطافات الويب على نمط HMAC (مثل `X-Hub-Signature-256` الخاص بـ GitHub وStripe). `undefined` عندما لم يحتفظ وقت التشغيل بها. | | + | `isBase64Encoded` | `boolean` | ما إذا كان جسم الطلب مُرمَّزًا بترميز base64 | | + | `requestContext.http.method` | `string` | طريقة HTTP (GET, POST, PUT, PATCH, DELETE) | | + | `requestContext.http.path` | `string` | المسار الخام للطلب | | + + +#### forwardedRequestHeaders + +افتراضيًا، **لا** تُمرَّر رؤوس HTTP من الطلبات الواردة إلى دالتك المنطقية لأسباب أمنية. +للوصول إلى رؤوس محددة، أدرِجها في مصفوفة `forwardedRequestHeaders`: + +```ts +export default defineLogicFunction({ + universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', + name: 'webhook-handler', + handler, + httpRouteTriggerSettings: { + path: '/webhook', + httpMethod: 'POST', + isAuthRequired: false, + forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], + }, +}); +``` + +في معالجك، يمكنك الوصول إلى الرؤوس المُمرَّرة بهذه الطريقة: + +```ts +const handler = async (event: RoutePayload) => { + const signature = event.headers['x-webhook-signature']; + const contentType = event.headers['content-type']; + + // Validate webhook signature... + return { received: true }; +}; +``` + + +تُحوَّل أسماء الرؤوس إلى أحرف صغيرة. يمكنك الوصول إليها باستخدام مفاتيح بأحرف صغيرة (على سبيل المثال، `event.headers['content-type']`). + + +#### استجابة HTTP مخصصة + +بشكل افتراضي، فإن إرجاع قيمة بسيطة من المعالج الخاص بك يعيدها كاستجابة `200` (بصيغة JSON للكائنات و`text/plain` للسلاسل النصية). للتحكم في رمز الحالة ورؤوس الاستجابة، أعد كائن `Response` من `twenty-sdk/logic-function`: + +```ts +import { Response } from 'twenty-sdk/logic-function'; + +const handler = async (event: RoutePayload) => { + return new Response('

Hello

', { + status: 201, + headers: { 'content-type': 'text/html' }, + }); +}; +``` + +لأسباب أمنية، يتم تقييد ترويسات الاستجابة بقائمة مسموح بها. يتم إسقاط أي ترويسة ليست في القائمة (مثل `Set-Cookie`، وترويسات CORS مثل `Access-Control-Allow-Origin`، أو ترويسات `X-*` المخصصة) بصمت قبل إرسال الاستجابة. ترويسات الاستجابة المسموح بها هي: + +* `content-type` +* `content-language` +* `content-disposition` +* `cache-control` +* `retry-after` + + +يجب أن يكون رمز الحالة رمز حالة HTTP صالحًا (بين 100 و599). تتم مطابقة أسماء ترويسات الاستجابة دون حساسية لحالة الأحرف. + + +#### حمولة مُحفِّز حدث قاعدة البيانات + +عندما يستدعي مُحفِّز حدث قاعدة البيانات دالة المنطق الخاصة بك، فإنه يستقبل كائن `DatabaseEventPayload` واحدًا لكل سجل تم تغييره. تجمع الحمولة بين البيانات الوصفية حول مساحة العمل والكائن المصدر وبين الحدث على مستوى السجل. + +```ts +import type { + DatabaseEventPayload, + ObjectRecordCreateEvent, + ObjectRecordDestroyEvent, + ObjectRecordUpdateEvent, +} from 'twenty-sdk/logic-function'; + +type Person = { + id: string; + emails?: { primaryEmail?: string }; +}; +``` + +تتضمن الحمولة ما يلي: + +| الخاصية | الوصف | +| ------------------------------------------------ | ----------------------------------------------------------------------------------------------- | +| `name` | اسم الحدث، مثل `person.updated`. | +| `workspaceId` | مساحة العمل التي وقع فيها الحدث. | +| `objectMetadata` | بيانات وصفية للكائن الذي تم تغييره. | +| `recordId` | معرّف السجل الذي تم تغييره. | +| `userId`, `userWorkspaceId`, `workspaceMemberId` | حقول الفاعل عندما يكون الحدث ناتجًا عن مستخدم في مساحة العمل. | +| `properties` | بيانات السجل الخاصة بالحدث، مع `before` و`after` و`diff` و`updatedFields` اعتمادًا على العملية. | + +| حدث | بيانات السجل | +| ------------------ | -------------------------------------------------------------------------------------------------------------- | +| `person.created` | `event.properties.after` | +| `person.updated` | `event.properties.before`, `event.properties.after`, `event.properties.diff`, `event.properties.updatedFields` | +| `person.destroyed` | `event.properties.before` | + +في عمليات الحذف اللين (soft deletes)، يتبع `.deleted` بنية نمط التحديث لأن حقل `deletedAt` في السجل يتغيّر. +في عمليات الحذف الدائم، استخدم `.destroyed`. + + +`databaseEventTriggerSettings.updatedFields` يرشّح أيّ أحداث التحديث التي تُشغِّل الدالة. +`event.properties.updatedFields` يوضّح لك أي الحقول تغيّرت فعليًا في الحدث الحالي. + + +مثال على حدث الإنشاء: + +```ts +type PersonCreatedEvent = DatabaseEventPayload< + ObjectRecordCreateEvent +>; + +const handler = async (event: PersonCreatedEvent) => { + const person = event.properties.after; + + return { + personId: event.recordId, + email: person.emails?.primaryEmail, + }; +}; +``` + +مثال على حدث التحديث: + +```ts +type PersonUpdatedEvent = DatabaseEventPayload< + ObjectRecordUpdateEvent +>; + +const handler = async (event: PersonUpdatedEvent) => { + const { before, after, diff, updatedFields } = event.properties; + + return { + personId: event.recordId, + updatedFields, + previousEmail: before.emails?.primaryEmail, + currentEmail: after.emails?.primaryEmail, + emailDiff: diff.emails, + }; +}; +``` + +تشغيل المشغّل فقط عند تحديثات البريد الإلكتروني: + +```ts +export default defineLogicFunction({ + ..., + databaseEventTriggerSettings: { + eventName: 'person.updated', + updatedFields: ['emails'], + }, +}); +``` + +مثال على حدث الحذف: + +```ts +type PersonDestroyedEvent = DatabaseEventPayload< + ObjectRecordDestroyEvent +>; + +const handler = async (event: PersonDestroyedEvent) => { + const personBeforeDestroy = event.properties.before; + + return { + personId: event.recordId, + email: personBeforeDestroy.emails?.primaryEmail, + }; +}; +``` + +#### إتاحة دالة كأداة ذكاء اصطناعي أو كإجراء ضمن سير العمل + +يمكن إتاحة دوال المنطق على واجهتين، ولكلٍ منهما مشغِّل خاص به: + +* **`toolTriggerSettings`** — يجعل الدالة قابلة للاكتشاف عبر ميزات الذكاء الاصطناعي الخاصة بـ Twenty (الدردشة، MCP، استدعاء الدوال). يستخدم JSON Schema القياسي، وهو التنسيق الذي تفهمه LLMs أصلاً. +* **`workflowActionTriggerSettings`** — يجعل الدالة تظهر كخطوة في منشئ سير العمل المرئي. يستخدم `InputSchema` الغني الخاص بـ Twenty لكي يتمكن المُنشئ من عرض محرّرات الحقول المناسبة، وأدوات انتقاء المتغيّرات، والتسميات. + +يمكن للدالة اختيار أحدهما، أو الآخر، أو كليهما. توجد جنبًا إلى جنب مع `cronTriggerSettings` و`databaseEventTriggerSettings` و`httpRouteTriggerSettings` — النمط نفسه، والشكل نفسه. + +```ts src/logic-functions/enrich-company.logic-function.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (params: { companyName: string; domain?: string }) => { + const client = new CoreApiClient(); + + const result = await client.mutation({ + createTask: { + __args: { + data: { + title: `Enrich data for ${params.companyName}`, + body: `Domain: ${params.domain ?? 'unknown'}`, + }, + }, + id: true, + }, + }); + + return { taskId: result.createTask.id }; +}; + +export default defineLogicFunction({ + universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479', + name: 'enrich-company', + description: 'Enrich a company record with external data', + timeoutSeconds: 10, + handler, + toolTriggerSettings: {}, +}); +``` + +النقاط الرئيسية: + +* يمكن للدالة مزج الواجهات — صرِّح بكلٍ من `toolTriggerSettings` و`workflowActionTriggerSettings` لإتاحتها في الدردشة وفي منشئ سير العمل. +* `toolTriggerSettings.inputSchema` و`workflowActionTriggerSettings.inputSchema` كلاهما اختياري. عند الإغفال، يستنتج مُنشئ البيان هذه المخططات من الشيفرة المصدرية للمعالج (JSON Schema لأداة الذكاء الاصطناعي، و`InputSchema` الخاصة بـ Twenty لإجراء سير العمل). قدّم واحدًا صراحةً عندما ترغب في أنواع أكثر ثراءً — على سبيل المثال، مع حقول واعية بـ `FieldMetadataType` مثل `CURRENCY` أو `RELATION` لمنشئ سير العمل، أو مع حقول `description` التي يمكن لوكيل الذكاء الاصطناعي قراءتها: + +```ts +export default defineLogicFunction({ + ..., + toolTriggerSettings: { + inputSchema: { + type: 'object', + properties: { + companyName: { + type: 'string', + description: 'The name of the company to enrich', + }, + domain: { + type: 'string', + description: 'The company website domain (optional)', + }, + }, + required: ['companyName'], + }, + }, +}); +``` + + +**اكتب `description` جيدًا.** يعتمد وكلاء الذكاء الاصطناعي على حقل `description` الخاص بالدالة لتحديد وقت استخدام الأداة. كن محددًا بشأن ما تفعله الأداة ومتى ينبغي استدعاؤها. + + +
+
+ + +**خطافات التثبيت** — معالجات ما قبل التثبيت وما بعد التثبيت — تشترك في وقت التشغيل نفسه، ولكن يُصرَّح عنها بدوال تعريف خاصة بها ولا تأخذ إعدادات المشغّلات. راجع [خطافات التثبيت (Install Hooks)](/l/ar/developers/extend/apps/config/install-hooks) لمعرفة `definePreInstallLogicFunction` و `definePostInstallLogicFunction`. + + +## عملاء واجهة برمجة تطبيقات مضبوطة الأنواع (`twenty-client-sdk`) + +توفر حزمة `twenty-client-sdk` عميلين لـ GraphQL ذوي أنواع ثابتة للتفاعل مع واجهة Twenty البرمجية من وظائفك المنطقية ومكوّنات الواجهة الأمامية. + +| العميل | استيراد | نقطة النهاية | مُولَّد؟ | +| ------------------- | ---------------------------- | --------------------------------------------------- | -------------------------- | +| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — بيانات مساحة العمل (السجلات، الكائنات) | نعم، في وقت التطوير/البناء | +| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — تكوين مساحة العمل، رفع الملفات | لا، يأتي مُجهزًا مسبقًا | + + + + +`CoreApiClient` هو العميل الرئيسي للاستعلام وتعديل بيانات مساحة العمل. يُولَّد **من مخطط مساحة العمل لديك** أثناء `yarn twenty dev` أو `yarn twenty dev:build`، لذا فهو مضبوط الأنواع بالكامل ليتوافق مع كائناتك وحقولك. + +```ts +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const client = new CoreApiClient(); + +// Query records +const { companies } = await client.query({ + companies: { + edges: { + node: { + id: true, + name: true, + domainName: { + primaryLinkLabel: true, + primaryLinkUrl: true, + }, + }, + }, + }, +}); + +// Create a record +const { createCompany } = await client.mutation({ + createCompany: { + __args: { + data: { + name: 'Acme Corp', + }, + }, + id: true, + name: true, + }, +}); +``` + +يستخدم العميل صياغة مجموعة اختيار: مرِّر `true` لتضمين حقل، واستخدم `__args` للوسيطات، وعشّش الكائنات للعلاقات. ستحصل على إكمال تلقائي كامل وفحص للأنواع يعتمد على مخطط مساحة العمل لديك. + + +**يتم توليد CoreApiClient في وقت التطوير/البناء.** إذا استخدمته دون تشغيل `yarn twenty dev` أو `yarn twenty dev:build` أولًا، فسيؤدي ذلك إلى خطأ. تحدث عملية التوليد تلقائيًا — إذ يستطلع CLI مخطط GraphQL لمساحة عملك وينشئ عميلًا مضبوط الأنواع باستخدام `@genql/cli`. + + +#### استخدام CoreSchema للتعليقات التوضيحية للأنواع + +`CoreSchema` يوفّر أنواع TypeScript المطابقة لكائنات مساحة العمل لديك — مفيد لتعيين أنواع حالة المكوّن أو معاملات الدوال: + +```ts +import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; +import { useState } from 'react'; + +const [company, setCompany] = useState< + Pick | undefined +>(undefined); + +const client = new CoreApiClient(); +const result = await client.query({ + company: { + __args: { filter: { position: { eq: 1 } } }, + id: true, + name: true, + }, +}); +setCompany(result.company); +``` + + + + +يأتي `MetadataApiClient` مُجهّزًا مسبقًا مع SDK (لا حاجة للتوليد). يستعلم عن نقطة النهاية `/metadata` للحصول على تكوين مساحة العمل والتطبيقات ورفع الملفات. + +```ts +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; + +const metadataClient = new MetadataApiClient(); + +// List first 10 objects in the workspace +const { objects } = await metadataClient.query({ + objects: { + edges: { + node: { + id: true, + nameSingular: true, + namePlural: true, + labelSingular: true, + isCustom: true, + }, + }, + __args: { + filter: {}, + paging: { first: 10 }, + }, + }, +}); +``` + +#### رفع الملفات + +يتضمن `MetadataApiClient` طريقة `uploadFile` لإرفاق الملفات بالحقول من نوع الملف: + +```ts +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import * as fs from 'fs'; + +const metadataClient = new MetadataApiClient(); + +const fileBuffer = fs.readFileSync('./invoice.pdf'); + +const uploadedFile = await metadataClient.uploadFile( + fileBuffer, // file contents as a Buffer + 'invoice.pdf', // filename + 'application/pdf', // MIME type + '58a0a314-d7ea-4865-9850-7fb84e72f30b', // field universalIdentifier +); + +console.log(uploadedFile); +// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' } +``` + +| المعلمة | النوع | الوصف | +| ---------------------------------- | -------- | ---------------------------------------------------------------------- | +| `fileBuffer` | `Buffer` | المحتوى الخام للملف | +| `filename` | `string` | اسم الملف (يُستخدم للتخزين والعرض) | +| `contentType` | `string` | نوع MIME (القيمة الافتراضية `application/octet-stream` إذا لم يُحدَّد) | +| `fieldMetadataUniversalIdentifier` | `string` | قيمة `universalIdentifier` لحقل نوع الملف في كائنك | + +النقاط الرئيسية: +* يستخدم `universalIdentifier` الخاص بالحقل (وليس معرّفه الخاص بمساحة العمل)، بحيث يعمل كود الرفع لديك عبر أي مساحة عمل مُثبَّت فيها تطبيقك. +* العنوان `url` المُعاد هو عنوان URL موقّع يمكنك استخدامه للوصول إلى الملف المرفوع. + + + + + + عند تشغيل كودك على Twenty (وظائف منطقية أو مكوّنات أمامية)، يقوم النظام الأساسي بحقن بيانات الاعتماد كمتغيرات بيئية: + + * `TWENTY_API_URL` — عنوان URL الأساسي لواجهة Twenty البرمجية + * `TWENTY_APP_ACCESS_TOKEN` — مفتاح قصير العمر ذو نطاق يقتصر على الدور الافتراضي لوظيفة تطبيقك + + لست **بحاجة** إلى تمرير هذه القيم إلى العملاء — فهي تُقرأ تلقائيًا من `process.env`. تُحدَّد أذونات مفتاح واجهة برمجة التطبيقات بواسطة الدور المُعلن باستخدام `defineApplicationRole()` (أو المشار إليه عبر `defaultRoleUniversalIdentifier` في `application-config.ts`). + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx new file mode 100644 index 0000000000..c5697d167b --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/logic/overview.mdx @@ -0,0 +1,55 @@ +--- +title: نظرة عامة +description: TypeScript على جانب الخادم الذي يعمل داخل Twenty — يتم تشغيله بواسطة مسارات HTTP، وجداول كرون، وأحداث قاعدة البيانات، وأدوات الذكاء الاصطناعي، أو إجراءات سير العمل. +icon: bolt +--- + +**طبقة المنطق** في تطبيق Twenty هي الشيفرة التي *تعمل* — معالِجات TypeScript على جانب الخادم تستجيب لطلبات HTTP، وجداول كرون، وتغييرات السجلات؛ ومهارات ووكلاء الذكاء الاصطناعي التي تعمل داخل مساحة العمل؛ واتصالات OAuth التي تتيح لدوالّك العمل نيابةً عن المستخدم في الخدمات الخارجية. + +```text + ┌─ HTTP route ──┐ + │ Cron schedule │ + │ Database event │ ┌────────────────────┐ + triggers ─┤ AI tool call ├─────▶│ Logic function │ + │ Workflow action │ │ (your handler) │ + │ Manual exec │ └────────────────────┘ + └────────────────────┘ │ + ▼ + ┌────────────────────────────┐ + │ Twenty API (records) │ + │ Third-party API │ + │ (via Connection token) │ + └────────────────────────────┘ +``` + +## في هذا القسم + + + + لبنة البناء الأساسية — أنواع المشغلات، والحمولات، وعميل واجهة برمجة التطبيقات ذو الأنواع. + + + تعليمات قابلة لإعادة الاستخدام لوكلاء الذكاء الاصطناعي ومساعدين مع مطالبات نظام مخصّصة. + + + بيانات اعتماد OAuth التي يحتفظ بها تطبيقك للخدمات الخارجية — مثل Linear وGitHub وSlack وغيرها. + + + +## لمحة عن أنواع المشغلات + +دالة المنطق تختار واحدًا أو أكثر من المشغلات — كل إدخال أدناه هو حقل منفصل في `defineLogicFunction()`: + +| المشغّل | متى يعمل | الإعداد | +| ---------------------- | -------------------------------------------------------------- | ------------------------------- | +| **مسار HTTP** | طلب يصل إلى نقطة نهاية `/s/\` الخاصة بك | `httpRouteTriggerSettings` | +| **كرون** | عند تطابق تعبير CRON | `cronTriggerSettings` | +| **حدث قاعدة البيانات** | يتم إنشاء سجل في مساحة العمل أو تحديثه أو حذفه | `databaseEventTriggerSettings` | +| **أداة ذكاء اصطناعي** | ميزة ذكاء اصطناعي في Twenty تقرر استدعاء دالتك | `toolTriggerSettings` | +| **إجراء سير العمل** | تستدعي خطوة في سير العمل دالتك | `workflowActionTriggerSettings` | + +تعمل الدوال ضمن عمليات Node.js معزولة، وتصل إلى مساحة العمل عبر عميل واجهة برمجة تطبيقات مضبوط الأنواع ومحدّد النطاق بالدور المصرّح عنه في [`defineApplication()`](/l/ar/developers/extend/apps/config/application). + + +**خطافات وقت التثبيت** — الشيفرة التي تعمل قبل التثبيت أو بعده — تشارك بيئة التشغيل هذه ولكنها تستخدم دوال تعريف خاصة بها وتوجد ضمن [Config → Install Hooks](/l/ar/developers/extend/apps/config/install-hooks). + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/logic/skills-and-agents.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/logic/skills-and-agents.mdx new file mode 100644 index 0000000000..35228063d9 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/logic/skills-and-agents.mdx @@ -0,0 +1,138 @@ +--- +title: المهارات والوكلاء +description: عرّف مهارات ووكلاء الذكاء الاصطناعي لتطبيقك. +icon: robot +--- + + + المهارات والوكلاء حاليًا في مرحلة الألفا. الميزة تعمل لكنها لا تزال قيد التطور. + + +يمكن للتطبيقات تعريف قدرات ذكاء اصطناعي تعمل داخل مساحة العمل — تعليمات مهارات قابلة لإعادة الاستخدام ووكلاء بموجهات نظام مخصّصة. + + + + +تُحدِّد المهارات تعليمات وإمكانات قابلة لإعادة الاستخدام يمكن لوكلاء الذكاء الاصطناعي استخدامها داخل مساحة العمل لديك. استخدم `defineSkill()` لتعريف مهارات مع تحقّق مدمج: + +```ts src/skills/example-skill.ts +import { defineSkill } from 'twenty-sdk/define'; + +export default defineSkill({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + name: 'sales-outreach', + label: 'Sales Outreach', + description: 'Guides the AI agent through a structured sales outreach process', + icon: 'IconBrain', + content: `You are a sales outreach assistant. When reaching out to a prospect: +1. Research the company and recent news +2. Identify the prospect's role and likely pain points +3. Draft a personalized message referencing specific details +4. Keep the tone professional but conversational`, +}); +``` + +النقاط الرئيسية: +* `name` هي سلسلة معرّف فريدة للمهارة (يُنصَح باستخدام kebab-case). +* `label` هو اسم العرض المقروء للبشر الظاهر في واجهة المستخدم. +* `content` يحتوي على تعليمات المهارة — وهو النص الذي يستخدمه وكيل الذكاء الاصطناعي. +* `icon` (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم. +* `description` (اختياري) يوفّر سياقًا إضافيًا حول غرض المهارة. + + + + +الوكلاء هم مساعدون ذكاء اصطناعي يعيشون داخل مساحة العمل لديك. استخدم `defineAgent()` لإنشاء وكلاء بموجه نظام مخصّص: + +```ts src/agents/example-agent.ts +import { defineAgent } from 'twenty-sdk/define'; + +export default defineAgent({ + universalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123', + name: 'sales-assistant', + label: 'Sales Assistant', + description: 'Helps the sales team draft outreach emails and research prospects', + icon: 'IconRobot', + prompt: 'You are a helpful sales assistant. Help users with their questions and tasks.', +}); +``` + +النقاط الرئيسية: +* `name` هي سلسلة معرّف فريدة للوكيل (يُنصح باستخدام kebab-case). +* `label` هو اسم العرض الظاهر في واجهة المستخدم. +* `prompt` هو موجه النظام الذي يحدّد سلوك الوكيل. +* `description` (اختياري) يوفّر سياقًا حول ما يفعله الوكيل. +* `icon` (اختياري) يحدّد الأيقونة المعروضة في واجهة المستخدم. +* `modelId` (اختياري) يتجاوز نموذج الذكاء الاصطناعي الافتراضي الذي يستخدمه الوكيل. +* `responseFormat` (اختياري) يتحكم في شكل مخرجات الوكيل. القيمة الافتراضية هي `{ type: 'text' }` للنص الحر. استخدم `{ type: 'json', schema }` لفرض مخرجات JSON منظمة. + +بشكل افتراضي، يعيد الوكيل نصًا حرًا. للحصول على مخرجات منظمة، عيّن `responseFormat` إلى `{ type: 'json' }` ووفّر `schema`: + +```ts src/agents/structured-agent.ts +import { defineAgent } from 'twenty-sdk/define'; + +export default defineAgent({ + universalIdentifier: 'c4d5e6f7-a8b9-0123-cdef-456789012345', + name: 'lead-scorer', + label: 'Lead Scorer', + prompt: 'Score the lead and explain your reasoning.', + responseFormat: { + type: 'json', + schema: { + type: 'object', + properties: { + score: { type: 'number', description: 'Lead score from 0 to 100' }, + summary: { type: 'string', description: 'Short reasoning for the score' }, + }, + required: ['score', 'summary'], + additionalProperties: false, + }, + }, +}); +``` + +ملاحظات حول المخطط: +* المخطط كائن مسطح: يجب أن يكون `type` لكل خاصية نوعًا بدائيًا (`string` أو `number` أو `boolean`). الكائنات المتداخلة والمصفوفات غير مدعومة. +* `description` (اختياري) على كل خاصية يوجه النموذج لما يجب وضعه هناك. +* `required` (اختياري) يسرد الخصائص التي يجب على النموذج إرجاعها دائمًا. +* `additionalProperties: false` (اختياري) يمنع أي خاصية غير معرّفة في `properties`. + + + + +تتيح `runAgent()` لدالة منطقية تشغيل أحد وكلاء تطبيقك (مع مهاراته وأدواته). عرِّف الوكيل عن طريق `universalIdentifier` الذي مررته إلى `defineAgent()`: + +```ts src/logic-functions/run-enricher.ts +import { runAgent } from 'twenty-sdk/logic-function'; + +const { result, error, success } = await runAgent({ + agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123', + prompt: 'Enrich House Ad : fill empty fields from its listing URL.', +}); +``` + +النقاط الرئيسية: +* يعمل الوكيل **بشكل متزامن** ويمكنه قراءة/تحديث السجلات بنفسه عبر أدواته الخاصة — يتم حل `runAgent()` بمجرد اكتمال التشغيل. +* لا يمكن للتطبيق تشغيل سوى وكلائه الخاصين. +* يجب أن يمنح [الدور الافتراضي](/l/ar/developers/extend/apps/config/roles) للتطبيق علامة الإذن `AI` — أضِف `SystemPermissionFlag.AI` إلى `permissionFlagUniversalIdentifiers` الخاصة به (أو عيِّن `canAccessAllTools: true`). + بدون ذلك، تفشل `runAgent()` بخطأ في الأذونات. +* اضبط قيمة كبيرة لـ `timeoutSeconds` على الدالة المنطقية — قد يستغرق تشغيل الوكيل عدة ثوانٍ. +* يكون `success` بقيمة `true` و`result` غير فارغ عند اكتمال التشغيل؛ في حال الفشل يكون `success` بقيمة `false`، و`result` بقيمة `null`، وتحتوي `error` على السبب (على سبيل المثال، عندما تنفد أرصدة الذكاء الاصطناعي الخاصة بمساحة العمل أثناء التشغيل). + +```ts src/roles/default-role.ts +import { defineApplicationRole, SystemPermissionFlag } from 'twenty-sdk/define'; + +export default defineApplicationRole({ + universalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061', + label: 'Default function role', + // runAgent() requires the AI permission flag on the app's default role. + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.AI], +}); +``` + + + **تجنب الحلقات:** إذا استدعيت `runAgent()` من مشغل حدث قاعدة بيانات من نوع `*.updated` وقام الوكيل بتحديث نفس السجل، فحدد نطاق المشغل باستخدام `updatedFields` إلى حقل لا يكتبه الوكيل أبدًا (مثل عنوان URL المصدر)، أو تحقَّق مما إذا كان أي حقل مستهدف لا يزال فارغًا قبل استدعاء `runAgent()`. + + + + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx new file mode 100644 index 0000000000..33c18ee784 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/operations/cli.mdx @@ -0,0 +1,105 @@ +--- +title: CLI +description: أوامر yarn twenty لتنفيذ الدوال، وبثّ السجلات، وإدارة تثبيتات التطبيقات، والتبديل بين الريموتات. +icon: الطرفية +--- + +إلى جانب `dev` و`dev:build` و`dev:add` و`dev:typecheck`، يوفّر `yarn twenty` CLI أوامر لتنفيذ الدوال، وعرض السجلات، وإدارة تثبيتات التطبيقات. + +## تنفيذ الدوال (`yarn twenty dev:function:exec`) + +تشغيل دالة منطقية يدويًا دون تشغيلها عبر HTTP أو cron أو حدث قاعدة بيانات: + +```bash filename="Terminal" +# Execute by function name +yarn twenty dev:function:exec -n create-new-post-card + +# Execute by universalIdentifier +yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf + +# Pass a JSON payload +yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}' + +# Execute the post-install function +yarn twenty dev:function:exec --postInstall +``` + +## عرض سجلات الدوال (`yarn twenty dev:function:logs`) + +بثّ سجلات التنفيذ لدوال تطبيقك المنطقية: + +```bash filename="Terminal" +# Stream all function logs +yarn twenty dev:function:logs + +# Filter by function name +yarn twenty dev:function:logs -n create-new-post-card + +# Filter by universalIdentifier +yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf +``` + + +يختلف هذا عن `yarn twenty docker:logs`، الذي يعرض سجلات حاوية Docker. يعرض `yarn twenty dev:function:logs` سجلات تنفيذ دوال تطبيقك من خادم Twenty. + + +## توليد العميل محدد الأنواع (`yarn twenty dev:generate-client`) + +أعد توليد عميل واجهة برمجة التطبيقات محدد الأنواع (`twenty-client-sdk`) من مخطط الجهة البعيدة النشطة، دون بناء تطبيق أو مزامنته. استخدمه للحصول على عميل محدد الأنواع في أي مشروع — مثل خدمة خلفية موجودة في مستودع منفصل — يتواصل مع مثيل Twenty الخاص بك: + +```bash filename="Terminal" +# In your project (no Twenty app definition required) +yarn add twenty-sdk twenty-client-sdk + +# Connect to the Twenty instance to generate the client from +yarn twenty remote:add + +# Generate the typed client into node_modules/twenty-client-sdk +yarn twenty dev:generate-client +``` + +ثم استورد العميل في شيفرتك: + +```typescript +import { CoreApiClient } from 'twenty-client-sdk/core'; +``` + +أعد تشغيل الأمر كلما تغيّر نموذج البيانات لديك لتحديث الأنواع المُولَّدة. + + +يتم إنشاء العميل البرمجي داخل `node_modules`، لذا لا يُدرج مع شيفرتك في الالتزامات (commits). شغّل `yarn twenty dev:generate-client` بعد كل عملية تثبيت (على سبيل المثال في سكربت `postinstall` أو في CI). + + +## إلغاء تثبيت تطبيق (`yarn twenty app:uninstall`) + +أزل تطبيقك من مساحة العمل النشطة: + +```bash filename="Terminal" +yarn twenty app:uninstall + +# Skip the confirmation prompt +yarn twenty app:uninstall --yes +``` + +## إدارة الريموتات + +**الريموت** هو خادم Twenty يتصل به تطبيقك. أثناء الإعداد، تُنشئ أداة إنشاء الهيكل واحدًا لك تلقائيًا. يمكنك إضافة ريموتات أخرى أو التبديل بينها في أي وقت. + +```bash filename="Terminal" +# Add a new remote (opens a browser for OAuth login) +yarn twenty remote:add + +# Connect to a local Twenty server (auto-detects port 2020 or 3000) +yarn twenty remote:add --local + +# Add a remote non-interactively (useful for CI) +yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote + +# List all configured remotes +yarn twenty remote:list + +# Set the active remote +yarn twenty remote:use +``` + +تُخزَّن بيانات اعتمادك في `~/.twenty/config.json`. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/overview.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/overview.mdx new file mode 100644 index 0000000000..36eab471ed --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/operations/overview.mdx @@ -0,0 +1,32 @@ +--- +title: نظرة عامة +description: قم ببناء تطبيقك واختباره وإصداره — أوامر CLI، واختبارات التكامل، وCI، والنشر إلى خادم أو إلى npm. +icon: rocket +--- + +**طبقة العمليات** هي كل ما تفعله *على* تطبيقك وليس *به*: استدعاء أوامر CLI، وتشغيل اختبارات التكامل على خادم Twenty حقيقي، وإعداد CI، وإطلاق الإصدارات — إما كملف tarball يُنشر على خادم واحد أو كحزمة npm مُدرجة في المتجر. + +```text + develop ─▶ test ─▶ build ─▶ deploy / publish + ─────── ──── ───── ───────────────── + yarn yarn yarn yarn twenty app:publish --private (tarball → one server) + twenty test twenty + dev dev:build yarn twenty app:publish (npm → marketplace) +``` + +## في هذا القسم + + + + مرجع `yarn twenty` — exec، logs، uninstall، remotes. + + + أي أمر يُستخدم ومتى، وكيفية قراءة فروق المزامنة، وسلّم الاستعادة. + + + إعداد Vitest، اختبارات التكامل، فحص الأنواع، سير عمل CI. + + + البناء، نشر ملف tarball، النشر إلى npm، التثبيت. + + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/publishing.mdx new file mode 100644 index 0000000000..902a239a3e --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/operations/publishing.mdx @@ -0,0 +1,294 @@ +--- +title: النشر +icon: رفع +description: وزّع تطبيق Twenty الخاص بك على سوق Twenty أو انشره داخليًا. +--- + +## نظرة عامة + +بمجرد أن يكون تطبيقك [مبنيًا ومختبرًا محليًا](/l/ar/developers/extend/apps/getting-started/concepts)، لديك مساران لتوزيعه: + +* **نشر أرشيف tar** — ارفع تطبيقك مباشرةً إلى خادم Twenty محدد للاستخدام الداخلي أو الخاص. +* **النشر على npm** — أدرج تطبيقك في سوق Twenty ليتسنى لأي مساحة عمل اكتشافه وتثبيته. + +كلا المسارين يبدآن من نفس خطوة **build**. + +## بناء تطبيقك + +شغّل أمر build لتجميع تطبيقك وإنشاء ملف `manifest.json` جاهز للتوزيع: + +```bash filename="Terminal" +yarn twenty dev:build +``` + +يقوم هذا بتجميع مصادر TypeScript، وتحويل دوال المنطق ومكوّنات الواجهة الأمامية، وكتابة كل شيء إلى `.twenty/output/`. أضِف `--tarball` لإنتاج حزمة `.tgz` أيضًا للتوزيع اليدوي أو لأمر publish. + +## النشر إلى خادم (tarball) + +بالنسبة للتطبيقات التي لا تريد إتاحتها للعامة — مثل الأدوات المملوكة، أو عمليات التكامل الخاصة بالمؤسسات فقط، أو الإصدارات التجريبية — يمكنك نشر tarball مباشرةً إلى خادم Twenty. + +### المتطلبات الأساسية + +قبل النشر، تحتاج إلى remote مُعدّ يشير إلى خادم الهدف. تُخزّن remotes عنوان URL للخادم وبيانات اعتماد المصادقة محليًا في `~/.twenty/config.json`. + +أضِف remote: + +```bash filename="Terminal" +yarn twenty remote:add --url https://your-twenty-server.com --as production +``` + +### النشر + +بناء تطبيقك ورفعه إلى الخادم في خطوة واحدة: + +```bash filename="Terminal" +yarn twenty app:publish --private +# To deploy to a specific remote: +# yarn twenty app:publish --private --remote production +``` + +### مشاركة تطبيق منشور + + +تُعد مشاركة التطبيقات الخاصة (tarball) عبر مساحات العمل ميزة ضمن **Enterprise**. ستعرض علامة التبويب **التوزيع** مطالبة بالترقية بدلًا من عناصر التحكم في المشاركة حتى تحتوي مساحة العمل لديك على مفتاح Enterprise صالح. اطلع على [الإعدادات > لوحة الإدارة > Enterprise](/settings/admin-panel#enterprise) لتنشيطه. + + +تطبيقات tarball لا تُدرَج في السوق العامة، لذا لن تكتشفها مساحات العمل الأخرى على الخادم نفسه عبر الاستعراض. بمجرد أن تصبح مساحة العمل لديك ضمن خطة Enterprise، يمكنك مشاركة تطبيق تم نشره كما يلي: + +1. اذهب إلى **الإعدادات > التطبيقات > التسجيلات** وافتح تطبيقك +2. في علامة التبويب **التوزيع**، انقر **نسخ رابط المشاركة** +3. شارك هذا الرابط مع المستخدمين في مساحات عمل أخرى — سيأخذهم مباشرةً إلى صفحة تثبيت التطبيق + +يستخدم رابط المشاركة عنوان URL الأساسي للخادم (من دون أي نطاق فرعي لمساحة عمل)، لذا يعمل مع أي مساحة عمل على الخادم. + +### إدارة الإصدارات + +عند تحديث تطبيق tarball منشور مسبقًا، يشترط الخادم أن تكون قيمة `version` في `package.json` **أعلى قطعًا** (وفق ترتيب [الإصدار الدلالي](https://semver.org)) من الإصدار المنشور حاليًا. إعادة نشر الإصدار نفسه، أو دفع إصدار أدنى، يُرفَض قبل تخزين ملف tarball — سترى خطأ `VERSION_ALREADY_EXISTS` من CLI. + +لطرح تحديث: + +1. قم بزيادة الحقل `version` في ملف `package.json` (مثلًا: `1.2.3` → `1.2.4`، `1.3.0`، أو `2.0.0`) +2. شغّل `yarn twenty app:publish --private` (أو `yarn twenty app:publish --private --remote production`) +3. سترى مساحات العمل التي ثبّتت التطبيق الترقية متاحة في إعداداتها + + +علامات ما قبل الإصدار تعمل كما هو متوقع: زيادة `1.0.0-rc.1` → `1.0.0-rc.2` مسموح بها، ويُعترَف بالإصدار النهائي مثل `1.0.0` على أنه أعلى من `1.0.0-rc.5`. يجب أن يكون الإصدار في `package.json` بنفسه سلسلة semver صالحة. + + +{/* TODO: add screenshot of the Upgrade button */} + +### توافق إصدار الخادم + +إذا كان تطبيقك يستخدم ميزة تم تقديمها في إصدار معيّن من خادم Twenty (على سبيل المثال، موفّرو OAuth الذين أضيفوا في v2.3.0)، فيجب عليك التصريح بأدنى إصدار من الخادم يتطلبه تطبيقك باستخدام الحقل `engines.twenty` في `package.json`: + +```json filename="package.json" +{ + "name": "twenty-my-app", + "version": "1.0.0", + "engines": { + "node": "^24.5.0", + "twenty": ">=2.3.0" + } +} +``` + +القيمة عبارة عن [نطاق semver](https://github.com/npm/node-semver#ranges) قياسي. أنماط شائعة: + +| النطاق | المعنى | +| ---------------------------------- | ------------------------------------------------ | +| `>=2.3.0` | أي خادم من 2.3.0 فصاعدًا | +| `>=2.3.0 \<3.0.0` | 2.3.0 أو أحدث، لكن أقل من الإصدار الرئيسي التالي | +| `^2.3.0` | مماثل لـ `>=2.3.0 \<3.0.0` | + +**ماذا يحدث وقت النشر والتثبيت:** + +* إذا تم تعيين `engines.twenty` ولم يستوفِ إصدار الخادم الهدف النطاق، فسيتم رفض النشر (tarball upload) أو التثبيت بخطأ `SERVER_VERSION_INCOMPATIBLE` ورسالة تُشير إلى كلٍ من النطاق المطلوب وإصدار الخادم الفعلي. +* إذا كان `engines.twenty` **غير مُعين**، فسيُقبل التطبيق على أي إصدار من الخادم (متوافق مع الإصدارات السابقة للتطبيقات الحالية). +* إذا لم يكن لدى الخادم قيمة `APP_VERSION` مُكوَّنة، فسيتم تخطي الفحص. + + +الخادم هو الجهة المرجعية للفحص — إذ يتحقق من `engines.twenty` عند كلٍ من رفع tarball وتثبيت مساحة العمل. إذا قمت بنشر tarball خارج القناة المعتادة أو التثبيت من السوق، فسيظل الخادم يفرض التوافق. + + +## CI/CD المؤتمتة (مهام سير عمل مُولَّدة بالقوالب) + +التطبيقات المُولَّدة باستخدام `create-twenty-app` تأتي افتراضيًا مع مهمَّتي سير عمل من GitHub Actions ضمن `.github/workflows/`. هي جاهزة للتشغيل بمجرد دفع المستودع إلى GitHub — لا حاجة لأي إعداد إضافي لـ CI، وCD يتطلّب سرًّا واحدًا فقط. + +### CI — `ci.yml` + +يشغّل اختبارات التكامل عند كل دفع إلى `main` وعند كل طلب سحب. + +**ماذا يفعل:** + +1. يجلب مصدر تطبيقك. +2. ينشئ مثيلاً اختبارياً معزولاً من Twenty باستخدام الإجراء المركّب `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (المكافئ في CI للأمر `yarn twenty docker:start --test`). +3. يُفعِّل Corepack، ويُعدّ Node.js من ملف `.nvmrc` لديك، ويثبّت التبعيات بواسطة `yarn install --immutable`. +4. يشغّل `yarn test`، ويمرّر `TWENTY_API_URL` و`TWENTY_API_KEY` من المثيل الذي تم إنشاؤه بحيث تتمكّن اختباراتك من التواصل مع خادم حقيقي. + +**خيارات التكوين:** + +* `TWENTY_VERSION` (متغيّر بيئة، القيمة الافتراضية `latest`) — ثبّت نسخة خادم Twenty المستخدمة في CI عبر تعديل هذا في `ci.yml`. +* يتم تجميع التشغيل المتزامن حسب `github.ref` ويلغي التشغيلات قيد التقدّم عند أي دفع جديد. + +لا تتطلّب أي أسرار — مثيل الاختبار مؤقّت ويستمر فقط طوال مدّة المهمّة. + +### CD — `cd.yml` + +ينشر تطبيقك إلى خادم Twenty مُهيّأ عند كل دفع إلى `main`، وبشكل اختياري من طلب سحب عند تطبيق الوسم `deploy`. + +**ماذا يفعل:** + +1. يجلب رأس طلب السحب (للطلبات الموسومة) أو الالتزام المدفوع. +2. يشغّل `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — وهو المكافئ في CI للأمر `yarn twenty app:publish --private`. +3. يشغّل `twentyhq/twenty/.github/actions/install-twenty-app@main` بحيث تُثبَّت النسخة المُنشَرة حديثًا في مساحة العمل المستهدفة. + +**التكوين المطلوب:** + +| الإعداد | حيث | الغرض | +| ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| `TWENTY_DEPLOY_URL` | `env` في `cd.yml` (القيمة الافتراضية `http://localhost:3000`) | خادم Twenty الذي سيتم النشر إليه. غيّر هذا إلى عنوان URL لخادمك الحقيقي قبل أول استخدام. | +| `TWENTY_DEPLOY_API_KEY` | في مستودع GitHub **Settings → Secrets and variables → Actions** | مفتاح API يمتلك إذن النشر على الخادم المستهدف. | + + +القيمة الافتراضية لـ `TWENTY_DEPLOY_URL` وهي `http://localhost:3000` مجرد عنصر نائب — لن تصل إلى أي شيء من مُشغِّل مستضاف لدى GitHub. حدّثها إلى عنوان URL العام لخادمك (أو استخدم مُشغِّلًا مستضافًا ذاتيًا مع وصول شبكي) قبل تمكين CD. + + +**تشغيل نشر معاينة من طلب سحب:** + +أضِف الوسم `deploy` إلى طلب سحب. الشرط `if:` في `cd.yml` سيشغّل المهمّة لذلك الطلب مستخدمًا التزام رأس الطلب، مما يتيح لك التحقّق من التغيير على الخادم المستهدف قبل الدمج. + +### تثبيت الإجراءات القابلة لإعادة الاستخدام + +يشير كلا سيرَي العمل إلى إجراءات قابلة لإعادة الاستخدام عند `@main`، لذا تُلتقط تحديثات الإجراءات في مستودع `twentyhq/twenty` تلقائيًا. إذا كنت تريد بناءات حتمية، فاستبدِل `@main` بقيمة SHA لالتزام أو بوسم إصدار في كل سطر `uses:`. + +## النشر على npm + +يُتيح النشر على npm إمكانية العثور على تطبيقك في سوق Twenty. يمكن لأي مساحة عمل في Twenty استعراض تطبيقات السوق وتثبيتها وترقيتها مباشرةً من واجهة المستخدم. + +### المتطلبات + +* حساب على [npm](https://www.npmjs.com) +* الكلمة المفتاحية `twenty-app` في مصفوفة `keywords` في `package.json` (أضفها يدويًا — فهي غير مضمنة افتراضيًا في قالب `create-twenty-app`) + +```json filename="package.json" +{ + "name": "twenty-app-postcard-sender", + "version": "1.0.0", + "keywords": ["twenty-app"] +} +``` + +### بيانات التعريف لسوق التطبيقات + +يدعم إعداد `defineApplication()` حقولًا اختيارية تتحكم في كيفية ظهور تطبيقك في السوق. استخدم `logoUrl` و`screenshots` للإشارة إلى الصور من مجلد `public/`: + +```ts src/application-config.ts +export default defineApplication({ + universalIdentifier: '...', + displayName: 'My App', + description: 'A great app', + logoUrl: 'public/logo.png', + screenshots: [ + 'public/screenshot-1.png', + 'public/screenshot-2.png', + ], +}); +``` + +اطّلع على [أكورديون defineApplication](/l/ar/developers/extend/apps/config/application#marketplace-metadata) في صفحة بناء التطبيقات للاطلاع على القائمة الكاملة لحقول السوق (`author` و`category` و`aboutDescription` و`websiteUrl` و`termsUrl` وغيرها). + +#### أبعاد لقطات الشاشة الموصى بها + +يعرض السوق `screenshots` داخل حاوية ثابتة بنسبة `8:5` (على سبيل المثال، `1600×1000 px`). + + +تعرض لقطات الشاشة بأي نسبة عرض إلى ارتفاع بالكامل ولن يتم اقتطاعها مطلقًا، ولكن أي شيء أطول أو أضيق بكثير من `8:5` سيظهر مساحات فارغة على الجانبين. + + +### النشر + +```bash filename="Terminal" +yarn twenty app:publish +``` + +للنشر تحت dist-tag معيّن (مثلًا: `beta` أو `next`): + +```bash filename="Terminal" +yarn twenty app:publish --tag beta +``` + +### كيف تعمل آلية الاكتشاف في السوق + +يقوم خادم Twenty بمزامنة كتالوج السوق من سجل npm **كل ساعة**. + +يمكنك تشغيل المزامنة فورًا بدلًا من الانتظار: + +```bash filename="Terminal" +yarn twenty dev:catalog-sync +# To target a specific remote: +# yarn twenty dev:catalog-sync --remote production +``` + +تأتي بيانات التعريف المعروضة في السوق من إعداد `defineApplication()` — حقول مثل `displayName` و`description` و`author` و`category` و`logoUrl` و`screenshots` و`aboutDescription` و`websiteUrl` و`termsUrl`. + + +إذا لم يحدد تطبيقك `aboutDescription` في `defineApplication()`، فسيستخدم السوق تلقائيًا ملف `README.md` الخاص بحزمتك من npm كمحتوى لصفحة حول. هذا يعني أنه يمكنك الاحتفاظ بملف README واحد لكل من npm وسوق Twenty. إذا كنت تريد وصفًا مختلفًا في السوق، فقم بتعيين `aboutDescription` بشكل صريح. + + +### النشر عبر CI + +استخدم سير عمل GitHub Actions هذا للنشر تلقائيًا مع كل إصدار (يستخدم [OIDC](https://docs.npmjs.com/trusted-publishers)): + +```yaml filename=".github/workflows/publish.yml" +name: Publish +on: + release: + types: [published] + +permissions: + contents: read + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: https://registry.npmjs.org + - run: yarn install --immutable + - run: npx twenty dev:build + - run: npm publish --provenance --access public + working-directory: .twenty/output +``` + +بالنسبة لأنظمة CI الأخرى (GitLab CI، وCircleCI، إلخ)، تنطبق الأوامر الثلاثة نفسها: `yarn install`، ثم `yarn twenty dev:build`، ثم `npm publish` من `.twenty/output`. + + +**npm provenance** اختياري ولكنه موصى به. يضيف النشر باستخدام `--provenance` شارة ثقة إلى إدراجك على npm، مما يتيح للمستخدمين التحقق من أن الحزمة تم بناؤها من التزام محدد ضمن خط أنابيب CI عام. راجع [وثائق npm provenance](https://docs.npmjs.com/generating-provenance-statements) للحصول على تعليمات الإعداد. + + +## تثبيت التطبيقات + +بعد نشر التطبيق (npm) أو نشره (tarball)، يمكن لمساحات العمل تثبيته عبر واجهة المستخدم. + +اذهب إلى صفحة **الإعدادات > التطبيقات** في Twenty، حيث يمكن استعراض تطبيقات السوق والتطبيقات المنشورة عبر tarball وتثبيتها. + +{/* TODO: add screenshot of the UI when the app is registered */} + +يمكنك أيضًا تثبيت التطبيقات من سطر الأوامر: + +```bash filename="Terminal" +yarn twenty app:install +``` + + +يفرض الخادم اعتماد إصدارات semver عند التثبيت، بما يعكس القواعد المطبّقة عند النشر: + +* تثبيت الإصدار نفسه المثبّت بالفعل في مساحة عملك يُرفَض بخطأ `APP_ALREADY_INSTALLED`. +* تثبيت إصدار أدنى من الإصدار المثبّت حاليًا يُرفَض بخطأ `CANNOT_DOWNGRADE_APPLICATION`. + +لتثبيت إصدار أحدث، انشره (deploy) أو انشره إلى السجل (publish) أولًا، ثم أعد تشغيل `yarn twenty app:install`. + diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx new file mode 100644 index 0000000000..4e3e806214 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/operations/sync-and-recovery.mdx @@ -0,0 +1,111 @@ +--- +title: المزامنة والاستعادة +description: أي أمر تستخدمه ومتى، وكيفية قراءة مخرجات المزامنة، وسلّم استعادة لما يجب فعله عندما تنحرف البيانات الوصفية المحلية — قبل الوصول إلى إعادة تعيين كاملة. +icon: بوصلة +--- + +يدور تطوير التطبيقات محليًا حول **المزامنة**: يقوم الـ CLI بإعادة إنشاء ملف manifest الخاص بك ويطبّق الخادم فقط الفرق بينه وبين البيانات الوصفية الموجودة بالفعل في مساحة العمل لديك. تغطي هذه الصفحة الأمر الذي ينبغي استخدامه، وكيفية قراءة ما غيّرته المزامنة، وما الذي يجب فعله — بالترتيب — عندما تبدو الحالة المحلية غير متسقة. + +## أي أمر، ومتى + + +للتكرار اليومي المحلي ستحتاج تقريبًا دائمًا إلى `yarn twenty dev`. يُستخدم النشر والإصدار لإطلاق الإصدارات، **وليس** للحلقة المحلية. + + +| ترغب في… | أمر | الملاحظات | +| ------------------------------------------------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| التكرار محليًا مع المزامنة الحية | `yarn twenty dev` | يراقب ملفاتك ويجري مزامنة عند كل تغيير. | +| مزامنة واحدة ثم إنهاء (CI، السكربتات، الخطّافات) | `yarn twenty dev --once` | عملية إنشاء واحدة + مزامنة، ثم إنهاء. | +| معاينة التغييرات **بدون تطبيقها** | `yarn twenty dev --once --dry-run` | يحتسب الفرق ويطبعه؛ ولا يكتب أي شيء. | +| إزالة التطبيق من مساحة العمل | `yarn twenty app:uninstall` | أضف `--yes` لتخطي رسالة التأكيد. | +| إرسال ملف tarball إلى خادم | `yarn twenty app:publish --private` | يتطلّب إصدارًا **أعلى بشكل صارم** في `package.json` — راجع قسم [النشر](/l/ar/developers/extend/apps/operations/publishing). | +| النشر في السوق (npm) | `yarn twenty app:publish` | — | +| تثبيت / ترقية إصدار منشور | `yarn twenty app:install` | يُثبّت الإصدار المنشور حاليًا. | +| مسح الخادم المحلي والبدء من جديد | `yarn twenty docker:reset` | يحذف **كل** البيانات المحلية — كملاذ أخير. | + +### لا تحتاج المزامنة المحلية إلى زيادة في الإصدار + +تنطبق قاعدة `version` المتزايدة بدقة (`VERSION_ALREADY_EXISTS` عند النشر، و`APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` عند التثبيت) على **`app:publish` / `app:install`** — مسار الإصدارات. يقوم `yarn twenty dev` بمزامنة ملف manifest في مكانه ولا يتطلّب تغيير الإصدار أبدًا، لذا لست بحاجة إلى تعديل `package.json` للتكرار. إذا وجدت نفسك تزيد الإصدار لاختبار تغيير محلي، فأنت تستخدم مسار الإصدارات بينما ما تريده هو حلقة التطوير. + +## قراءة مخرجات المزامنة + +كل عملية مزامنة تطبع التغييرات في البيانات الوصفية التي تم تطبيقها (أو التي سيتم تطبيقها، مع خيار `--dry-run`): + +```text filename="Terminal" +Metadata changes: 2 created, 1 updated, 1 deleted + created objectMetadata rocket + created fieldMetadata timelineActivities + updated fieldMetadata launchedAt + deleted pageLayout legacyTab +✓ Synced +``` + +هذه أداتك الأولى للتشخيص: تُخبرك بدقة ما الكائنات والحقول والتخطيطات التي تغيّرت، بحيث يمكنك التأكد من أن المزامنة أنجزت ما توقّعته قبل التحقّق من واجهة المستخدم. + +عندما تفشل المزامنة على كيان واحد، يذكر الخطأ اسم الكيان المسبب للمشكلة و`universalIdentifier` الخاص به، على سبيل المثال: + +```text +Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed +``` + +استخدم ذلك المعرّف للعثور على الكيان في ملف manifest الخاص بك (وإن لزم الأمر، في مساحة العمل) بدلًا من تخمين أيّها يتعارض. + +## معاينة التغييرات (تشغيل تجريبي dry run) + +يبني `yarn twenty dev --once --dry-run` ملف manifest الخاص بك، ويطلب من الخادم خطة الترحيل، ويطبعها — **بدون تطبيق أي شيء**. إنها الطريقة الآمنة للإجابة عن سؤال "ما الذي ستغيّره هذه المزامنة؟" قبل الالتزام بها. + +```bash filename="Terminal" +yarn twenty dev --once --dry-run +``` + +```text filename="Terminal" +Building manifest... +Computing metadata diff (dry run, nothing will be applied)... +Metadata changes: 1 created, 1 updated + created fieldMetadata timelineActivities + updated objectMetadata rocket +✓ Dry run complete for My App — no changes were applied +``` + +تشغيل تجريبي: + +* **لا يكتب أي شيء** — لا ترحيل لبيانات وصفية، ولا تحديث لسجل التطبيق، ولا تغييرات في الأدوار/التبويبات الافتراضية، ولا توليد لعميل API. +* يُرجع **نفس الفرق** الذي ستُطبِّقه مزامنة حقيقية، حتى تتمكن من مراجعة الكيانات التي سيتم إنشاؤها/تحديثها/حذفها مسبقًا. +* يكون مفيدًا قبل إجراء تغيير محفوف بالمخاطر، أو عند مراجعة تغيير تم إنشاؤه بواسطة الذكاء الاصطناعي، أو في سكربت يجب أن يفشل إذا كان تغيير غير متوقَّع على وشك الحدوث. + + +يُعاين التشغيل التجريبي فقط **تغييرات البيانات الوصفية**، ويتطلّب أن يكون التطبيق قد تمت مزامنته مرة واحدة على الأقل (حتى تعرف به مساحة العمل). إذا شغّلته ضد تطبيق لم تتم مزامنته من قبل، سيبلغ الخادم أن التطبيق غير مُثبّت — شغّل `yarn twenty dev` مرة واحدة أولًا. + + +## سلّم الاستعادة + +عندما تبدو البيانات الوصفية المحلية غير صحيحة، صعِّد الإجراءات بهذا الترتيب وتوقّف بمجرد زوال العائق. كل خطوة أكثر إرباكًا من التي قبلها. + +1. **أعد المزامنة.** شغّل `yarn twenty dev --once` مرة أخرى. عمليات المزامنة متطابِقة الأثر (idempotent) — إعادة تشغيل ملف manifest النظيف آمنة وغالبًا ما تحل تعثرًا عابرًا. +2. **عاين الخطة.** شغّل `yarn twenty dev --once --dry-run` لرؤية ما الذي تنوي المزامنة التالية تغييره بالضبط، بدون تطبيقه. +3. **اقرأ الخطأ المسمّى.** إذا فشلت المزامنة، لاحظ نوع البيانات الوصفية و`universalIdentifier` في الرسالة (انظر أعلاه) وحدّد ذلك الكيان في ملف manifest الخاص بك. يشير التعارض عادةً إلى معرّف مكرر أو مُعاد استخدامه. +4. **إلغاء التثبيت وإعادة التثبيت.** شغّل `yarn twenty app:uninstall`، ثم أجرِ مزامنة مرة أخرى (`yarn twenty dev`). هذا يعيد بناء بيانات التطبيق الوصفية من نقطة بداية نظيفة مع إبقاء باقي مساحة العمل سليمة. +5. **إعادة تعيين كاملة (الملاذ الأخير).** شغّل `yarn twenty docker:reset`، ثم أعد التهيئة والمزامنة. + + +يقوم `yarn twenty docker:reset` بحذف **كل** البيانات في النسخة المحلية لديك — كل مساحة عمل، وكل سجل، وكل تطبيق. استخدمه فقط بعد فشل الخطوات السابقة. + + + +هل واجهت خطأ في البيانات الوصفية؟ يرجى [فتح مشكلة](https://github.com/twentyhq/twenty/issues/new/choose) وإرفاق رسالة الترحيل الفاشلة (بما في ذلك نوع البيانات الوصفية و`universalIdentifier`)، ومخرجات `Metadata changes` من عملية المزامنة، والأوامر التي شغّلتها. + + +## تجنّب إجراء مزامنات متزامنة على مساحة عمل واحدة + +تطبّق المزامنة عمليات ترحيل للبيانات الوصفية. قد يؤدّي تشغيل عدة عمليات مزامنة أو نشر أو تثبيت ضد **نفس** مساحة العمل في الوقت نفسه — على سبيل المثال، عدّة نوافذ طرفية أو وكلاء ذكاء اصطناعي يتكرّرون بالتوازي — إلى تداخل عمليات الترحيل تلك وترك البيانات الوصفية في حالة مطبَّقة جزئيًا. + +يقوم الخادم بتسلسل عمليات المزامنة لكل مساحة عمل لمنع ذلك، لكن ما زال ينبغي عليك تمرير عمليات البيانات الوصفية الحساسة عبر عملية **واحدة** بدلًا من تنفيذها بالتوازي. إذا كنت تنظّم التطوير باستخدام عدة وكلاء، فمرّر استدعاءات المزامنة/النشر/التثبيت عبر طابور واحد حتى تعمل واحدة فقط في الوقت نفسه. + +## تمييز أنواع الإخفاقات + +عندما يحدث خلل ما، يتيح لك فرق البيانات الوصفية والأخطاء المسمّاة تحديد موضع الفشل: + +* **خطأ في إنشاء ملف manifest** — يفشل الـ CLI قبل إجراء المزامنة (`MANIFEST_BUILD_FAILED`، `TYPECHECK_FAILED`)؛ أصلِح كود التطبيق لديك. +* **خطأ في المزامنة / الترحيل** — تنجح عملية الإنشاء لكن يفشل تطبيق الفرق، مع تسمية الكيان و`universalIdentifier`؛ أصلِح البيانات الوصفية المتعارِضة. +* **خطأ في وقت تشغيل كود التطبيق** — تتم المزامنة بنجاح، ولكن دوال المنطق أو المكوّنات لديك لا تعمل بشكل صحيح أثناء وقت التشغيل؛ تحقّق من [سجلات الدوال](/l/ar/developers/extend/apps/operations/cli). +* **حالة المثيل المحلي** — لا ينطبق أيّ مما سبق وما زالت مساحة العمل تبدو غير صحيحة؛ تابع النزول في سلّم الاستعادة. diff --git a/packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx new file mode 100644 index 0000000000..89282760dd --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/extend/apps/operations/testing.mdx @@ -0,0 +1,301 @@ +--- +title: الاختبار +description: إعداد Vitest، واختبارات تكامل مقابل خادم Twenty حقيقي، والتحقق من الأنواع، والتكامل المستمر (CI) باستخدام GitHub Actions. +icon: flask +--- + +يوفّر SDK واجهات برمجة قابلة للتنفيذ برمجيًا تمكّنك من بناء تطبيقك ونشره وتثبيته وإلغاء تثبيته من شيفرة الاختبار. بالاقتران مع [Vitest](https://vitest.dev/) وعملاء واجهة البرمجة مضبوطي الأنواع، يمكنك كتابة اختبارات تكامل تتحقّق من أن تطبيقك يعمل من البداية إلى النهاية مقابل خادم Twenty حقيقي. + +## استخدام حِزَم npm + +يمكنك تثبيت واستخدام أي حزمة npm في تطبيقك. يتم تجميع كلٍ من الدوال المنطقية والمكوّنات الأمامية باستخدام [esbuild](https://esbuild.github.io/)، والذي يُضمّن جميع التبعيات ضمن المخرجات — لا حاجة إلى `node_modules` وقت التشغيل. + +### تثبيت حزمة + +```bash filename="Terminal" +yarn add axios +``` + +ثم استوردها في شيفرتك: + +```ts src/logic-functions/fetch-data.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import axios from 'axios'; + +const handler = async (): Promise => { + const { data } = await axios.get('https://api.example.com/data'); + + return { data }; +}; + +export default defineLogicFunction({ + universalIdentifier: '...', + name: 'fetch-data', + description: 'Fetches data from an external API', + timeoutSeconds: 10, + handler, +}); +``` + +وينطبق الأمر نفسه على المكوّنات الأمامية: + +```tsx src/front-components/chart.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { format } from 'date-fns'; + +const DateWidget = () => { + return

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

; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'date-widget', + component: DateWidget, +}); +``` + +### كيف يعمل التجميع + +تستخدم خطوة البناء أداة esbuild لإنتاج ملف واحد مستقل لكل دالة منطقية ولكل مكوّن أمامي. تُضمَّن جميع الحزم المستوردة داخل الحزمة. + +**الدوال المنطقية** تعمل في بيئة Node.js. الوحدات المدمجة في Node (`fs` و`path` و`crypto` و`http` وغيرها) متاحة ولا تحتاج إلى تثبيت. + +**المكوّنات الأمامية** تعمل ضمن Web Worker. وحدات Node المدمجة غير متاحة — المتاح فقط واجهات برمجة المتصفّح وحِزَم npm التي تعمل في بيئة المتصفّح. + +كلتا البيئتين تحتويان على `twenty-client-sdk/core` و`twenty-client-sdk/metadata` كوحدات متاحة مُسبقًا — لا تُضمَّن هذه ضمن الحزم بل تُحلّ وقت التشغيل بواسطة الخادم. + +## إعداد + +يتضمّن التطبيق المُولَّد بالقالب بالفعل Vitest. إذا أعددته يدويًا، فثبّت التبعيات: + +```bash filename="Terminal" +yarn add -D vitest vite-tsconfig-paths +``` + +أنشئ `vitest.config.ts` في جذر تطبيقك: + +```ts vitest.config.ts +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + tsconfigPaths({ + projects: ['tsconfig.spec.json'], + ignoreConfigErrors: true, + }), + ], + test: { + testTimeout: 120_000, + hookTimeout: 120_000, + include: ['src/**/*.integration-test.ts'], + setupFiles: ['src/__tests__/setup-test.ts'], + env: { + TWENTY_API_URL: 'http://localhost:2020', + TWENTY_API_KEY: 'your-api-key', + }, + }, +}); +``` + +أنشئ ملف إعداد يتحقّق من إمكانية الوصول إلى الخادم قبل تشغيل الاختبارات: + +```ts src/__tests__/setup-test.ts +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { beforeAll } from 'vitest'; + +const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020'; +const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test'); + +beforeAll(async () => { + // Verify the server is running + const response = await fetch(`${TWENTY_API_URL}/healthz`); + + if (!response.ok) { + throw new Error( + `Twenty server is not reachable at ${TWENTY_API_URL}. ` + + 'Start the server before running integration tests.', + ); + } + + // Write a temporary config for the SDK + fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true }); + + fs.writeFileSync( + path.join(TEST_CONFIG_DIR, 'config.json'), + JSON.stringify({ + remotes: { + local: { + apiUrl: process.env.TWENTY_API_URL, + apiKey: process.env.TWENTY_API_KEY, + }, + }, + defaultRemote: 'local', + }, null, 2), + ); +}); +``` + +## واجهات SDK البرمجية + +يُصدِّر المسار الفرعي `twenty-sdk/cli` دوالًا يمكنك استدعاؤها مباشرةً من شيفرة الاختبار: + +| دالة | الوصف | +| -------------- | ----------------------------------------- | +| `appBuild` | بناء التطبيق واختياريًا حزم ملف tarball | +| `appDeploy` | رفع ملف tarball إلى الخادم | +| `appInstall` | تثبيت التطبيق على مساحة العمل النشطة | +| `appUninstall` | إلغاء تثبيت التطبيق من مساحة العمل النشطة | + +تُرجع كل دالة كائن نتيجة يحتوي على `success: boolean` وعلى إمّا `data` أو `error`. + +## كتابة اختبار تكامل + +إليك مثالًا كاملًا يبني التطبيق وينشره ويثبّته، ثم يتحقّق من ظهوره في مساحة العمل: + +```ts src/__tests__/app-install.integration-test.ts +import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config'; +import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli'; +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +const APP_PATH = process.cwd(); + +describe('App installation', () => { + beforeAll(async () => { + const buildResult = await appBuild({ + appPath: APP_PATH, + tarball: true, + onProgress: (message: string) => console.log(`[build] ${message}`), + }); + + if (!buildResult.success) { + throw new Error(`Build failed: ${buildResult.error?.message}`); + } + + const deployResult = await appDeploy({ + tarballPath: buildResult.data.tarballPath!, + onProgress: (message: string) => console.log(`[deploy] ${message}`), + }); + + if (!deployResult.success) { + throw new Error(`Deploy failed: ${deployResult.error?.message}`); + } + + const installResult = await appInstall({ appPath: APP_PATH }); + + if (!installResult.success) { + throw new Error(`Install failed: ${installResult.error?.message}`); + } + }); + + afterAll(async () => { + await appUninstall({ appPath: APP_PATH }); + }); + + it('should find the installed app in the workspace', async () => { + const metadataClient = new MetadataApiClient(); + + const result = await metadataClient.query({ + findManyApplications: { + id: true, + name: true, + universalIdentifier: true, + }, + }); + + const installedApp = result.findManyApplications.find( + (app: { universalIdentifier: string }) => + app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER, + ); + + expect(installedApp).toBeDefined(); + }); +}); +``` + +## تشغيل الاختبارات + +تأكّد من تشغيل خادم Twenty المحلي لديك، ثم: + +```bash filename="Terminal" +yarn test +``` + +أو في وضع المراقبة أثناء التطوير: + +```bash filename="Terminal" +yarn test:watch +``` + +## التحقق من الأنواع + +يمكنك أيضًا تشغيل التحقق من الأنواع على تطبيقك دون تشغيل الاختبارات: + +```bash filename="Terminal" +yarn twenty dev:typecheck +``` + +يشغِّل هذا الأمر `tsc --noEmit` ويبلغ عن أي أخطاء في الأنواع. + +## التكامل المستمر (CI) باستخدام GitHub Actions + +تولّد أداة إنشاء الهيكل سير عمل GitHub Actions جاهزًا للاستخدام في `.github/workflows/ci.yml`. يشغّل اختبارات التكامل لديك تلقائيًا عند كل دفع إلى `main` وعلى طلبات السحب. + +سير العمل: + +1. يجلب الشيفرة الخاصة بك +2. يشغّل خادم Twenty مؤقتًا باستخدام الإجراء `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` +3. يثبّت التبعيات باستخدام `yarn install --immutable` +4. يشغّل `yarn test` مع حقن `TWENTY_API_URL` و`TWENTY_API_KEY` من مخرجات الإجراء + +```yaml .github/workflows/ci.yml +name: CI + +on: + push: + branches: + - main + pull_request: {} + +env: + TWENTY_VERSION: latest + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Spawn Twenty instance + id: twenty + uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main + with: + twenty-version: ${{ env.TWENTY_VERSION }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable Corepack + run: corepack enable + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + + - name: Run integration tests + run: yarn test + env: + TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} + TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }} +``` + +لا تحتاج إلى تهيئة أي أسرار — إذ يبدأ إجراء `spawn-twenty-docker-image` خادم Twenty عابرًا مباشرة في المشغّل ويُخرِج تفاصيل الاتصال. يتم توفير السر `GITHUB_TOKEN` تلقائيًا من قِبل GitHub. + +لتثبيت إصدار محدّد من Twenty بدلًا من `latest`، غيّر متغير البيئة `TWENTY_VERSION` في أعلى سير العمل. diff --git a/packages/twenty-docs/l/ar/developers/extend/capabilities/apis.mdx b/packages/twenty-docs/l/ar/developers/extend/capabilities/apis.mdx index 6039d0e7f5..fb54564d1d 100644 --- a/packages/twenty-docs/l/ar/developers/extend/capabilities/apis.mdx +++ b/packages/twenty-docs/l/ar/developers/extend/capabilities/apis.mdx @@ -88,7 +88,7 @@ Authorization: Bearer YOUR_API_KEY لتحسين الأمان، عيّن دوراً محدداً لتقييد الوصول: -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. انقر على الدور الذي ترغب في تعيينه 3. افتح علامة التبويب **التعيين** 4. ضمن **مفاتيح API**، انقر على **+ تعيين إلى مفتاح API** diff --git a/packages/twenty-docs/l/ar/developers/self-host/capabilities/docker-compose.mdx b/packages/twenty-docs/l/ar/developers/self-host/capabilities/docker-compose.mdx index 3d8c0eb0ad..741c332483 100644 --- a/packages/twenty-docs/l/ar/developers/self-host/capabilities/docker-compose.mdx +++ b/packages/twenty-docs/l/ar/developers/self-host/capabilities/docker-compose.mdx @@ -51,7 +51,7 @@ VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent. curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example ``` -2. **إنشاء رموز سرية** +2. **إنشاء مفتاح تشفير** قم بتشغيل الأمر التالي لإنشاء سلسلة عشوائية فريدة: @@ -59,16 +59,18 @@ VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent. openssl rand -base64 32 ``` - **مهم:** احتفظ بهذه القيمة سرية ولا تشاركها. + **مهم:** احتفظ بهذه القيمة سرية ولا تشاركها. فقدان `ENCRYPTION_KEY` يعني فقدان الوصول إلى كل سر مخزَّن في قاعدة البيانات (رموز OAuth، متغيرات التطبيق، أسرار TOTP، إلخ). 3. **تحديث الـ `.env`** استبدل قيمة النائب في ملف .env بالقيمة الرمزية المولدة: ```ini - APP_SECRET=first_random_string + ENCRYPTION_KEY=random_string ``` + راجع [دليل تدوير المفاتيح](/l/ar/developers/self-host/capabilities/key-rotation) للحصول على إرشادات حول تدويره بدون توقّف عن العمل. + 4. **تعيين كلمة مرور PostgreSQL** قم بتحديث قيمة `PG_DATABASE_PASSWORD` في ملف .env باستخدام كلمة مرور قوية بدون أحرف خاصة. diff --git a/packages/twenty-docs/l/ar/developers/self-host/capabilities/key-rotation.mdx b/packages/twenty-docs/l/ar/developers/self-host/capabilities/key-rotation.mdx new file mode 100644 index 0000000000..486bd0d241 --- /dev/null +++ b/packages/twenty-docs/l/ar/developers/self-host/capabilities/key-rotation.mdx @@ -0,0 +1,60 @@ +--- +title: تدوير المفاتيح +icon: rotate +--- + +يمتلك Twenty عائلتين مستقلتين من المفاتيح: + +* **مفاتيح توقيع JWT** — أزواج مفاتيح غير متماثلة ES256 (مع علامة `kid`) مُخزَّنة في `core."signingKey"`، تُستخدم لتوقيع والتحقق من رموز الوصول / التحديث. +* **مفتاح التشفير أثناء السكون (At-rest encryption key)** — `ENCRYPTION_KEY`، يُستخدم لتشفير رموز OAuth، ومتغيرات التطبيق، ومفاتيح التوقيع الخاصة، وقيم الإعدادات الحساسة، وأسرار TOTP داخل غلاف `enc:v2:`. + +يُعد `APP_SECRET` سراً قديماً مُحتفَظاً به لأغراض التوافق مع الإصدارات السابقة: عندما لا يكون `ENCRYPTION_KEY` مضبوطاً، فإنه يعمل كحل احتياطي لمفتاح التشفير أثناء السكون / ملفات تعريف الارتباط للجلسة، ولا يزال يتحقق من رموز الوصول HS256 الموجودة مسبقاً. سيتم إهماله (إيقاف دعمه). + +## مفاتيح توقيع JWT + +يحمل كل مفتاح قيمة `publicKey` (يُحتفَظ بها إلى أجل غير مسمى حتى يمكنها التحقق من الرموز المصدرة مسبقاً)، و`privateKey` مُشفَّراً (يُستخدم فقط أثناء كون المفتاح حالياً)، وراية `isCurrent` (صف واحد فقط في كل وقت)، وحقل `revokedAt` اختياري. + +### تدوير المفتاح الحالي + +اضبط `SIGNING_KEY_ROTATION_DAYS` للتفعيل: عندها تصدر مهمة cron يومية مفتاحًا حاليًا جديدًا بمجرد أن يصبح المفتاح القائم أقدم من تلك العتبة. لا يتم إبطال المفاتيح السابقة، لذلك تستمر الرموز الموقعة تحتها في التحقق. اترك المتغير غير معيّن لتعطيل التدوير التلقائي. + +ميزة التدوير التلقائي متوفّرة ابتداءً من الإصدار v2.6+. + +### إبطال مفتاح (في حالات التسريب / الطوارئ فقط) + +**Settings → Admin Panel → Signing keys → Revoke** على صف غير حالي. تعمل على مسح المادة الخاصة المشفرة، وتعيين `revokedAt`، ورفض كل رمز حالي موقَّع تحت هذا الـ `kid`. + +## تدوير `ENCRYPTION_KEY` + +أمر `secret-encryption:rotate` الموضَّح أدناه متوفر ابتداءً من الإصدار v2.6+. + +يُغلَّف كل مقدار مُشفَّر بالشكل `enc:v2:\:\`، حيث إن `\` هو بادئة مكوَّنة من 8 أرقام ست عشرية مشتقة من المفتاح الخام. تتم عملية التدوير (الاستبدال) أثناء العمل، وقابلة للاستئناف. + +1. **إنشاء مفتاح جديد**: `openssl rand -base64 32`. + +2. **ضَبْط المفتاحين معاً جنباً إلى جنب** في ملف `.env`، ثم إعادة تشغيل الخدمة: + ```ini + ENCRYPTION_KEY=NEW_VALUE + FALLBACK_ENCRYPTION_KEY=OLD_VALUE + ``` + تستخدم عمليات الكتابة الجديدة المفتاح الجديد، بينما لا تزال الصفوف الحالية تُفك تشفيرها عبر المفتاح الاحتياطي (fallback). + +3. **إعادة تشفير الصفوف الحالية**: + + ```bash + docker exec -it {server_container} yarn command:prod secret-encryption:rotate + ``` + + يتناول الأمر ستة مواقع (`connected-account-tokens`، `application-variable`، `application-registration-variable`، `signing-key-private-keys`، `sensitive-config-storage`، `totp-secrets`). يُهمِل عامل تصفية SQL الصفوف الموجودة بالفعل على `\` الجديد، لذلك يكون الأمر عديم الأثر التكراري (idempotent): يمكنك مقاطعته وإعادة تشغيله حسب الحاجة. يخرج بقيمة مختلفة عن الصفر إذا فشل أي صف — أعد تشغيله لإعادة المحاولة. + + | خيار | الوصف | + | ---------------------------------------- | ------------------------------------------------------------- | + | `-s, --site \` | حصر التنفيذ على موقع واحد فقط. | + | `-b, --batch-size \` | عدد الصفوف في كل دفعة (الافتراضي `200`، والحد الأقصى `5000`). | + | `-d, --dry-run` | فك التشفير + إعادة التشفير في الذاكرة، مع تخطي جملة `UPDATE`. | + +4. **إزالة المفتاح الاحتياطي (fallback)** بمجرد أن يُظهِر الخيار `--dry-run` عدم وجود صفوف متبقية: أزِل `FALLBACK_ENCRYPTION_KEY` وأعد التشغيل. + +## دعم `APP_SECRET` القديم + +تستخدم النُسخ الأقدم التي لم تُعيِّن `ENCRYPTION_KEY` مطلقاً المتغيِّر `APP_SECRET` كمفتاح التشفير أثناء السكون (وكسرّ ملف تعريف الارتباط الخاص بالجلسة، المشتقّ منه). يُحفَظ هذا المسار لأغراض التوافق مع الإصدارات السابقة ولكنه **مهمل (موقوف الدعم)** — عيِّن متغير `ENCRYPTION_KEY` مخصَّصاً واتبع إجراء التدوير الموضَّح أعلاه للانتقال بعيداً عنه. يبقى `APP_SECRET` نفسه قيد الاستخدام للتحقق من رموز الوصول HS256 القديمة. diff --git a/packages/twenty-docs/l/ar/developers/self-host/capabilities/setup.mdx b/packages/twenty-docs/l/ar/developers/self-host/capabilities/setup.mdx index 9cbd4582c3..ab47013a13 100644 --- a/packages/twenty-docs/l/ar/developers/self-host/capabilities/setup.mdx +++ b/packages/twenty-docs/l/ar/developers/self-host/capabilities/setup.mdx @@ -43,11 +43,26 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # افتراضي كل متغير موثق بوصف في لوحة الإدارة الخاصة بك في **الإعدادات → لوحة الإدارة → متغيرات التكوين**. -بعض إعدادات البنية التحتية مثل اتصالات قاعدة البيانات (`PG_DATABASE_URL`)، عناوين الخوادم (`SERVER_URL`)، وأسرار التطبيقات (`APP_SECRET`) يمكن ضبطها فقط عبر ملف `.env`. +بعض إعدادات البنية التحتية مثل اتصالات قاعدة البيانات (`PG_DATABASE_URL`)، عناوين الخوادم (`SERVER_URL`)، والأسرار (`ENCRYPTION_KEY`, `FALLBACK_ENCRYPTION_KEY`) يمكن ضبطها فقط عبر ملف `.env`. [مرجع تقني كامل →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts) +## مفاتيح التشفير + +تستخدم Twenty مفتاحَي تشفير يحددان حصراً عبر متغيرات البيئة: + +| المتغيّر | الغرض | مطلوب | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| `ENCRYPTION_KEY` | المفتاح الأساسي المستخدم لتشفير الأسرار أثناء التخزين (رموز OAuth، متغيرات التطبيق، المفاتيح الخاصة لمفاتيح التوقيع، أسرار TOTP، قيم الإعدادات الحساسة). | نعم في عمليات التثبيت الجديدة (قد تعتمد عمليات التثبيت القديمة بدلاً من ذلك على `APP_SECRET` — انظر أدناه) | +| `FALLBACK_ENCRYPTION_KEY` | مفتاح مخصص للتحقق فقط. يتم تعيينه أثناء التدوير ليكون `ENCRYPTION_KEY` السابق حتى تظل الصفوف الحالية قابلة لفك التشفير. | فقط أثناء التدوير | + +لضمان التوافق مع الإصدارات السابقة، إذا لم يتم تعيين `ENCRYPTION_KEY`، فإن Twenty تستخدم `APP_SECRET` كبديل لتشفير البيانات أثناء التخزين — بما يطابق سلوك الإصدارات القديمة في عمليات النشر الأقدم. يجب على عمليات التثبيت الجديدة دائماً تعيين قيمة مخصصة لـ `ENCRYPTION_KEY`. + +قم بإنشاء القيم باستخدام الأمر `openssl rand -base64 32` وخزنها في مكان آمن (مثل مدير الأسرار، إعدادات مُشفَّرة، إلخ). فقدان `ENCRYPTION_KEY` يعني فقدان الوصول إلى كل سر مخزن في قاعدة البيانات. + +لتدوير `ENCRYPTION_KEY` بدون وقت توقف، راجع [دليل تدوير المفاتيح](/l/ar/developers/self-host/capabilities/key-rotation). + ## 2. إعداد بيئي فقط ```bash diff --git a/packages/twenty-docs/l/ar/developers/self-host/capabilities/upgrade-guide.mdx b/packages/twenty-docs/l/ar/developers/self-host/capabilities/upgrade-guide.mdx index 52f677ffe3..2563549e59 100644 --- a/packages/twenty-docs/l/ar/developers/self-host/capabilities/upgrade-guide.mdx +++ b/packages/twenty-docs/l/ar/developers/self-host/capabilities/upgrade-guide.mdx @@ -31,6 +31,18 @@ cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {pos على سبيل المثال، الترقية من v1.22 مباشرةً إلى v2.0 مدعومة بالكامل. +## الترقية إلى v2.5+ — غلاف التشفير للبيانات الساكنة (at-rest) + +بدءًا من **v2.5**، يقوم Twenty بتخزين الأسرار أثناء السكون (at-rest) — مثل رموز OAuth، ومتغيرات التطبيق، ومفاتيح التوقيع الخاصة، وقيم الإعدادات الحساسة، وأسرار TOTP — داخل غلاف ذي إصدار `enc:v2:` ومشفّر باستخدام `ENCRYPTION_KEY` (أو `APP_SECRET` إذا لم يتم تعيين `ENCRYPTION_KEY`). + +أول إقلاع على v2.5 يشغّل أوامر ترقية بطيئة تقوم بإجراء **ملء رجعي** للصفوف الموجودة داخل الغلاف الجديد. هذه الأوامر عديمة الأثر عند التكرار (idempotent) — إيقاف الخادم وإعادة تشغيله يستأنف من حيث توقّف — لكنها قد تستغرق بعض الوقت على قواعد البيانات الكبيرة. يمكنك متابعة التقدّم باستخدام `upgrade:status`. + +يجب تعيين `ENCRYPTION_KEY` مخصص **قبل** الترقية إلى v2.5 لكي يكتب الملء الرجعي الصفوف باستخدامه منذ البداية. يتطلّب تبديل المفاتيح بعد الملء الرجعي إجراء [تدوير](/l/ar/developers/self-host/capabilities/key-rotation). + +## تدوير الأسرار ومفاتيح التوقيع + +لمهام التشغيل اليومية مثل تدوير `ENCRYPTION_KEY`، أو تدوير مفتاح توقيع JWT، أو إبطال مفتاح توقيع تم تسريبه، راجع [دليل تدوير المفاتيح (Key rotation guide)](/l/ar/developers/self-host/capabilities/key-rotation). + ## التحقق من حالة الترقية يتيح لك الأمر `upgrade:status` فحص الحالة الحالية لمثيلك وعمليات ترحيل مساحات العمل. يكون مفيدًا لاستكشاف مشكلات الترقية وإصلاحها أو عند تقديم طلب دعم. diff --git a/packages/twenty-docs/l/ar/navigation.json b/packages/twenty-docs/l/ar/navigation.json index 9125e27054..6459fff16c 100644 --- a/packages/twenty-docs/l/ar/navigation.json +++ b/packages/twenty-docs/l/ar/navigation.json @@ -155,7 +155,27 @@ "label": "نظرة عامة" }, "apps": { - "label": "التطبيقات" + "label": "التطبيقات", + "groups": { + "appsGettingStarted": { + "label": "البدء" + }, + "appsConfig": { + "label": "التهيئة" + }, + "appsData": { + "label": "بيانات" + }, + "appsLogic": { + "label": "المنطق" + }, + "appsLayout": { + "label": "التخطيط" + }, + "appsOperations": { + "label": "العمليات" + } + } }, "api": { "label": "واجهة برمجة التطبيقات" diff --git a/packages/twenty-docs/l/ar/twenty-ui/input/buttons.mdx b/packages/twenty-docs/l/ar/twenty-ui/input/buttons.mdx index 93f1b4d03c..66443f7fdc 100644 --- a/packages/twenty-docs/l/ar/twenty-ui/input/buttons.mdx +++ b/packages/twenty-docs/l/ar/twenty-ui/input/buttons.mdx @@ -13,7 +13,7 @@ icon: مؤشر اليد - + ```jsx import { Button } from "@/ui/input/button/components/Button"; diff --git a/packages/twenty-docs/l/ar/twenty-ui/input/color-scheme.mdx b/packages/twenty-docs/l/ar/twenty-ui/input/color-scheme.mdx index 33b3fea632..4a56997699 100644 --- a/packages/twenty-docs/l/ar/twenty-ui/input/color-scheme.mdx +++ b/packages/twenty-docs/l/ar/twenty-ui/input/color-scheme.mdx @@ -12,7 +12,7 @@ icon: لوحة الألوان يمثل مخططات ألوان مختلفة ومخصص بشكل خاص للمواضيع الفاتحة والداكنة. - + ```jsx import { ColorSchemeCard } from "twenty-ui/display"; diff --git a/packages/twenty-docs/l/ar/twenty-ui/input/image-input.mdx b/packages/twenty-docs/l/ar/twenty-ui/input/image-input.mdx index 05031a7ffc..d1bab5e4af 100644 --- a/packages/twenty-docs/l/ar/twenty-ui/input/image-input.mdx +++ b/packages/twenty-docs/l/ar/twenty-ui/input/image-input.mdx @@ -1,12 +1,12 @@ --- -title: "\x062A\x062F\x062E\x064A\x0644 \x0627\x0644\x0635\x0648\x0631\x0629" +title: Image Input --- رأس الصفحة -4A4F33452D 44445245332A2E2F454A46 28452F 482532274429 35483129. +Allows users to upload and remove an image. @@ -25,7 +25,7 @@ export const MyComponent = () => { | الخصائص | النوع | الوصف | | ------------ | ----------- | --------------------------------------------------------------------------------- | -| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A | +| صورة | نص | The image source URL | | onUpload | دالة | الدالة التي تُستدعى عند قيام المستخدم بتحميل صورة جديدة. تستقبل كائن `File` كوسيط | | onRemove | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإزالة | | onAbort | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإلغاء أثناء تحميل الصورة | diff --git a/packages/twenty-docs/l/ar/twenty-ui/input/radio.mdx b/packages/twenty-docs/l/ar/twenty-ui/input/radio.mdx index 45f79f2615..316a48fcce 100644 --- a/packages/twenty-docs/l/ar/twenty-ui/input/radio.mdx +++ b/packages/twenty-docs/l/ar/twenty-ui/input/radio.mdx @@ -38,7 +38,7 @@ export const MyComponent = () => { /> ); }; -},{ + ``` diff --git a/packages/twenty-docs/l/ar/twenty-ui/input/text.mdx b/packages/twenty-docs/l/ar/twenty-ui/input/text.mdx index 0a486c641e..31ca0ca7db 100644 --- a/packages/twenty-docs/l/ar/twenty-ui/input/text.mdx +++ b/packages/twenty-docs/l/ar/twenty-ui/input/text.mdx @@ -12,7 +12,7 @@ title: نص - + ```jsx import { TextInput } from "@/ui/input/components/TextInput"; diff --git a/packages/twenty-docs/l/ar/twenty-ui/navigation/links.mdx b/packages/twenty-docs/l/ar/twenty-ui/navigation/links.mdx index 332eb0783f..17b3a51494 100644 --- a/packages/twenty-docs/l/ar/twenty-ui/navigation/links.mdx +++ b/packages/twenty-docs/l/ar/twenty-ui/navigation/links.mdx @@ -12,7 +12,7 @@ icon: رابط مكون رابط منمق لعرض معلومات الاتصال. - + ```jsx import { BrowserRouter as Router } from 'react-router-dom'; @@ -35,7 +35,7 @@ export const MyComponent = () => { ); -};},{ +}; ``` diff --git a/packages/twenty-docs/l/ar/user-guide/ai/capabilities/mcp.mdx b/packages/twenty-docs/l/ar/user-guide/ai/capabilities/mcp.mdx index 0ebb7eb82c..5072f52976 100644 --- a/packages/twenty-docs/l/ar/user-guide/ai/capabilities/mcp.mdx +++ b/packages/twenty-docs/l/ar/user-guide/ai/capabilities/mcp.mdx @@ -103,11 +103,10 @@ MCP حاليًا في مرحلة **ألفا** وهو متاح فقط في بعض بعد الاتصال، يوفّر خادم MCP أدوات تعكس واجهة برمجة تطبيقات Twenty (API). سير العمل الموصى به هو: -1. **`get_tool_catalog`** — اكتشف جميع الأدوات المتاحة -2. **`learn_tools`** — احصل على مخطط الإدخال لأدوات محددة -3. **`execute_tool`** — شغّل أداة +1. **`learn_tools`** — احصل على مخطط الإدخال لأدوات محددة +2. **`execute_tool`** — شغّل أداة -لا تحتاج إلى تذكّر أسماء الأدوات. اسأل مساعد الذكاء الاصطناعي عمّا يمكنه فعله وسيستدعي `get_tool_catalog` تلقائيًا. +لا تحتاج إلى تذكّر أسماء الأدوات. اسأل مساعد الذكاء الاصطناعي عمّا يمكنه فعله وسيستدعي `learn_tools` تلقائيًا. ## الصلاحيات diff --git a/packages/twenty-docs/l/ar/user-guide/ai/capabilities/permissions-access-control.mdx b/packages/twenty-docs/l/ar/user-guide/ai/capabilities/permissions-access-control.mdx index fe7142975d..fceb31d0b1 100644 --- a/packages/twenty-docs/l/ar/user-guide/ai/capabilities/permissions-access-control.mdx +++ b/packages/twenty-docs/l/ar/user-guide/ai/capabilities/permissions-access-control.mdx @@ -9,7 +9,7 @@ description: تحكّم بما يمكن لوكلاء الذكاء الاصطنا ## تعيين دور لوكيل ذكاء اصطناعي -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. انقر على الدور الذي ترغب في تعيينه 3. افتح علامة التبويب **التعيين** 4. ضمن **وكلاء الذكاء الاصطناعي**، انقر **+ تعيين لوكيل ذكاء اصطناعي** diff --git a/packages/twenty-docs/l/ar/user-guide/ai/how-tos/ai-faq.mdx b/packages/twenty-docs/l/ar/user-guide/ai/how-tos/ai-faq.mdx index 47513752f6..05931310de 100644 --- a/packages/twenty-docs/l/ar/user-guide/ai/how-tos/ai-faq.mdx +++ b/packages/twenty-docs/l/ar/user-guide/ai/how-tos/ai-faq.mdx @@ -17,7 +17,7 @@ description: الأسئلة الشائعة حول ميزات الذكاء الا - سيعمل وكلاء الذكاء الاصطناعي ضمن نظام الأذونات. يمكنك تعيين أدوار محددة لوكلاء الذكاء الاصطناعي ضمن **الإعدادات → الأدوار**، مما يمنحك سيطرة كاملة على البيانات التي يمكنهم الوصول إليها والإجراءات التي يمكنهم القيام بها. + سيعمل وكلاء الذكاء الاصطناعي ضمن نظام الأذونات. يمكنك تعيين أدوار محددة لوكلاء الذكاء الاصطناعي ضمن **الإعدادات → الأعضاء → الأدوار**، مما يمنحك سيطرة كاملة على البيانات التي يمكنهم الوصول إليها والإجراءات التي يمكنهم القيام بها. diff --git a/packages/twenty-docs/l/ar/user-guide/ai/overview.mdx b/packages/twenty-docs/l/ar/user-guide/ai/overview.mdx index 8baa5e85d2..97c251caf8 100644 --- a/packages/twenty-docs/l/ar/user-guide/ai/overview.mdx +++ b/packages/twenty-docs/l/ar/user-guide/ai/overview.mdx @@ -48,7 +48,7 @@ description: ميزات مدعومة بالذكاء الاصطناعي قادم سيُدار وكلاء الذكاء الاصطناعي عبر نظام الأذونات الحالي: -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. حدِّد البيانات التي يمكن لكل وكيل ذكاء اصطناعي الوصول إليها 3. عيّن أذونات القراءة/الكتابة لكل كائن diff --git a/packages/twenty-docs/l/ar/user-guide/billing/capabilities/pricing-plans.mdx b/packages/twenty-docs/l/ar/user-guide/billing/capabilities/pricing-plans.mdx index 1e029638c4..6f44952235 100644 --- a/packages/twenty-docs/l/ar/user-guide/billing/capabilities/pricing-plans.mdx +++ b/packages/twenty-docs/l/ar/user-guide/billing/capabilities/pricing-plans.mdx @@ -19,7 +19,7 @@ description: تعرّف على خطط تسعير Twenty وكيفية التبد * دعم قياسي -Premium features (SSO, row-level permissions and AI usage data) are not included in the Pro plan. +الميزات المتميزة (SSO، أذونات على مستوى الصف وبيانات استخدام الذكاء الاصطناعي) غير مشمولة في خطة Pro. ### المؤسسة (سحابي) @@ -27,7 +27,7 @@ Premium features (SSO, row-level permissions and AI usage data) are not included للفرق الأكبر ذات الاحتياجات المتقدّمة: * كل ما في Pro -* **Premium features**: SSO integration, row-level permissions and AI usage data +* **الميزات المتميزة**: تكامل SSO، أذونات على مستوى الصف وبيانات استخدام الذكاء الاصطناعي * دعم متميز ## خطط الاستضافة الذاتية @@ -45,7 +45,7 @@ Premium features (SSO, row-level permissions and AI usage data) are not included للفرق التي تحتاج إلى ميزات متميزة أثناء الاستضافة الذاتية: * جميع ميزات Pro -* **Premium features**: SSO integration, row-level permissions and AI usage data +* **الميزات المتميزة**: تكامل SSO، أذونات على مستوى الصف وبيانات استخدام الذكاء الاصطناعي * دعم فريق Twenty * لا يُشترط نشر الشيفرة المخصّصة كمفتوح المصدر قبل التوزيع @@ -55,7 +55,7 @@ Premium features (SSO, row-level permissions and AI usage data) are not included * **تكامل SSO**: تسجيل دخول أحادي مع موفّر الهوية لديك * **أذونات على مستوى الصف**: تحكّم دقيق في الوصول على مستوى السجل -* **AI usage data**: Track AI consumption across the workspace +* **بيانات استخدام الذكاء الاصطناعي**: تتبع استهلاك الذكاء الاصطناعي عبر مساحة العمل ## التبديل بين الخطط @@ -79,14 +79,14 @@ Premium features (SSO, row-level permissions and AI usage data) are not included تواصل مع الدعم للعودة إلى الفوترة الشهرية. -## Obtain an Enterprise Key for Organization (Self-Hosted) +## احصل على مفتاح Enterprise لخطة Organization (Self-Hosted) -To use the Organization (Self-Hosted) plan, you need to obtain an Enterprise key: +لاستخدام خطة Organization (Self-Hosted)، تحتاج إلى الحصول على مفتاح Enterprise: -1. Go to **Settings → Admin Panel → Enterprise** +1. اذهب إلى **الإعدادات → لوحة الإدارة → Enterprise** -Enterprise key +مفتاح Enterprise -2. Click **Get Enterprise Key** -3. When you are redirected to Stripe, enter your payment details and confirm -4. When your Enterprise key is displayed, paste it into the Enterprise settings page and activate the Organization license +2. انقر **احصل على مفتاح Enterprise** +3. عند إعادة توجيهك إلى Stripe، أدخل تفاصيل الدفع الخاصة بك وأكّد +4. عند عرض مفتاح Enterprise الخاص بك، الصقه في صفحة إعدادات Enterprise وقم بتفعيل ترخيص Organization diff --git a/packages/twenty-docs/l/ar/user-guide/calendar-emails/overview.mdx b/packages/twenty-docs/l/ar/user-guide/calendar-emails/overview.mdx index cf2f7015c6..d6a9d4abda 100644 --- a/packages/twenty-docs/l/ar/user-guide/calendar-emails/overview.mdx +++ b/packages/twenty-docs/l/ar/user-guide/calendar-emails/overview.mdx @@ -25,15 +25,25 @@ description: Connect your email and calendar accounts to Twenty. 6. Configure calendar sync settings (visibility, auto-creation) → click **Add Account** 7. ستبدأ رسائل البريد الإلكتروني وفعاليات التقويم بالمزامنة تلقائيًا -### إعداد SMTP/CalDAV (مزودون آخرون) +### إعداد IMAP/SMTP/CalDAV (مزودون آخرون) بالنسبة لمزودي البريد الإلكتروني والتقويم الآخرين: 1. اذهب إلى **الإعدادات → الحسابات** -2. قم بتكوين إعدادات SMTP للبريد الإلكتروني +2. قم بتهيئة إعدادات IMAP لمزامنة رسائل البريد الإلكتروني الواردة وإعدادات SMTP لإرسال البريد الإلكتروني 3. قم بتكوين إعدادات CalDAV للتقويم 4. اختبر الاتصال + +**الاستضافة الذاتية على شبكة معزولة هوائيًا أو شبكة داخلية**: بشكل افتراضي، يرفض Twenty الاتصالات الصادرة إلى عناوين IP الخاصة/الداخلية (حماية SSRF). إذا كان خادم البريد أو التقويم يعمل على عنوان IP محلي/خاص (مثل خادم داخلي على شبكة LAN)، فسيتم حظر الاتصالات به. للسماح بهذه الاتصالات، قم بتعيين متغير البيئة التالي على الخادم: + +``` +OUTBOUND_HTTP_SAFE_MODE_ENABLED=false +``` + +هذا يعطّل الوضع الآمن لكل طلبات الإرسال الصادرة (**HTTP workflow actions**، و **webhooks**، واتصالات **IMAP/SMTP/CalDAV**)، لذا لا تقم بتفعيله إلا على الشبكات المعزولة والموثوقة حيث لا تكون حماية SSRF مطلوبة. + + ### صناديق بريد متعددة * **حسابات غير محدودة**: ربط حسابات بريد إلكتروني متعددة لكل مستخدم @@ -59,10 +69,19 @@ description: Connect your email and calendar accounts to Twenty. * **معطل**: لا يتم إنشاء جهات اتصال تلقائيًا * **للرسائل المرسلة والمستلمة**: إنشاء جهات اتصال لجميع التفاعلات البريدية الخارجية * **للرسائل المرسلة فقط**: إنشاء جهات اتصال فقط لرسائل البريد التي ترسلها -* **ملاحظة**: لا تتم مزامنة رسائل البريد الداخلية (نفس النطاق) للحفاظ على الخصوصية +* **ملاحظة**: بشكل افتراضي، لا تتم مزامنة الرسائل الإلكترونية الداخلية (عندما يشترك جميع المشاركين في نفس النطاق) لحماية الخصوصية When enabled, contacts are automatically linked to their Company records based on their email domain. If the company doesn't exist yet, Twenty creates it for you. + +**مزامنة الرسائل الإلكترونية الداخلية**: سلوك "عدم مزامنة الرسائل الإلكترونية الداخلية" هو السلوك الافتراضي، ولكن يمكن إيقافه. مفتاح التبديل موجود في الإعدادات المتقدمة: +1. افتح **الإعدادات** وفعّل مفتاح التبديل **المتقدمة** في أسفل صفحة الإعدادات +2. اذهب إلى **عام → الأمان** +3. فعّل مفتاح التبديل **مزامنة الرسائل الإلكترونية الداخلية** لتضمين الرسائل الإلكترونية التي يشترك فيها جميع المشاركين في نفس النطاق + +هذا إعداد على مستوى مساحة العمل (مفيد للجامعات أو المؤسسات ذات النطاق المشترك). + + ### التحكم بالرسائل التي تتم مزامنتها من خلال اختيار مجلد الرسائل تحكم بما تم مزامنته من مجلدات البريد الإلكتروني مع Twenty: @@ -79,7 +98,7 @@ description: Connect your email and calendar accounts to Twenty. **ما الذي يتم مزامنته:** * **رسائل البريد الخارجية**: جميع رسائل البريد الإلكتروني مع جهات اتصال خارجية من المجلدات المحددة -* **الرسائل الداخلية**: لا يتم مزامنتها (تبقى رسائل البريد من نفس النطاق خاصة) +* **الرسائل الداخلية**: لا تتم مزامنتها بشكل افتراضي (تبقى رسائل البريد من نفس النطاق خاصة). فعّل مفتاح التبديل **المتقدمة** في أسفل **الإعدادات**، ثم شغّل **مزامنة الرسائل الإلكترونية الداخلية** ضمن **عام → الأمان** لتضمينها على مستوى مساحة العمل. * **المرفقات**: ستأتي في النصف الأول من 2026 **ملاحظة**: لا نقدم عنوان بريد إلكتروني لنسخة كربونية للمزامنة الانتقائية. بدلاً من ذلك، استخدم ميزة مجلد الرسائل المذكورة أعلاه لتحقيق نفس مستوى التحكم حول أي الرسائل يتم مزامنتها مع Twenty. diff --git a/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/field-mapping.mdx b/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/field-mapping.mdx index 88c6b72414..04fef427aa 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/field-mapping.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/field-mapping.mdx @@ -56,7 +56,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; استخدم التنسيق التالي: ``` -[\"value1\",\"value2\"] +["value1","value2"] ``` ### حقول القيم المنطقية @@ -94,7 +94,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; * للبريد الإلكتروني الإضافي: استخدم **Emails / Primary Email** للبريد الرئيسي، و**Emails / Additional Emails** بهذا التنسيق: ``` -[\"jane@twenty.com\",\"jane.doe@twenty.com\"] +["jane@twenty.com","jane.doe@twenty.com"] ``` ### حقول المعرّف @@ -125,7 +125,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; * للروابط الثانوية، استخدم عمود **Links / Secondary Links** بهذا التنسيق: ``` -[{\"url\":\"https://twenty.com\",\"label\":\"Twenty\"}] +[{"url":"https://twenty.com","label":"Twenty"}] ``` ### حقول التحديد المتعدد @@ -133,7 +133,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; استخدم **أسماء واجهة برمجة التطبيقات (API)** (وليس تسميات العرض) بالتنسيق التالي: ``` -[\"VALUE1\",\"VALUE2\"] +["VALUE1","VALUE2"] ``` اطّلع [هنا](#finding-api-names-for-select-fields) لمعرفة مكان العثور على أسماء واجهة برمجة التطبيقات. @@ -143,7 +143,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; **الاستيراد يستبدل، لا يضيف.** -إذا كان السجل يحتوي بالفعل على `VALUE2` و`VALUE3` محدّدين، ثم استوردت `[\"VALUE1\"]`، فسيحتوي السجل على `VALUE1` فقط بعد الاستيراد. يتم استبدال التحديدات السابقة، وليس دمجها. +إذا كان السجل يحتوي بالفعل على `VALUE2` و`VALUE3` محدّدين، ثم استوردت `["VALUE1"]`، فسيحتوي السجل على `VALUE1` فقط بعد الاستيراد. يتم استبدال التحديدات السابقة، وليس دمجها. ### حقول الأرقام diff --git a/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/file-formats.mdx b/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/file-formats.mdx index 9a4773e612..11f2adddbb 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/file-formats.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-migration/capabilities/file-formats.mdx @@ -25,7 +25,7 @@ description: تنسيقات الملفات المدعومة لاستيراد ا ## أفضل الممارسات لملفات CSV * **المحدد**: استخدم الفاصلة (`,`) أو الفاصلة المنقوطة (`;`) -* **محدد النص**: استخدم علامات الاقتباس المزدوجة (`\"`) للنص الذي يحتوي على فواصل +* **محدد النص**: استخدم علامات الاقتباس المزدوجة (`"`) للنص الذي يحتوي على فواصل * **نهايات الأسطر**: Windows (CRLF) أو Unix (LF) كلاهما مدعومان * **القيم الفارغة**: اترك الخلايا فارغة، لا تستخدم "NULL" أو "N/A" diff --git a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx index 1f11481593..ba730e4e70 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx @@ -214,7 +214,7 @@ Jane,Doe,jane@widgets.co,https://widgets.co ### تكوين الأدوار والصلاحيات -* قم بإعداد الأدوار في **الإعدادات → الأدوار** +* قم بإعداد الأدوار في **الإعدادات → الأعضاء → الأدوار** * عيّن المستخدمين إلى الأدوار المناسبة ### اربط البريد الإلكتروني والتقويم diff --git a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx index 3374e2f161..fb1cba151a 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx @@ -127,7 +127,7 @@ Acme Corp,https://acme.com,john@yourcompany.com ### الأدوار والصلاحيات -* قم بتكوين الأدوار في **الإعدادات → الأدوار** +* قم بتكوين الأدوار في **الإعدادات → الأعضاء → الأدوار** * عيّن المستخدمين إلى الأدوار المناسبة ### التكاملات diff --git a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx index 62b1a05b7d..25aa9ac1cc 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx @@ -65,7 +65,7 @@ description: دليل كامل خطوة بخطوة لتنسيق بياناتك * للعناوين الإضافية للبريد الإلكتروني، استخدم هذا التنسيق في عمود **Emails / Additional Emails**: ``` -[\"jane@twenty.com\",\"jane.doe@twenty.com\"] +["jane@twenty.com","jane.doe@twenty.com"] ``` ### حقول النطاق diff --git a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx index 467c272843..6374761830 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx @@ -27,9 +27,9 @@ description: دليل كامل خطوة بخطوة لتحديث السجلات **يتم استبدال حقول الاختيار المتعدد، ولا يتم دمجها.** -إذا كان السجل محدّدًا فيه `Option A` و`Option B`، وقمتَ باستيراد `[\"Option C\"]`، فسيحتوي السجل على `Option C` فقط بعد الاستيراد. تستبدل عملية الاستيراد جميع الاختيارات السابقة — ولا تضيف إليها. +إذا كان السجل محدّدًا فيه `Option A` و`Option B`، وقمتَ باستيراد `["Option C"]`، فسيحتوي السجل على `Option C` فقط بعد الاستيراد. تستبدل عملية الاستيراد جميع الاختيارات السابقة — ولا تضيف إليها. -للاحتفاظ بالقيم الحالية، ضمّنها جميعًا في عملية الاستيراد: `[\"Option A\",\"Option B\",\"Option C\"]` +للاحتفاظ بالقيم الحالية، ضمّنها جميعًا في عملية الاستيراد: `["Option A","Option B","Option C"]` ## الخطوة 1: تصدير بياناتك الحالية diff --git a/packages/twenty-docs/l/ar/user-guide/data-model/capabilities/fields.mdx b/packages/twenty-docs/l/ar/user-guide/data-model/capabilities/fields.mdx index a70ec1786d..b0c6b558fd 100644 --- a/packages/twenty-docs/l/ar/user-guide/data-model/capabilities/fields.mdx +++ b/packages/twenty-docs/l/ar/user-guide/data-model/capabilities/fields.mdx @@ -101,6 +101,10 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; إذا ظهرت لك رسالة خطأ عند تعيين خاصية التفرد، فتحقق من وجود قيم مكررة في بياناتك (بما في ذلك السجلات المحذوفة). +## الفهارس (متقدّم) + +تُدار فهارس قاعدة البيانات تلقائيًا — ونادرًا ما تكون إضافة فهارسك الخاصة ضرورية، ومن السهل الوقوع في الأخطاء عند القيام بذلك. مع تفعيل الوضع المتقدّم، يكون لكل كائن قسم **Indexes** تحت `Settings → Data Model → ` للحالات التي تعلم فيها أنك بحاجة إلى فهرس. + ## أفضل ممارسات تكوين الحقول ### اتفاقيات التسمية والقيود diff --git a/packages/twenty-docs/l/ar/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/ar/user-guide/permissions-access/capabilities/permissions.mdx index 7336b516f8..c758508574 100644 --- a/packages/twenty-docs/l/ar/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/ar/user-guide/permissions-access/capabilities/permissions.mdx @@ -13,7 +13,7 @@ description: تحكّم في الوصول إلى الكائنات والحقول لإنشاء دور جديد: -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. تحت **كل الأدوار**، انقر على **+ إنشاء دور** 3. أدخل اسم الدور 4. في علامة التبويب الافتراضية **الأذونات**، [كوّن الأذونات](#customize-permissions) @@ -23,7 +23,7 @@ description: تحكّم في الوصول إلى الكائنات والحقول لحذف دور: -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. انقر على الدور الذي ترغب في إزالته 3. افتح علامة التبويب **الإعدادات**، ثم انقر على **حذف الدور** 4. انقر على **تأكيد** في النافذة المنبثقة @@ -36,13 +36,13 @@ description: تحكّم في الوصول إلى الكائنات والحقول ### عرض التعيينات الحالية -* اذهب إلى **الإعدادات → الأدوار** +* انتقل إلى **الإعدادات → الأعضاء → الأدوار** * رؤية جميع الأدوار وعدد الأعضاء المعينين لكل منها * عرض الأعضاء الذين لديهم الأدوار المختلفة ### تعيين دور لعضو -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. انقر على الدور الذي ترغب في تعيينه 3. افتح علامة التبويب **التعيين** 4. انقر على **+ تعيين لعضو** @@ -51,7 +51,7 @@ description: تحكّم في الوصول إلى الكائنات والحقول ### تعيين الدور الافتراضي -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. في قسم **الخيارات**، اعثر على **الدور الافتراضي** 3. اختر أي دور يجب أن يحصل عليه الأعضاء الجدد تلقائيًا 4. سيتم تعيين أعضاء مساحة العمل الجدد هذا الدور عند انضمامهم @@ -98,6 +98,22 @@ description: تحكّم في الوصول إلى الكائنات والحقول | الفرص → تعطيل "عرض السجلات" | لا يمكن للمتدرب رؤية كائن الفرص إطلاقًا | | الأشخاص → تفعيل "تحرير السجلات" | يمكن للمتدرب تحرير سجلات الأشخاص (ولكن ليس الكائنات الأخرى) | +### أذونات مستوى الصف + + +أذونات مستوى الصف هي **ميزة Premium** متاحة ضمن خطة **Organization** (السحابي والمستضاف ذاتيًا). + + +تتيح لك أذونات مستوى الصف تقييد السجلات الفردية التي يمكن للدور عرضها أو تعديلها، بناءً على معايير ديناميكية. على عكس أذونات الكائن (التي تنطبق على نوع الكائن بالكامل)، تقوم أذونات مستوى الصف بتقييم كل سجل بشكل مستقل. + +**أمثلة لحالات الاستخدام:** + +* يمكن لمندوبي المبيعات رؤية الفرص الخاصة بهم فقط +* يمكن للمديرين رؤية جميع السجلات في منطقتهم +* يمكن لوكلاء الدعم عرض التذاكر المخصصة لهم فقط + +لتهيئة أذونات مستوى الصف، افتح دورًا معيّنًا، وانتقل إلى علامة تبويب **Objects**، واستخدم قسم **Row-Level** لتحديد شروط التصفية لكائن معيّن. + ### أذونات الحقول داخل كل قاعدة على مستوى الكائن، يمكنك المتابعة أبعد من ذلك وتكوين **أذونات على مستوى الحقل** للتحكم في الوصول إلى حقول محددة. @@ -168,7 +184,7 @@ description: تحكّم في الوصول إلى الكائنات والحقول ### تعيين دور لمفتاح API -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. انقر على الدور الذي ترغب في تعيينه 3. افتح علامة التبويب **التعيين** 4. ضمن **مفاتيح API**، انقر على **+ تعيين إلى مفتاح API** @@ -183,7 +199,7 @@ description: تحكّم في الوصول إلى الكائنات والحقول ### تعيين دور لوكيل ذكاء اصطناعي -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. انقر على الدور الذي ترغب في تعيينه 3. افتح علامة التبويب **التعيين** 4. ضمن **وكلاء الذكاء الاصطناعي**، انقر على **+ تعيين إلى وكيل ذكاء اصطناعي** diff --git a/packages/twenty-docs/l/ar/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/ar/user-guide/permissions-access/how-tos/permissions-faq.mdx index 69e7b185be..7905e659a0 100644 --- a/packages/twenty-docs/l/ar/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/ar/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -19,7 +19,7 @@ description: الأسئلة الشائعة حول الأدوار والصلاح -انتقل إلى **Settings → Roles**، وابحث عن خيار **Default Role**، ثم اختر الدور الذي يجب أن يحصل عليه الأعضاء الجدد تلقائيًا عند انضمامهم. +انتقل إلى **الإعدادات → الأعضاء → الأدوار**، وابحث عن خيار **Default Role**، ثم اختر الدور الذي يجب أن يحصل عليه الأعضاء الجدد تلقائيًا عند انضمامهم. @@ -60,11 +60,11 @@ description: الأسئلة الشائعة حول الأدوار والصلاح -ستكون الصلاحيات على مستوى الصف متاحة ضمن خطة **Organization** بحلول الربع الأول من عام 2026. يتيح لك ذلك تقييد الوصول إلى سجلات محددة بناءً على معايير معينة (مثل: رؤية فرصك الخاصة فقط). +تتوفر الصلاحيات على مستوى الصف ضمن خطة **Organization**. يتيح لك ذلك تقييد الوصول إلى سجلات محددة بناءً على معايير معينة (مثل: رؤية فرصك الخاصة فقط). -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. اختر الدور 3. انتقل إلى الكائن الذي يحتوي على الحقل 4. عيّن صلاحية الحقل إلى **See Field** (من دون Edit Field) diff --git a/packages/twenty-docs/l/ar/user-guide/settings/capabilities/domains-settings.mdx b/packages/twenty-docs/l/ar/user-guide/settings/capabilities/domains-settings.mdx index 80edf2f5d8..d3477a4378 100644 --- a/packages/twenty-docs/l/ar/user-guide/settings/capabilities/domains-settings.mdx +++ b/packages/twenty-docs/l/ar/user-guide/settings/capabilities/domains-settings.mdx @@ -3,10 +3,12 @@ title: إعدادات النطاق description: قم بتكوين نطاق مساحة العمل، ونطاقات الوصول المعتمدة، والنطاقات العامة. --- -قم بتكوين إعدادات النطاق ضمن **الإعدادات → النطاقات**. +إعدادات النطاق موجودة في ثلاثة أماكن، حسب ما تريد تكوينه. ## نطاق مساحة العمل +قم بالتكوين ضمن **الإعدادات → عام → نطاق مساحة العمل**. + قم بتعديل اسم النطاق الفرعي الخاص بك أو عيّن نطاقًا مخصصًا لمساحة العمل. ### تخصيص النطاق @@ -19,6 +21,8 @@ description: قم بتكوين نطاق مساحة العمل، ونطاقات ## النطاقات المعتمدة +قم بالتكوين ضمن **الإعدادات → الأعضاء → دعوة**. + يُسمح لأي شخص لديه عنوان بريد إلكتروني ضمن هذه النطاقات بالتسجيل تلقائيًا في مساحة العمل هذه. ### إضافة نطاق وصول معتمد @@ -35,13 +39,16 @@ description: قم بتكوين نطاق مساحة العمل، ونطاقات ## النطاقات العامة -توفير بيئة استضافة كاملة وآمنة على هذه النطاقات. +قم بالتكوين ضمن **الإعدادات → التطبيقات → المطور**. + +توفير بيئة استضافة كاملة وآمنة على هذه النطاقات. يمكن ربط نطاق عام بتطبيق معيّن — عند ربطه، تكون دوال منطق HTTP الموجهة لهذا التطبيق وحدها قابلة للوصول على هذا النطاق. اترك الربط فارغًا لكشف جميع مسارات HTTP في مساحة العمل. ### إضافة نطاق عام 1. انقر **إضافة نطاق عام** 2. أدخل النطاق الذي تريد استخدامه -3. قم بتكوين إعدادات DNS وفق التعليمات -4. تحقق من النطاق +3. اربطه اختياريًا بتطبيق +4. قم بتكوين إعدادات DNS وفق التعليمات +5. تحقق من النطاق يتم توفير شهادات SSL تلقائيًا للنطاقات العامة. diff --git a/packages/twenty-docs/l/ar/user-guide/settings/capabilities/member-management.mdx b/packages/twenty-docs/l/ar/user-guide/settings/capabilities/member-management.mdx index 04f9a0e356..cbf8eccb05 100644 --- a/packages/twenty-docs/l/ar/user-guide/settings/capabilities/member-management.mdx +++ b/packages/twenty-docs/l/ar/user-guide/settings/capabilities/member-management.mdx @@ -77,7 +77,7 @@ description: دعوة أعضاء الفريق وإدارة الوصول إلى السماح لأعضاء الفريق بالانضمام تلقائيًا بناءً على نطاق بريدهم الإلكتروني: -1. اذهب إلى **الإعدادات → النطاقات** +1. انتقل إلى **الإعدادات → الأعضاء → دعوة** 2. أضف نطاق شركتك (مثل: `yourcompany.com`) 3. يمكن لأي شخص ينتمي بريده الإلكتروني إلى ذلك النطاق الانضمام من دون دعوة diff --git a/packages/twenty-docs/l/ar/user-guide/settings/how-tos/settings-faq.mdx b/packages/twenty-docs/l/ar/user-guide/settings/how-tos/settings-faq.mdx index 0bdd472134..fbf4f5a6a2 100644 --- a/packages/twenty-docs/l/ar/user-guide/settings/how-tos/settings-faq.mdx +++ b/packages/twenty-docs/l/ar/user-guide/settings/how-tos/settings-faq.mdx @@ -137,7 +137,7 @@ description: الأسئلة الشائعة حول إعدادات Twenty. -نعم! اذهب إلى **الإعدادات → النطاقات** ثم انقر **تخصيص النطاق**. لديك خياران: +نعم! اذهب إلى **الإعدادات → عام → نطاق مساحة العمل** ثم انقر **تخصيص النطاق**. لديك خياران: * **النطاق الفرعي**: استخدم نطاقًا فرعيًا من Twenty مثل `yourcompany.twenty.com` * **نطاق مخصص**: استخدم نطاقك الخاص مثل `crm.yourcompany.com` (يتطلب تهيئة DNS) @@ -146,7 +146,7 @@ description: الأسئلة الشائعة حول إعدادات Twenty. -يمكنك تكوين نطاقات الوصول المعتمدة بحيث يتمكن أعضاء الفريق ذوو عناوين البريد الإلكتروني الخاصة بالشركة من الانضمام تلقائيًا إلى مساحة العمل الخاصة بك. اذهب إلى **الإعدادات → النطاقات** وأضف نطاق شركتك (مثلًا، `yourcompany.com`). +يمكنك تكوين نطاقات الوصول المعتمدة بحيث يتمكن أعضاء الفريق ذوو عناوين البريد الإلكتروني الخاصة بالشركة من الانضمام تلقائيًا إلى مساحة العمل الخاصة بك. اذهب إلى **الإعدادات → الأعضاء → دعوة** وأضف نطاق شركتك (مثلًا، `yourcompany.com`). diff --git a/packages/twenty-docs/l/ar/user-guide/settings/overview.mdx b/packages/twenty-docs/l/ar/user-guide/settings/overview.mdx index 463481b9d8..f14bc7278b 100644 --- a/packages/twenty-docs/l/ar/user-guide/settings/overview.mdx +++ b/packages/twenty-docs/l/ar/user-guide/settings/overview.mdx @@ -44,7 +44,7 @@ description: قم بإعداد مساحة عملك في Twenty باستخدام 4. عيّن الأدوار المناسبة -قبل دعوة فريقك، تحقّق من الدور الافتراضي ضمن **الإعدادات → الأدوار**. يتم تعيين هذا الدور تلقائيًا للأعضاء الجدد عند انضمامهم. +قبل دعوة فريقك، تحقّق من الدور الافتراضي ضمن **الإعدادات → الأعضاء → الأدوار**. يتم تعيين هذا الدور تلقائيًا للأعضاء الجدد عند انضمامهم. ## قائمة التحقق لإعدادات مساحة العمل diff --git a/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx b/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx index bef5832bea..bf4ee63f55 100644 --- a/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx +++ b/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx @@ -38,7 +38,7 @@ description: احسب واعرض قيم الصفقات الموزونة استن إذا كنت لا تريد أن يحرّر المستخدمون هذه الحقول المحسوبة يدويًا: -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. حدِّد الدور لتكوينه 3. اعثر على كائن «الفرص» 4. عيّن حقلي **الاحتمال** و**المبلغ المتوقع** كحقول للقراءة فقط diff --git a/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx b/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx index 3ce2b64a45..0db72c9271 100644 --- a/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx +++ b/packages/twenty-docs/l/ar/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx @@ -61,7 +61,7 @@ description: راقِب سرعة الصفقات بتتبُّع وقت دخول إذا كنت لا تريد أن يحرّر المستخدمون هذه الحقول المحسوبة يدويًا: -1. اذهب إلى **الإعدادات → الأدوار** +1. انتقل إلى **الإعدادات → الأعضاء → الأدوار** 2. حدِّد الدور لتهيئته 3. اعثر على كائن الفرص 4. عيِّن حقول "آخر دخول" و"الأيام في" كحقول للقراءة فقط diff --git a/packages/twenty-docs/l/ar/user-guide/workflows/capabilities/workflow-actions.mdx b/packages/twenty-docs/l/ar/user-guide/workflows/capabilities/workflow-actions.mdx index 808b91d29f..ebf10683e5 100644 --- a/packages/twenty-docs/l/ar/user-guide/workflows/capabilities/workflow-actions.mdx +++ b/packages/twenty-docs/l/ar/user-guide/workflows/capabilities/workflow-actions.mdx @@ -307,5 +307,5 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; -يحترم وكلاء الذكاء الاصطناعي الأذونات المستندة إلى الأدوار. يمكنك تعيين أدوار محددة للوكلاء ضمن **الإعدادات → الأدوار** للتحكم في البيانات التي يمكنهم الوصول إليها. راجع [الأذونات](/l/ar/user-guide/permissions-access/capabilities/permissions) للحصول على التفاصيل. +يحترم وكلاء الذكاء الاصطناعي الأذونات المستندة إلى الأدوار. يمكنك تعيين أدوار محددة للوكلاء ضمن **الإعدادات → الأعضاء → الأدوار** للتحكم في البيانات التي يمكنهم الوصول إليها. راجع [الأذونات](/l/ar/user-guide/permissions-access/capabilities/permissions) للحصول على التفاصيل. diff --git a/packages/twenty-docs/l/ar/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx b/packages/twenty-docs/l/ar/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx index b01fb9ec0f..a7db85ce86 100644 --- a/packages/twenty-docs/l/ar/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx +++ b/packages/twenty-docs/l/ar/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx @@ -8,7 +8,7 @@ description: الأسئلة الشائعة حول سير العمل في Twenty. من المحتمل أن تكون مشكلة أذونات. تحتاج إلى صلاحية الوصول إلى سير العمل لإنشائها وتفعيلها. - **الحل**: تواصل مع مسؤول مساحة العمل لمنحك صلاحية الوصول إلى سير العمل ضمن **الإعدادات → الأدوار**. + **الحل**: تواصل مع مسؤول مساحة العمل لمنحك صلاحية الوصول إلى سير العمل ضمن **الإعدادات → الأعضاء → الأدوار**. إذا لم ترَ قسم سير العمل إطلاقاً في الشريط الجانبي لديك، فهذا يؤكد أنها مشكلة أذونات. diff --git a/packages/twenty-docs/l/cs/developers/extend/api.mdx b/packages/twenty-docs/l/cs/developers/extend/api.mdx index 1a38fee1d0..638f6ca796 100644 --- a/packages/twenty-docs/l/cs/developers/extend/api.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/api.mdx @@ -37,7 +37,7 @@ Both are available as REST and GraphQL. GraphQL adds batch upserts and the abili Authorization: Bearer YOUR_API_KEY ``` -Create an API key in **Settings → API & Webhooks → + Create key**. Copy it immediately — it's shown once. Keys can be scoped to a specific role under **Settings → Roles → Assignment tab** to limit what they can access. +Create an API key in **Settings → API & Webhooks → + Create key**. Copy it immediately — it's shown once. API klíče lze omezit na konkrétní roli v **Settings → Members → Roles → Assignment tab**, aby se omezilo, k čemu mají přístup. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/config/application.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/config/application.mdx new file mode 100644 index 0000000000..0df39dc7ef --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/config/application.mdx @@ -0,0 +1,64 @@ +--- +title: Konfigurace aplikace +description: Deklarujte identitu své aplikace, výchozí roli, proměnné a metadata tržiště pomocí defineApplication. +icon: rocket +--- + +Každá aplikace musí mít právě jedno volání `defineApplication`. Deklaruje: + +* **Identita** — univerzální identifikátor, zobrazovaný název, popis. +* **Oprávnění** — pod jakou rolí běží její logické funkce a frontendové komponenty. +* **Proměnné** *(volitelné)* — páry klíč–hodnota zpřístupněné vašemu kódu jako proměnné prostředí. +* **Předinstalační / postinstalační hooky** *(volitelné)* — viz [Logické funkce](/l/cs/developers/extend/apps/logic/logic-functions). + +```ts src/application-config.ts +import { defineApplication } from 'twenty-sdk/define'; + +export default defineApplication({ + universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d', + displayName: 'My Twenty App', + description: 'My first Twenty app', + applicationVariables: { + DEFAULT_RECIPIENT_NAME: { + universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', + description: 'Default recipient name for postcards', + value: 'Jane Doe', + isSecret: false, + }, + }, +}); +``` + +Poznámky: + +* Pole `universalIdentifier` jsou deterministické identifikátory, které vlastníte. Vygenerujte je jednou a zachovejte je stabilní napříč synchronizacemi. +* `applicationVariables` se stanou proměnnými prostředí pro vaše funkce a frontendové komponenty. V logických funkcích (na straně serveru) jsou dostupné jako `process.env.VARIABLE_NAME`. Ve frontendových komponentách použijte `getApplicationVariable('VARIABLE_NAME')` z `twenty-sdk/front-component`. Proměnné označené jako `isSecret: true` jsou předávány pouze do logických funkcí. Frontendové komponenty přijímají pouze proměnné, které nejsou tajné. +* Výchozí role je automaticky detekována ze souboru role označeného pomocí [`defineApplicationRole()`](/l/cs/developers/extend/apps/config/roles) — není potřeba na ni odkazovat z `defineApplication()`. +* Předinstalační a postinstalační funkce jsou při sestavení manifestu detekovány automaticky — není třeba na ně odkazovat v `defineApplication()`. +* Předávání `defaultRoleUniversalIdentifier` explicitně je stále podporováno kvůli zpětné kompatibilitě, ale je zastaralé ve prospěch `defineApplicationRole()`. + +## Výchozí role funkce + +Role deklarovaná pomocí [`defineApplicationRole()`](/l/cs/developers/extend/apps/config/roles) určuje, k čemu mají přístup logické funkce a front-endové komponenty aplikace: + +* Běhový token vložený jako `TWENTY_APP_ACCESS_TOKEN` je odvozen z této role. +* Typovaný klient API je omezen na oprávnění udělená této roli. +* Dodržujte princip nejmenších oprávnění: deklarujte pouze ta oprávnění, která vaše funkce potřebují. + +Když vygenerujete novou aplikaci, CLI vytvoří úvodní soubor role v `src/roles/default-role.ts`. Úplnou referenci najdete v části [Role a oprávnění](/l/cs/developers/extend/apps/config/roles). + +## Metadata tržiště + +Pokud plánujete [zveřejnit svou aplikaci](/l/cs/developers/extend/apps/operations/publishing), tato volitelná pole určují, jak se vaše aplikace zobrazuje v tržišti: + +| Pole | Popis | +| ------------------ | ------------------------------------------------------------------------------------------------------------- | +| `author` | Jméno autora nebo název společnosti | +| `category` | Kategorie aplikace pro filtrování v tržišti | +| `logoUrl` | Cesta k logu vaší aplikace (např. `public/logo.png`) | +| `screenshots` | Pole cest ke snímkům obrazovky (např. `public/screenshot-1.png`) | +| `aboutDescription` | Delší popis v Markdownu pro kartu "O aplikaci". Pokud je vynecháno, tržiště použije `README.md` balíčku z npm | +| `websiteUrl` | Odkaz na váš web | +| `termsUrl` | Odkaz na podmínky služby | +| `emailSupport` | E-mailová adresa podpory | +| `issueReportUrl` | Odkaz na nástroj pro sledování problémů | diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx new file mode 100644 index 0000000000..402f9aaf19 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/config/install-hooks.mdx @@ -0,0 +1,206 @@ +--- +title: Instalační hooky +description: Spouštějte logiku před instalací nebo po ní — naplňte data, zazálohujte záznamy, ověřte aktualizaci. +icon: klíč +--- + +Instalační hooky jsou speciální logické funkce, které se spouštějí během životního cyklu instalace nebo upgradu. Sdílí stejný runtime handleru jako běžné [logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) a přijímají `InstallPayload`, ale deklarují se pomocí vlastních definičních funkcí — `definePostInstallLogicFunction()` a `definePreInstallLogicFunction()` — a fungují mimo běžný model triggerů (HTTP, cron, databázové události). + +Každá aplikace může definovat **nanejvýš jednu pre-install** a **nanejvýš jednu post-install** funkci. Sestavení manifestu skončí chybou, pokud je zjištěno více než jedno. + +``` +┌─────────────────────────────────────────────────────────────┐ +│ install flow │ +│ │ +│ upload package → [pre-install] → metadata migration → │ +│ generate SDK → [post-install] │ +│ │ +│ old schema visible new schema visible │ +└─────────────────────────────────────────────────────────────┘ +``` + + + + +Postinstalační funkce se spustí automaticky, jakmile je instalace vaší aplikace v pracovním prostoru dokončena. Server ji provede **poté**, co byla synchronizována metadata aplikace a vygenerován klient SDK, takže je pracovní prostor plně připraven k použití a nové schéma je zavedeno. Mezi typické případy použití patří naplnění výchozími daty, vytvoření počátečních záznamů, konfigurace nastavení pracovního prostoru nebo zřizování prostředků ve službách třetích stran. + +```ts src/logic-functions/post-install.ts +import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; + +const handler = async (payload: InstallPayload): Promise => { + console.log('Post install logic function executed successfully!', payload.previousVersion); +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210', + name: 'post-install', + description: 'Runs after installation to set up the application.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + shouldRunSynchronously: false, + handler, +}); +``` + +Postinstalační funkci můžete také kdykoli spustit ručně pomocí CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec --postInstall +``` + +Hlavní body: +* Postinstalační funkce používají `definePostInstallLogicFunction()` — specializovanou variantu, která vynechává nastavení spouštěčů (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`). +* Obslužná funkce obdrží `InstallPayload` s `{ previousVersion?: string; newVersion: string }` — `newVersion` je verze, která se instaluje, a `previousVersion` je verze, která byla nainstalována dříve (nebo `undefined` při čisté instalaci). Tyto hodnoty použijte k rozlišení čistých instalací od aktualizací a ke spuštění migrační logiky specifické pro verzi. +* **Kdy se hook spouští**: ve výchozím nastavení pouze při čistých instalacích. Předejte `shouldRunOnVersionUpgrade: true`, pokud chcete, aby se spouštěl i při aktualizaci aplikace z předchozí verze. Pokud je vynechán, příznak má výchozí hodnotu `false` a při aktualizacích se hook přeskočí. +* **Model provádění — ve výchozím nastavení asynchronní, synchronní volitelně**: příznak `shouldRunSynchronously` určuje *jak* se spouští post-install. + * `shouldRunSynchronously: false` *(výchozí)* — hook je **zařazen do fronty zpráv** s `retryLimit: 3` a běží asynchronně ve workeru. Odezva instalace se vrátí hned po zařazení úlohy do fronty, takže pomalá nebo chybující obslužná funkce neblokuje volajícího. Worker se pokusí o opakování až třikrát. **Použijte pro dlouho běžící úlohy** — plnění velkých datových sad, volání pomalých externích API, zřizování externích prostředků, cokoli, co by mohlo přesáhnout rozumné časové okno HTTP odezvy. + * `shouldRunSynchronously: true` — hook se provádí **inline během instalačního procesu** (stejný vykonavatel jako pre-install). Instalační požadavek blokuje, dokud obslužná funkce nedokončí, a pokud vyvolá výjimku, volající instalace obdrží `POST_INSTALL_ERROR`. Žádné automatické opakování. **Použijte pro rychlé úlohy, které se musí dokončit před odpovědí** — například vrácení validační chyby uživateli nebo rychlé nastavení, na kterém bude klient záviset ihned po návratu volání instalace. Mějte na paměti, že v době, kdy se spustí post-install, už byla migrace metadat aplikována, takže selhání v synchronním režimu změny schématu **ne**vrací zpět — pouze odhalí chybu. +* Ujistěte se, že vaše obslužná funkce je idempotentní. V asynchronním režimu se může fronta pokusit až třikrát; v obou režimech se může hook znovu spustit při aktualizacích, pokud je `shouldRunOnVersionUpgrade: true`. +* Proměnné prostředí `APPLICATION_ID`, `APP_ACCESS_TOKEN` a `API_URL` jsou dostupné uvnitř obslužné funkce (stejně jako u jakékoli jiné logické funkce), takže můžete volat Twenty API s aplikačním přístupovým tokenem omezeným na vaši aplikaci. +* Na jednu aplikaci je povolena pouze jedna postinstalační funkce. Sestavení manifestu skončí chybou, pokud je zjištěna více než jedna. +* Atributy funkce `universalIdentifier`, `shouldRunOnVersionUpgrade` a `shouldRunSynchronously` jsou během buildu automaticky připojeny k manifestu aplikace do pole `postInstallLogicFunction` — není potřeba je uvádět v [`defineApplication()`](/l/cs/developers/extend/apps/config/application). +* Výchozí časový limit je nastaven na 300 sekund (5 minut), aby umožnil delší úlohy nastavení, jako je naplnění daty. +* **Nespouští se v režimu dev**: když je aplikace registrována lokálně (pomocí `yarn twenty dev`), server zcela přeskočí instalační tok a synchronizuje soubory přímo prostřednictvím sledovače CLI — takže se post-install v režimu dev nikdy nespustí bez ohledu na `shouldRunSynchronously`. Použijte `yarn twenty dev:function:exec --postInstall` k ručnímu spuštění nad běžícím pracovním prostorem. + + + + +Předinstalační funkce se během instalace spouští automaticky, **před aplikováním migrace metadat pracovního prostoru**. Má stejný tvar payloadu jako post-install (`InstallPayload`), ale je zařazena dříve v instalačním toku, aby mohla připravit stav, na němž nadcházející migrace závisí — typické použití zahrnuje zálohování dat, ověření kompatibility s novým schématem nebo archivaci záznamů, které se chystají přeuspořádat nebo odstranit. + +```ts src/logic-functions/pre-install.ts +import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; + +const handler = async (payload: InstallPayload): Promise => { + console.log('Pre install logic function executed successfully!', payload.previousVersion); +}; + +export default definePreInstallLogicFunction({ + universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab', + name: 'pre-install', + description: 'Runs before installation to prepare the application.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: true, + handler, +}); +``` + +Předinstalační funkci můžete také kdykoli spustit ručně pomocí CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec --preInstall +``` + +Hlavní body: +* Funkce pre-install používají `definePreInstallLogicFunction()` — stejné specializované nastavení jako u post-install, pouze připojené k jiné fázi životního cyklu. +* Obě obslužné funkce pre- i post-install přijímají stejný typ `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Importujte jej jednou a znovu použijte pro oba hooky. +* **Kdy se hook spouští**: umístěn těsně před migrací metadat pracovního prostoru (`synchronizeFromManifest`). Před spuštěním server provede čistě aditivní "zjednodušenou synchronizaci", která v metadatech pracovního prostoru zaregistruje pre-install funkci **nové** verze — ničeho dalšího se nedotkne — a poté ji spustí. Protože tato synchronizace je pouze aditivní, objekty, pole a data předchozí verze zůstávají při spuštění vaší obslužné funkce zachována: můžete bezpečně číst a zálohovat stav před migrací. +* **Model provádění**: pre-install se provádí **synchronně** a **blokuje instalaci**. Pokud obslužná funkce vyvolá výjimku, instalace se přeruší ještě před aplikováním jakýchkoli změn schématu — pracovní prostor zůstane na předchozí verzi v konzistentním stavu. Je to záměrné: pre-install je vaše poslední šance odmítnout rizikovou aktualizaci. +* Stejně jako u post-install je na jednu aplikaci povolena pouze jedna funkce pre-install. Během buildu je automaticky připojena k manifestu aplikace pod `preInstallLogicFunction`. +* **Nespouští se v režimu dev**: stejně jako u post-install — u lokálně registrovaných aplikací je instalační tok zcela přeskočen, takže se pre-install pod `yarn twenty dev` nikdy nespustí. Použijte `yarn twenty dev:function:exec --preInstall` k ručnímu spuštění. + + + + +Oba hooky jsou součástí téhož instalačního toku a přijímají stejný `InstallPayload`. Rozdíl je v tom, **kdy** se spouštějí vzhledem k migraci metadat pracovního prostoru, a to určuje, jakých dat se mohou bezpečně dotýkat. + +Pre-install je vždy **synchronní** (blokuje instalaci a může ji přerušit). Post-install je **ve výchozím nastavení asynchronní** — zařazen do workeru s automatickými pokusy o opakování — ale může přejít na synchronní provádění pomocí `shouldRunSynchronously: true`. Viz accordion `definePostInstallLogicFunction` výše, kdy použít jednotlivé režimy. + +**Použijte `post-install` pro cokoli, co vyžaduje existenci nového schématu.** To je běžný případ: + +* Plnění výchozími daty (vytváření počátečních záznamů, výchozích pohledů, demo obsahu) vůči nově přidaným objektům a polím. +* Registrace webhooků u služeb třetích stran poté, co má aplikace své přihlašovací údaje. +* Volání vlastního API k dokončení nastavení, které závisí na synchronizovaných metadatech. +* Idempotentní logika "zajisti, že to existuje", která má při každé aktualizaci uvést stav do souladu — kombinujte s `shouldRunOnVersionUpgrade: true`. + +Příklad — po instalaci naplňte výchozí záznam `PostCard`: + +```ts src/logic-functions/post-install.ts +import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +import { createClient } from './generated/client'; + +const handler = async ({ previousVersion }: InstallPayload): Promise => { + if (previousVersion) return; // fresh installs only + + const client = createClient(); + await client.postCard.create({ + data: { title: 'Welcome to Postcard', content: 'Your first card!' }, + }); +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210', + name: 'post-install', + description: 'Seeds a welcome post card after install.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + handler, +}); +``` + +**Použijte `pre-install`, pokud by migrace jinak zničila nebo poškodila existující data.** Protože pre-install běží proti *předchozímu* schématu a jeho selhání vrací aktualizaci zpět, je to správné místo pro cokoli rizikového: + +* **Zálohování dat, která se chystají odstranit nebo přeuspořádat** — např. odstraňujete pole ve verzi v2 a potřebujete jeho hodnoty zkopírovat do jiného pole nebo je před spuštěním migrace exportovat do úložiště. +* **Archivace záznamů, které by nové omezení zneplatnilo** — např. pole se stává `NOT NULL` a je třeba nejprve smazat nebo opravit řádky s hodnotami null. +* **Ověření kompatibility a odmítnutí aktualizace, pokud nelze aktuální data čistě migrovat** — vyhoďte výjimku z obslužné funkce a instalace se ukončí bez provedených změn. Je to bezpečnější, než zjistit nekompatibilitu uprostřed migrace. +* **Přejmenování nebo změna klíčů dat** před změnou schématu, která by ztratila vazby. + +Příklad — archivujte záznamy před destruktivní migrací: + +```ts src/logic-functions/pre-install.ts +import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +import { createClient } from './generated/client'; + +const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => { + // Only the 1.x → 2.x upgrade drops the legacy `notes` field. + if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) { + return; + } + + const client = createClient(); + const legacyRecords = await client.postCard.findMany({ + where: { notes: { isNotNull: true } }, + }); + + if (legacyRecords.length === 0) return; + + // Copy legacy `notes` into the new `description` field before the migration + // drops the `notes` column. If this fails, the upgrade is aborted and the + // workspace stays on v1 with all data intact. + await Promise.all( + legacyRecords.map((record) => + client.postCard.update({ + where: { id: record.id }, + data: { description: record.notes }, + }), + ), + ); +}; + +export default definePreInstallLogicFunction({ + universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab', + name: 'pre-install', + description: 'Backs up legacy notes into description before the v2 migration.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: true, + handler, +}); +``` + +**Zlaté pravidlo:** + +| Chcete... | Použít | +| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| Naplňte výchozí data, nakonfigurujte pracovní prostor, zaregistrujte externí prostředky | `post-install` | +| Spusťte dlouho běžící plnění nebo volání třetích stran, která by neměla blokovat odezvu instalace | `post-install` (výchozí — `shouldRunSynchronously: false`, s opakovanými pokusy workeru) | +| Spusťte rychlé nastavení, na které bude volající spoléhat ihned po návratu volání instalace | `post-install` s `shouldRunSynchronously: true` | +| Čtěte nebo zálohujte data, která by nadcházející migrace ztratila | `pre-install` | +| Odmítněte aktualizaci, která by poškodila existující data | `pre-install` (vyhoďte výjimku z obslužné funkce) | +| Spouštějte srovnání stavu při každé aktualizaci | `post-install` s `shouldRunOnVersionUpgrade: true` | +| Proveďte jednorázové nastavení pouze při první instalaci | `post-install` s `shouldRunOnVersionUpgrade: false` (výchozí) | + + +Pokud si nejste jisti, výchozí volbou je **post-install**. Po pre-install sáhněte pouze tehdy, když je samotná migrace destruktivní a potřebujete zachytit předchozí stav, než zmizí. + + + + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/config/overview.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/config/overview.mdx new file mode 100644 index 0000000000..4a6f98f5b1 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/config/overview.mdx @@ -0,0 +1,51 @@ +--- +title: Přehled +description: Nakonfigurujte samotnou aplikaci – její identitu, výchozí oprávnění a to, co se spouští při instalaci. +icon: screwdriver-wrench +--- + +**Konfigurační vrstva** aplikace Twenty popisuje aplikaci *platformě* – její identitu, oprávnění, která má, a kód, který se spouští během instalace nebo aktualizace. Tato deklarativní nastavení nepřidávají nové datové struktury ani chování za běhu; říkají Twenty, *co je aplikace zač* a *jak ji nastavit*. + +```text +┌────────────────────────────────────────────────────────┐ +│ Application — identity, default role, variables, │ +│ marketplace metadata │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Role — what the app's logic functions can read │ │ +│ │ and write (referenced by Application) │ │ +│ └──────────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────┘ + │ + ▼ (at install / upgrade time) + ┌──────────────────────────────────┐ + │ Pre-install hook │ before metadata migration + └──────────────────────────────────┘ + ┌──────────────────────────────────┐ + │ Post-install hook │ after metadata migration + └──────────────────────────────────┘ +``` + +## V této části + + + + `defineApplication` – identita, výchozí role, proměnné, metadata pro marketplace. + + + `defineRole` – deklaruje, co mohou logické funkce vaší aplikace číst a zapisovat. + + + `definePreInstallLogicFunction` a `definePostInstallLogicFunction` – zálohují data, nastavují výchozí hodnoty, validují aktualizace. + + + +## Jak spolu části souvisejí + +* **Aplikace** je vstupní bod. Každá aplikace má právě jedno volání `defineApplication()`, které ukazuje na jednu **roli** jako výchozí. +* **Role** určuje, co mohou logické funkce aplikace a front-endové komponenty číst a zapisovat. Dodržujte zásadu nejmenších oprávnění: udělujte pouze ta oprávnění, která váš kód skutečně potřebuje. +* **Instalační hooky** se spouštějí během instalace nebo aktualizace – pre-install před migrací metadat (aby mohly odmítnout rizikovou aktualizaci), post-install po migraci (aby mohly proti novému schématu naplnit výchozí data). + + +Instalační hooky sdílejí běhové prostředí [logických funkcí](/l/cs/developers/extend/apps/logic/logic-functions) – stejný podpis handleru, stejné proměnné prostředí, stejný typovaný klient API – ale deklarují se pomocí vlastních funkcí define a fungují mimo běžný model spouštěčů (HTTP, cron, databázové události). + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/config/public-assets.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/config/public-assets.mdx new file mode 100644 index 0000000000..6d6495c04e --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/config/public-assets.mdx @@ -0,0 +1,67 @@ +--- +title: Veřejné soubory +description: Doručujte statické soubory — obrázky, ikony, písma — spolu se svou aplikací prostřednictvím složky public/. +icon: folder-open +--- + +Složka `public/` v kořenu vaší aplikace obsahuje statické soubory — obrázky, ikony, písma a další prostředky, které vaše aplikace potřebuje za běhu. Tyto soubory jsou automaticky zahrnuty do buildů, synchronizovány během vývojového režimu a nahrávány na server. + +Soubory umístěné v `public/` jsou: + +* **Veřejně přístupné** — po synchronizaci na server jsou prostředky dostupné na veřejné URL. K přístupu k nim není potřeba žádná autentizace. +* **Dostupné ve frontendových komponentách** — použijte URL prostředků k zobrazení obrázků, ikon či jiných médií uvnitř komponent Reactu. +* **Dostupné v logických funkcích** — odkazujte na URL prostředků v e-mailech, odpovědích API či jiné serverové logice. +* **Používány pro metadata Marketplace** — pole `logoUrl` a `screenshots` v `defineApplication()` odkazují na soubory z této složky (např. `public/logo.png`). Zobrazují se v Marketplace, když je vaše aplikace zveřejněna. +* **Automaticky synchronizované ve vývojovém režimu** — když v `public/` přidáte, aktualizujete nebo smažete soubor, je automaticky synchronizován na server. Není potřeba restartovat. +* **Zahrnuté do buildů** — `yarn twenty dev:build` zabalí všechny veřejné prostředky do distribučního výstupu. + +## Přístup k veřejným prostředkům pomocí `getPublicAssetUrl` + +K získání plné URL souboru ve vaší složce `public/` použijte pomocnou funkci `getPublicAssetUrl` z `twenty-sdk`. Funguje jak v logických funkcích, tak ve frontendových komponentách. + +**V logické funkci:** + +```ts src/logic-functions/send-invoice.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const handler = async (): Promise => { + const logoUrl = getPublicAssetUrl('logo.png'); + const invoiceUrl = getPublicAssetUrl('templates/invoice.png'); + + // Fetch the file content (no auth required — public endpoint) + const response = await fetch(invoiceUrl); + const buffer = await response.arrayBuffer(); + + return { logoUrl, size: buffer.byteLength }; +}; + +export default defineLogicFunction({ + universalIdentifier: 'a1b2c3d4-...', + name: 'send-invoice', + description: 'Sends an invoice with the app logo', + timeoutSeconds: 10, + handler, +}); +``` + +**Ve frontendové komponentě:** + +```tsx src/front-components/company-card.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const CompanyCard = () => { + const logoUrl = getPublicAssetUrl('logo.png'); + + return App logo; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'company-card', + component: CompanyCard, +}); +``` + +Argument `path` je relativní ke složce `public/` vaší aplikace. Jak `getPublicAssetUrl('logo.png')`, tak `getPublicAssetUrl('public/logo.png')` se vyhodnotí na stejnou URL — předpona `public/` je, je-li přítomna, automaticky odstraněna. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/config/roles.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/config/roles.mdx new file mode 100644 index 0000000000..4b835413f5 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/config/roles.mdx @@ -0,0 +1,94 @@ +--- +title: Role a oprávnění +description: Určete, které objekty a pole mohou funkce aplikační logiky a front-endové komponenty vaší aplikace číst a zapisovat. +icon: shield-halved +--- + +**Role** je sada oprávnění: které objekty může aplikace číst nebo zapisovat, která pole může vidět a jaké schopnosti na úrovni platformy může používat. Všechny logické funkce aplikace a front-endové komponenty dědí oprávnění role označené pomocí `defineApplicationRole()` (viz [Výchozí role funkce](#the-default-function-role) níže). + +```ts src/roles/restricted-company-role.ts +import { + defineRole, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + SystemPermissionFlag, +} from 'twenty-sdk/define'; + +export default defineRole({ + universalIdentifier: '2c80f640-2083-4803-bb49-003e38279de6', + label: 'My new role', + description: 'A role that can be used in your workspace', + canReadAllObjectRecords: false, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canUpdateAllSettings: false, + canBeAssignedToAgents: false, + canBeAssignedToUsers: false, + canBeAssignedToApiKeys: false, + objectPermissions: [ + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + ], + fieldPermissions: [ + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + fieldUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name + .universalIdentifier, + canReadFieldValue: false, + canUpdateFieldValue: false, + }, + ], + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS], +}); +``` + +## Výchozí role funkce + +Když vygenerujete novou aplikaci, CLI vytvoří výchozí soubor role deklarovaný pomocí `defineApplicationRole()`: + +```ts src/roles/default-role.ts +import { defineApplicationRole } from 'twenty-sdk/define'; + +export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = + 'b648f87b-1d26-4961-b974-0908fd991061'; + +export default defineApplicationRole({ + universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, + label: 'Default function role', + description: 'Default role for function Twenty client', + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canUpdateAllSettings: false, + canBeAssignedToAgents: false, + canBeAssignedToUsers: false, + canBeAssignedToApiKeys: false, + objectPermissions: [], + fieldPermissions: [], + permissionFlagUniversalIdentifiers: [], +}); +``` + +`defineApplicationRole()` je tenký wrapper kolem `defineRole()`, který označuje **tu** roli, jež je při instalaci použita jako výchozí role vaší aplikace. Validace je shodná s `defineRole`, ale build pipeline automaticky propojí její `universalIdentifier` s `defaultRoleUniversalIdentifier` v manifestu aplikace — takže na něj nemusíte v [`defineApplication`](/l/cs/developers/extend/apps/config/application) sami odkazovat. + +Poznámky: + +* Na jednu aplikaci je povolena přesně **jedna** definice `defineApplicationRole(...)` — pokud build manifestu najde více než jednu, sestavení selže. +* Pro všechny **další** role, které vaše aplikace poskytuje, použijte `defineRole()` (nikoli `defineApplicationRole()`). +* Explicitní nastavení `defaultRoleUniversalIdentifier` v `defineApplication()` je stále podporováno kvůli zpětné kompatibilitě, ale je označeno jako zastaralé ve prospěch `defineApplicationRole()`. + +## Osvědčené postupy + +* Začněte od vygenerované role a postupně ji omezujte — výchozí nastavení poskytuje široká oprávnění pro čtení, což je v produkci zřídka žádoucí. +* Nahraďte `objectPermissions` a `fieldPermissions` přesně těmi objekty a poli, které vaše funkce skutečně potřebují. +* `permissionFlagUniversalIdentifiers` řídí přístup k schopnostem na úrovni platformy. Udržujte je co nejmenší. +* Podívejte se na funkční příklad: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/data/extending-objects.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/data/extending-objects.mdx new file mode 100644 index 0000000000..805b267ecb --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/data/extending-objects.mdx @@ -0,0 +1,50 @@ +--- +title: Rozšiřování objektů +description: Přidávejte pole ke standardním objektům Twenty (Person, Company, …) nebo k objektům z jiných aplikací pomocí `defineField`. +icon: wand-magic-sparkles +--- + +Použijte `defineField()` pro přidání pole k objektu, který nevlastníte — standardnímu objektu Twenty, jako je Person nebo Company, nebo objektu dodanému jinou nainstalovanou aplikací. Na rozdíl od inline polí deklarovaných uvnitř [`defineObject`](/l/cs/developers/extend/apps/data/objects) vyžadují samostatná pole `objectUniversalIdentifier` k určení, který objekt rozšiřují. + +```ts src/fields/company-loyalty-tier.field.ts +import { defineField, FieldType } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890', + objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object + name: 'loyaltyTier', + type: FieldType.SELECT, + label: 'Loyalty Tier', + icon: 'IconStar', + options: [ + { value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' }, + { value: 'SILVER', label: 'Silver', position: 1, color: 'gray' }, + { value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' }, + ], +}); +``` + +## Hlavní body + +* `objectUniversalIdentifier` identifikuje cílový objekt. Pro standardní objekty Twenty importujte konstantu z `twenty-sdk`: + + ```ts + import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define'; + + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier + // … + ``` + +* Při definování polí **inline uvnitř `defineObject()`** `objectUniversalIdentifier` **nepotřebujete** — dědí se z nadřazeného objektu. + +* `defineField()` je jediný způsob, jak přidat pole k objektům, které jste nevytvořili pomocí `defineObject()`. + +* Umístění souboru je na vás. Konvence je `src/fields/\.field.ts`, ale SDK rozpozná pole kdekoli v `src/`. + +* Chcete-li přidat kartu ke standardnímu rozvržení stránky (např. na detailní stránku Task nebo Company), použijte [`definePageLayoutTab`](/l/cs/developers/extend/apps/layout/page-layouts#definepagelayouttab) s `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS` z `twenty-sdk/define`. + +## Přidání relace k existujícímu objektu + +Chcete-li přidat relační pole (např. pro propojení vlastního objektu se standardním `Person`), použijte `defineField()` s `FieldType.RELATION`. Vzor je stejný jako u inline relací, ale s `objectUniversalIdentifier` nastaveným explicitně. Obousměrný vzor najdete v části [Relations](/l/cs/developers/extend/apps/data/relations). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx new file mode 100644 index 0000000000..498b9764f4 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/data/objects.mdx @@ -0,0 +1,104 @@ +--- +title: Objekty +description: Deklarujte nové typy záznamů – vlastní tabulky s jejich vlastními poli – pomocí defineObject. +icon: tabulka +--- + +Vlastní **objekty** jsou nové typy záznamů, které vaše aplikace přidává do pracovního prostoru — pohlednice, faktura, předplatné, cokoli specifického pro vaši doménu. Každý objekt definuje své schéma (pole, vztahy, výchozí hodnoty) a stabilní univerzální identifikátor, který přetrvá mezi synchronizacemi a nasazeními. + +```ts src/objects/post-card.object.ts +import { defineObject, FieldType } from 'twenty-sdk/define'; + +enum PostCardStatus { + DRAFT = 'DRAFT', + SENT = 'SENT', + DELIVERED = 'DELIVERED', + RETURNED = 'RETURNED', +} + +export default defineObject({ + universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05', + nameSingular: 'postCard', + namePlural: 'postCards', + labelSingular: 'Post Card', + labelPlural: 'Post Cards', + description: 'A post card object', + icon: 'IconMail', + fields: [ + { + universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b', + name: 'content', + type: FieldType.TEXT, + label: 'Content', + description: "Postcard's content", + icon: 'IconAbc', + }, + { + universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac', + name: 'recipientName', + type: FieldType.FULL_NAME, + label: 'Recipient name', + icon: 'IconUser', + }, + { + universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266', + name: 'recipientAddress', + type: FieldType.ADDRESS, + label: 'Recipient address', + icon: 'IconHome', + }, + { + universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e', + name: 'status', + type: FieldType.SELECT, + label: 'Status', + icon: 'IconSend', + defaultValue: `'${PostCardStatus.DRAFT}'`, + options: [ + { value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' }, + { value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' }, + { value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' }, + { value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' }, + ], + }, + { + universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433', + name: 'deliveredAt', + type: FieldType.DATE_TIME, + label: 'Delivered at', + icon: 'IconCheck', + isNullable: true, + defaultValue: null, + }, + ], +}); +``` + +## Hlavní body + +* Hodnota `universalIdentifier` musí být jedinečná a stabilní napříč nasazeními. +* Každé pole vyžaduje `name`, `type`, `label` a svůj vlastní stabilní `universalIdentifier`. +* Pole `fields` je volitelné — objekty můžete definovat i bez vlastních polí. +* Pole definovaná zde inline **nepotřebují** `objectUniversalIdentifier` — dědí se z nadřazeného objektu. Pomocí [`defineField()`](/l/cs/developers/extend/apps/data/extending-objects) můžete přidávat pole k objektům, které nevlastníte. +* Nové objekty můžete vygenerovat pomocí `yarn twenty dev:add object`, který vás provede pojmenováním, poli a vztahy. Viz [Architektura → Scaffolding entit](/l/cs/developers/extend/apps/getting-started/scaffolding). + + +**Základní pole jsou přidána automaticky.** Když definujete vlastní objekt, Twenty pro vás vytvoří standardní pole jako `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` a `deletedAt`. Nemusíte je uvádět v poli `fields` — pouze svá vlastní pole. Výchozí pole můžete přepsat tak, že deklarujete pole se stejným názvem, ale jen zřídka je to dobrý nápad. + + +## Výchozí hodnoty + +Výchozí textové hodnoty musí být uzavřené v jednoduchých uvozovkách **uvnitř** řetězce — `defaultValue: "'Draft'"`, ne `defaultValue: "Draft"`. Proto pole `status` výše používá `` `'${PostCardStatus.DRAFT}'` ``. + +Neuzavřené (necitované) řetězce jsou vyhrazené pro vypočítané výchozí hodnoty, které se vyhodnocují při vytvoření záznamu: + +* `'uuid'` — generuje UUID (pro pole `UUID`) +* `'now'` — aktuální časové razítko (pro pole `DATE_TIME`) + +Stejná konvence platí pro řetězcová podpola složených výchozích hodnot (např. `{ source: "'MANUAL'" }` u pole `ACTOR`) a pro hodnoty `SELECT`/`MULTI_SELECT`. Doslovná řetězcová výchozí hodnota ponechaná bez uvozovek vyvolá při sestavení aplikace varování. + +## Co dál + +* **Propojte tento objekt s ostatními** — vzor obousměrných vztahů najdete v části [Relations](/l/cs/developers/extend/apps/data/relations). +* **Přidávejte pole k objektům z jiných aplikací** — viz [Extending Objects](/l/cs/developers/extend/apps/data/extending-objects) pro `defineField()`. +* **Zobrazte tento objekt v uživatelském rozhraní** — viz [Views](/l/cs/developers/extend/apps/layout/views) a [Navigation Menu Items](/l/cs/developers/extend/apps/layout/navigation-menu-items) pro umístění do postranního panelu. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/data/overview.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/data/overview.mdx new file mode 100644 index 0000000000..dc8e6a544d --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/data/overview.mdx @@ -0,0 +1,97 @@ +--- +title: Přehled +description: Formujte data, která vaše aplikace přidává do pracovního prostoru — objekty, pole a vztahy. +icon: database +--- + +**Datová vrstva** aplikace Twenty představuje data, která vaše aplikace *přidává* do pracovního prostoru — nové typy záznamů, které deklaruje, sloupce, které přidává k existujícím objektům a způsob, jakým se tyto záznamy vzájemně propojují. + +```text +┌──────────────────────────────────────────────────┐ +│ Object — a record type, e.g. PostCard │ +│ ├─ Field (name, type, label) │ +│ ├─ Field │ +│ └─ Relation (link to another object) │ +└──────────────────────────────────────────────────┘ + │ + ├── lives in your app, OR + │ + ▼ +┌──────────────────────────────────────────────────┐ +│ Standard / other apps' objects │ +│ └─ Field added by your app via defineField │ +└──────────────────────────────────────────────────┘ +``` + +## V této části + + + + `defineObject` — deklarujte nové typy záznamů s jejich vlastními poli. + + + `defineField` — přidejte pole ke standardním objektům nebo objektům jiných aplikací. + + + Obousměrná `MANY_TO_ONE` / `ONE_TO_MANY` propojení mezi objekty. + + + +## Entity v kostce + +| Entita | Účel | Definováno pomocí | +| ---------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | +| **Objekt** | Nový vlastní typ záznamu (např. PostCard, Invoice) s vlastními poli | `defineObject()` | +| **Pole** | Sloupec v objektu. Samostatná pole mohou rozšiřovat objekty, které jste nevytvořili (např. přidat `loyaltyTier` k objektu Company) | `defineField()` | +| **Vztah** | Obousměrné propojení mezi dvěma objekty — obě strany deklarované jako pole | `defineField()` s `FieldType.RELATION` | +| **Index** | Databázový index pro zrychlení opakovaného dotazu na jeden z vašich objektů | `defineIndex()` | + +SDK je detekuje pomocí analýzy AST v době buildu, takže organizace souborů je na vás — zažitou konvencí je `src/objects/`, `src/fields/` a `src/indexes/`. Stabilní UUID `universalIdentifier` vše propojují napříč nasazeními. + +## Indexy (volitelné) + +Aplikace mohou dodávat indexy společně se svými objekty, aby udržely opakované dotazy rychlými. Nejčastějším případem je sloupec se stavem nebo cizím klíčem, který často čtete. + +```ts src/indexes/post-card-status.index.ts +import { defineIndex } from 'twenty-sdk/define'; + +import { + POST_CARD_UNIVERSAL_IDENTIFIER, + STATUS_FIELD_UNIVERSAL_IDENTIFIER, +} from '../objects/post-card.object'; + +export default defineIndex({ + universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff0', + objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + fields: [ + { + universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff1', + fieldUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + }, + ], +}); +``` + +### Jedinečné indexy + +`defineIndex` přijímá `isUnique: true` jak pro jedinečnost jednoho sloupce, tak vícesloupcovou jedinečnost. Toto je doporučené primitivum — `defineField({ isUnique: true })` je zastaralé a bude odstraněno v některém z příštích vydání. + +```ts +defineIndex({ + universalIdentifier: '…', + objectUniversalIdentifier: PERSON_UNIVERSAL_IDENTIFIER, + isUnique: true, + fields: [{ universalIdentifier: '…', fieldUniversalIdentifier: EMAIL_FIELD_UNIVERSAL_IDENTIFIER }], +}); +``` + +### Další omezení + +* Částečné klauzule `WHERE` zůstávají pod kontrolou administrátora — aplikace je nemohou deklarovat. +* Každý objekt je omezen na 10 vlastních indexů (indexy samotného frameworku se nepočítají). + +Seřaďte pole `fields` tak, jak je má Postgres používat — nejlevější sloupec jako první, jako v telefonním seznamu. Indexy nejsou zadarmo: každý zápis do tabulky je aktualizuje. Přidávejte je jen tehdy, když máte dotaz, který je potřebuje. + + +Hledáte **Application Config** nebo **Roles & Permissions**? Ty popisují samotnou aplikaci, nikoli data, která přidává — najdete je pod [Config](/l/cs/developers/extend/apps/config/overview). Hledáte **Connections** (Linear, GitHub, Slack OAuth)? Ty existují proto, aby byly volány *z* logických funkcí, a najdete je pod [Logic](/l/cs/developers/extend/apps/logic/connections). + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/data/relations.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/data/relations.mdx new file mode 100644 index 0000000000..1b90a19971 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/data/relations.mdx @@ -0,0 +1,160 @@ +--- +title: Vztahy +description: Propojte objekty obousměrnými relacemi MANY_TO_ONE / ONE_TO_MANY. +icon: diagram-project +--- + +Relace propojují dva objekty. Ve Twenty jsou relace vždy **obousměrné** — každá relace má dvě strany a každá strana je deklarována jako pole, které odkazuje na tu druhou. + +| Typ vztahu | Popis | Má cizí klíč? | +| ------------- | --------------------------------------------------------------------- | ---------------------- | +| `MANY_TO_ONE` | Mnoho záznamů tohoto objektu ukazuje na jeden záznam cílového objektu | Ano (`joinColumnName`) | +| `ONE_TO_MANY` | Jeden záznam tohoto objektu má mnoho záznamů cílového objektu | Ne (inverzní strana) | + +## Jak fungují relace + +Každá relace vyžaduje dvě pole, která na sebe vzájemně odkazují: + +1. Strana MANY_TO_ONE — je na objektu, který drží cizí klíč. +2. Strana ONE_TO_MANY — je na objektu, který vlastní kolekci. + +Obě pole používají `FieldType.RELATION` a vzájemně se odkazují prostřednictvím `relationTargetFieldMetadataUniversalIdentifier`. + +## Příklad: Pohlednice má mnoho příjemců + +`PostCard` lze odeslat mnoha záznamům `PostCardRecipient`. Každý příjemce náleží přesně jedné pohlednici. + +**Krok 1: Definujte stranu ONE_TO_MANY na PostCard** (strana "one"): + +```ts src/fields/post-card-recipients-on-post-card.field.ts +import { defineField, FieldType, RelationType } from 'twenty-sdk/define'; +import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; +import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; + +// Export so the other side can reference it +export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111'; +// Import from the other side +import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field'; + +export default defineField({ + universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'postCardRecipients', + label: 'Post Card Recipients', + icon: 'IconUsers', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); +``` + +**Krok 2: Definujte stranu MANY_TO_ONE na PostCardRecipient** (strana "many" — drží cizí klíč): + +```ts src/fields/post-card-on-post-card-recipient.field.ts +import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define'; +import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; +import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; + +// Export so the other side can reference it +export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222'; +// Import from the other side +import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field'; + +export default defineField({ + universalIdentifier: POST_CARD_FIELD_ID, + objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'postCard', + label: 'Post Card', + icon: 'IconMail', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.CASCADE, + joinColumnName: 'postCardId', + }, +}); +``` + + +**Cyklické importy:** obě relační pole odkazují na `universalIdentifier` toho druhého. Abyste předešli problémům s cyklickými importy, exportujte ID polí jako pojmenované konstanty z každého souboru a v druhém je importujte. Build systém je vyřeší v době kompilace. + + +## Vazby na standardní objekty + +Chcete-li vytvořit relaci s vestavěným objektem Twenty (Person, Company atd.), použijte `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`: + +```ts src/fields/person-on-self-hosting-user.field.ts +import { + defineField, + FieldType, + RelationType, + OnDeleteAction, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; +import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object'; + +export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333'; +export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444'; + +export default defineField({ + universalIdentifier: PERSON_FIELD_ID, + objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'person', + label: 'Person', + description: 'Person matching with the self hosting user', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'personId', + }, +}); +``` + +## Vlastnosti relačních polí + +| Vlastnost | Povinné | Popis | +| ------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------- | +| `type` | Ano | Musí být `FieldType.RELATION` | +| `relationTargetObjectMetadataUniversalIdentifier` | Ano | `universalIdentifier` cílového objektu | +| `relationTargetFieldMetadataUniversalIdentifier` | Ano | `universalIdentifier` odpovídajícího pole na cílovém objektu | +| `universalSettings.relationType` | Ano | `RelationType.MANY_TO_ONE` nebo `RelationType.ONE_TO_MANY` | +| `universalSettings.onDelete` | Pouze MANY_TO_ONE | Co se stane, když je smazán odkazovaný záznam: `CASCADE`, `SET_NULL`, `RESTRICT` nebo `NO_ACTION` | +| `universalSettings.joinColumnName` | Pouze MANY_TO_ONE | Název databázového sloupce pro cizí klíč (např. `postCardId`) | + +## Vložená relační pole + +Relaci můžete také deklarovat přímo uvnitř [`defineObject`](/l/cs/developers/extend/apps/data/objects). Pokud je pole vložené, vynechejte `objectUniversalIdentifier` — dědí se z nadřazeného objektu: + +```ts +export default defineObject({ + universalIdentifier: '...', + nameSingular: 'postCardRecipient', + // ... + fields: [ + { + universalIdentifier: POST_CARD_FIELD_ID, + type: FieldType.RELATION, + name: 'postCard', + label: 'Post Card', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.CASCADE, + joinColumnName: 'postCardId', + }, + }, + // … other fields + ], +}); +``` diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/concepts.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/concepts.mdx new file mode 100644 index 0000000000..cf0f5808e1 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/concepts.mdx @@ -0,0 +1,101 @@ +--- +title: Pojmy +description: Jak aplikace Twenty fungují — model entit, sandboxing a životní cyklus instalace. +icon: sitemap +--- + +Aplikace Twenty jsou balíčky TypeScriptu, které rozšiřují váš pracovní prostor o vlastní objekty, logiku, komponenty UI a funkce AI. Běží na platformě Twenty s plnou izolací (sandboxingem) a řízením oprávnění. + +## Jak aplikace fungují + +Aplikace je kolekce **entit** deklarovaných pomocí funkcí `defineEntity()` z balíčku `twenty-sdk`. SDK tyto deklarace detekuje pomocí analýzy AST při sestavení a vytváří **manifest** — úplný popis toho, co vaše aplikace přidává do pracovního prostoru. Tyto funkce validují vaši konfiguraci v době sestavení a poskytují automatické doplňování v IDE a typovou bezpečnost. + +``` +your-app/ +├── src/ +│ ├── application-config.ts ← defineApplication (required, one per app) +│ ├── roles/ ← defineRole +│ ├── objects/ ← defineObject +│ ├── fields/ ← defineField +│ ├── logic-functions/ ← defineLogicFunction +│ ├── front-components/ ← defineFrontComponent +│ ├── skills/ ← defineSkill +│ ├── agents/ ← defineAgent +│ ├── views/ ← defineView +│ ├── navigation-menu-items/ ← defineNavigationMenuItem +│ └── page-layouts/ ← definePageLayout +├── public/ ← Static assets (images, icons) +└── package.json +``` + + + **Uspořádání souborů je na vás.** Detekce entit je založená na AST — SDK najde volání `export default defineEntity(...)` bez ohledu na to, kde se soubor nachází. Výše uvedená struktura složek je konvence, nikoli požadavek. + + +## Typy entit + +| Entita | Účel | Dokumentace | +| ----------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| **Aplikace** | Identita aplikace, výchozí role, proměnné | [Application Config](/l/cs/developers/extend/apps/config/application) | +| **Role** | Sady oprávnění pro objekty a pole | [Roles & Permissions](/l/cs/developers/extend/apps/config/roles) | +| **Objekt** | Vlastní typy záznamů s poli | [Objects](/l/cs/developers/extend/apps/data/objects) | +| **Pole** | Přidání polí k objektům z jiných aplikací | [Extending Objects](/l/cs/developers/extend/apps/data/extending-objects) | +| **Vztah** | Obousměrná propojení mezi objekty | [Relations](/l/cs/developers/extend/apps/data/relations) | +| **Logická funkce** | TypeScript na straně serveru se spouštěči | [Logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) | +| **Dovednost** | Znovupoužitelné pokyny pro AI agenty | [Dovednosti a agenti](/l/cs/developers/extend/apps/logic/skills-and-agents) | +| **Agent** | AI asistenti s vlastními prompty | [Dovednosti a agenti](/l/cs/developers/extend/apps/logic/skills-and-agents) | +| **Poskytovatel připojení** | Přihlašovací údaje OAuth pro externí rozhraní API třetích stran | [Connections](/l/cs/developers/extend/apps/logic/connections) | +| **Zobrazení** | Předkonfigurovaná zobrazení seznamu záznamů | [Views](/l/cs/developers/extend/apps/layout/views) | +| **Položka navigační nabídky** | Vlastní položky postranního panelu | [Navigation Menu Items](/l/cs/developers/extend/apps/layout/navigation-menu-items) | +| **Rozvržení stránky** | Karty a widgety na stránce s podrobnostmi záznamu | [Page Layouts](/l/cs/developers/extend/apps/layout/page-layouts) | +| **Frontendová komponenta** | Izolované React UI uvnitř Twenty | [Frontendové komponenty](/l/cs/developers/extend/apps/layout/front-components) | +| **Položka příkazové nabídky** | Rychlé akce a položky Cmd+K | [Command Menu Items](/l/cs/developers/extend/apps/layout/command-menu-items) | + +## Izolace (sandboxing) + +* **Logické funkce** běží v izolovaných procesech Node.js na serveru. K datům přistupují pouze prostřednictvím typovaného klienta API, a to v rozsahu oprávnění role aplikace. +* **Frontendové komponenty** běží ve Web Workerech s využitím Remote DOM — jsou oddělené od hlavní stránky, ale vykreslují nativní prvky DOM (nikoli iframy). Komunikují s Twenty prostřednictvím hostitelského API pro předávání zpráv. +* **Oprávnění** jsou vynucována na úrovni API. Běhový token (`TWENTY_APP_ACCESS_TOKEN`) je odvozen z role definované v `defineApplication()`. + +## Životní cyklus aplikace + +``` +┌─────────────────────────────────────────────────────────┐ +│ Development │ +│ npx create-twenty-app → yarn twenty dev (live sync) │ +├─────────────────────────────────────────────────────────┤ +│ Build & Deploy │ +│ yarn twenty dev:build → yarn twenty app:publish │ +├─────────────────────────────────────────────────────────┤ +│ Install flow │ +│ upload → [pre-install] → metadata migration → │ +│ generate SDK → [post-install] │ +├─────────────────────────────────────────────────────────┤ +│ Publish │ +│ npm publish → appears in Twenty marketplace │ +└─────────────────────────────────────────────────────────┘ +``` + +* **`yarn twenty dev`** — sleduje vaše zdrojové soubory a průběžně synchronizuje změny s připojeným serverem Twenty. Typovaný klient API se při změně schématu automaticky znovu vygeneruje. +* **`yarn twenty dev:build`** — zkompiluje TypeScript, zabalí logické funkce a frontendové komponenty pomocí esbuild a vytvoří manifest. +* **Pre/post-install hooks** — volitelné funkce, které běží během instalace. Podrobnosti viz [Install Hooks](/l/cs/developers/extend/apps/config/install-hooks). + +## Další kroky + + + + Identita aplikace, výchozí role a instalační hooky. + + + Objekty, pole a obousměrné relace. + + + Logické funkce, dovednosti, agenti a připojení přes OAuth. + + + Zobrazení, navigace, rozvržení stránek, frontendové komponenty. + + + CLI, testování, vzdálené repozitáře, CI a publikování vaší aplikace. + + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/local-server.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/local-server.mdx new file mode 100644 index 0000000000..765f1bb379 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/local-server.mdx @@ -0,0 +1,87 @@ +--- +title: Lokální server +description: Spravujte lokální Docker server Twenty – spouštění, zastavení, upgrade, paralelní testovací instance a ruční nastavení SDK. +icon: server +--- + +## Správa lokálního serveru + +K ovládání lokálního kontejneru Twenty použijte `yarn twenty docker:*`: + +| Příkaz | K čemu slouží | +| -------------------------------------- | ---------------------------------------------- | +| `yarn twenty docker:start` | Spustí server (v případě potřeby stáhne image) | +| `yarn twenty docker:start 2.2.0` | Spustit konkrétní verzi serveru | +| `yarn twenty docker:start --port 3030` | Spustí na vlastním portu | +| `yarn twenty docker:stop` | Zastaví server (zachová data) | +| `yarn twenty docker:status` | Zobrazí URL, verzi a přihlašovací údaje | +| `yarn twenty docker:logs` | Streamuje protokoly serveru | +| `yarn twenty docker:reset` | Vymaže data a začne znovu | +| `yarn twenty docker:upgrade` | Stáhne nejnovější image `twenty-app-dev` | +| `yarn twenty docker:upgrade 2.2.0` | Aktualizuje na konkrétní verzi | + +Data přetrvávají při restartech ve dvou svazcích Dockeru (`twenty-app-dev-data` pro PostgreSQL, `twenty-app-dev-storage` pro soubory). Pomocí `reset` vymažte vše. + +## Fixace verze serveru + +Když není předána žádná verze, `docker:start` určí verzi z rozsahu `engines.twenty` vaší aplikace v `package.json` — stejného rozsahu, vůči kterému server ověřuje, když je vaše aplikace nainstalována. Spustí nejnovější publikovaný image `twenty-app-dev`, který splňuje tento rozsah, a pokud pole chybí nebo žádná publikovaná verze neodpovídá, použije `latest`: + +```json filename="package.json" +{ + "engines": { + "twenty": ">=2.2.0" + } +} +``` + +Předáním verze můžete rozsah pro jedno spuštění přepsat: `yarn twenty docker:start 2.3.0`. Pokud už kontejner existuje v jiné verzi, `docker:start` jej na místě aktualizuje (znovu vytvoří kontejner při zachování vašich datových svazků). + +## Aktualizace obrazu serveru + +`yarn twenty docker:upgrade` stáhne nejnovější image, porovná digesty a znovu vytvoří kontejner pouze v případě, že se skutečně něco změnilo. Svazky zůstanou zachovány — nahradí se pouze kontejner. Pokud byl stažen nový image a kontejner běžel, upgrade automaticky spustí nový kontejner; poté spusťte `yarn twenty docker:start`, abyste počkali, než bude ve stavu 'healthy'. + +```bash filename="Terminal" +yarn twenty docker:upgrade # Latest +yarn twenty docker:upgrade 2.2.0 # Specific version +``` + +Běžící verzi ověříte pomocí `yarn twenty docker:status` (zobrazí `APP_VERSION` zabudovanou v kontejneru). + +## Spuštění paralelní testovací instance + +Předejte `--test` libovolnému příkazu `docker:*` pro správu druhé, plně izolované instance — užitečné pro integrační testy nebo experimentování bez zásahu do vašich hlavních vývojových dat: + +| Příkaz | K čemu slouží | +| ----------------------------------- | ------------------------------------------------ | +| `yarn twenty docker:start --test` | Spustí testovací instanci (výchozí port je 2021) | +| `yarn twenty docker:stop --test` | Zastaví ji | +| `yarn twenty docker:status --test` | Zobrazí její stav | +| `yarn twenty docker:logs --test` | Streamuje její protokoly | +| `yarn twenty docker:reset --test` | Vymaže její data | +| `yarn twenty docker:upgrade --test` | Aktualizuje její image | + +Testovací instance má vlastní kontejner (`twenty-app-dev-test`), svazky (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) a konfiguraci — běží souběžně s vaší hlavní instancí bez konfliktů. Zkombinujte `--test` s `--port` pro změnu výchozího portu 2021. + +## Ruční nastavení (bez generátoru kostry) + +Pokud přidáváte SDK do existujícího projektu, generátor kostry přeskočte: + +```bash filename="Terminal" +yarn add twenty-sdk twenty-client-sdk +``` + +Přidejte skript do `package.json`: + +```json filename="package.json" +{ + "scripts": { + "twenty": "twenty" + } +} +``` + +Nyní můžete spouštět `yarn twenty dev`, `yarn twenty docker:start` a další. + + +Neinstalujte `twenty-sdk` globálně — nainstalujte jej v každém projektu zvlášť, aby každá aplikace používala svou vlastní verzi. + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx new file mode 100644 index 0000000000..6568a7fc96 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/project-structure.mdx @@ -0,0 +1,61 @@ +--- +title: Struktura projektu +description: Co obsahuje vygenerovaná aplikace Twenty — soubory, složky a k čemu každý z nich slouží. +icon: folder-tree +--- + +Nová aplikace vygenerovaná pomocí `npx create-twenty-app` vypadá takto: + +```text filename="my-twenty-app/" +my-twenty-app/ + package.json + src/ + application-config.ts # Required — your app's entry point + default-role.ts # Permissions for logic functions + constants/ + universal-identifiers.ts # Auto-generated UUIDs and metadata + __tests__/ + setup-test.ts + app-install.integration-test.ts + .github/workflows/ci.yml # GitHub Actions + public/ # Static assets + vitest.config.ts # Test runner config + tsconfig.json, tsconfig.spec.json + .nvmrc, .yarnrc.yml, .oxlintrc.json + README.md, LLMS.md +``` + +## Klíčové soubory + +| Soubor / Složka | Účel | +| ---------------------------------------- | ----------------------------------------------------------------------- | +| `src/application-config.ts` | **Povinné.** Hlavní konfigurační soubor vaší aplikace. | +| `src/default-role.ts` | Výchozí role, která řídí, k čemu mohou vaše logické funkce přistupovat. | +| `src/constants/universal-identifiers.ts` | Automaticky generovaná UUID a metadata (zobrazovaný název, popis). | +| `src/__tests__/` | Integrační testy (nastavení + ukázkový test). | +| `public/` | Statické soubory (obrázky, písma) doručované vaší aplikací. | + + +**Uspořádání souborů je na vás.** Výše uvedené složky jsou konvence — SDK detekuje entity pomocí analýzy AST u volání `export default defineEntity(...)` bez ohledu na to, kde se soubor nachází. + + +## Závislosti + +Oba balíčky Twenty SDK patří pod `devDependencies`, ne pod `dependencies`: + +```json filename="package.json" +{ + "dependencies": {}, + "devDependencies": { + "twenty-client-sdk": "^2.13.0", + "twenty-sdk": "^2.13.0" + } +} +``` + +* **`twenty-sdk`** dodává `twenty` CLI a nástroje pro sestavení/scaffolding. Běží pouze při vývoji a sestavování a nikdy není importován za běhu zveřejněné aplikace. +* **`twenty-client-sdk`** je importován kódem vaší aplikace (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), ale Twenty ho poskytuje za běhu — logické funkce ho získávají z vygenerované SDK vrstvy a front-endové komponenty ho načítají z modulů poskytovaných serverem. Vaše nainstalovaná kopie se používá pouze pro kontrolu typů a build v době nasazení, takže ji nikdy není potřeba přibalit do nasazeného balíčku. + +Ponechání kteréhokoli balíčku pod `dependencies` ho vtáhne do runtime balíčku nainstalované aplikace, kde je jen mrtvou vahou. `twenty build` vypíše varování, pokud je kterýkoli z nich stále uveden pod `dependencies`. + +Vlastní runtime závislosti vaší aplikace (knihovny, které vaše logické funkce skutečně importují za běhu) přidejte jako obvykle pod `dependencies`. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx new file mode 100644 index 0000000000..ebb6f01dd3 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/quick-start.mdx @@ -0,0 +1,176 @@ +--- +title: Rychlý start +icon: rocket +description: Vytvořte svou první aplikaci Twenty během několika minut. +--- + +## Předpoklady + +* **Node.js 24+** — [Stáhnout](https://nodejs.org/) +* **Yarn 4** — je součástí Node.js prostřednictvím Corepacku. Povolte jej: `corepack enable` +* **Docker** — [Stáhnout](https://www.docker.com/products/docker-desktop/). Nutné pro spuštění lokálního serveru Twenty. Přeskočte, pokud už máte Twenty spuštěné jinde. + +Vytváření aplikace Twenty má tři fáze. Generátor kostry je spojuje do jediného příkazu pro ideální scénář, ale každá fáze je samostatný koncept — když se něco nepovede, znalost aktuální fáze napoví, co opravit. + +| Fáze | Co děláte | Nástroj | Výsledek | +| ---------------------- | -------------------------------------------------------- | ----------------------------- | -------------------------------------------- | +| **1. Vytvořit kostru** | Vygenerovat zdrojový kód aplikace | `npx create-twenty-app` | Projekt v TypeScriptu na disku | +| **2. Spustit server** | Spustit server Twenty, do kterého se bude synchronizovat | Docker + `yarn twenty server` | Běžící instance Twenty | +| **3. Synchronizovat** | Živě synchronizovat kód na server | `yarn twenty dev` | Vaše změny se objeví v uživatelském rozhraní | + +--- + +## Fáze 1 — Vytvořte kostru projektu + +Vytvořte novou aplikaci ze šablony: + +```bash filename="Terminal" +npx create-twenty-app@latest my-twenty-app +``` + +Budete vyzváni k zadání názvu a popisu — výchozí hodnoty potvrdíte klávesou **Enter**. Tím se v `my-twenty-app/` vytvoří projekt v TypeScriptu se startovacím `application-config.ts`, výchozí rolí, CI workflow a integračním testem. + +**Po této fázi:** máte na svém počítači zdrojový kód aplikace. Zatím neběží — to je předmětem Fáze 2. + +--- + +## Fáze 2 — Spusťte lokální server Twenty + +Vaše aplikace potřebuje server Twenty, do kterého se bude synchronizovat. Server je plnohodnotná instance Twenty — UI, GraphQL API, PostgreSQL — běžící lokálně v Dockeru. Váš lokální kód nahrává své definice na tento server, díky čemuž se objeví v UI. + +Generátor kostry nabídne, že vám jej spustí: + +> **Chcete nastavit lokální instanci Twenty?** + +* **Ano (doporučeno)** — stáhne Docker image `twentycrm/twenty-app-dev` a spustí jej na portu `2020`. Nejprve se ujistěte, že Docker běží. +* **Ne** — zvolte, pokud už máte server Twenty, ke kterému se chcete připojit. Můžete jej propojit později pomocí `yarn twenty remote:add`. + +
+ Spustit lokální instanci? +
+ +Jakmile server běží, otevře se prohlížeč pro přihlášení. Použijte předpřipravený demo účet: + +* **E-mail:** `tim@apple.dev` +* **Heslo:** `tim@apple.dev` + +
+ Přihlašovací obrazovka Twenty +
+ +Na další obrazovce klikněte na **Authorize** — tím udělíte nástroji CLI přístup k vašemu pracovnímu prostoru. + +
+ Autorizační obrazovka Twenty CLI +
+ +Váš terminál potvrdí, že je vše nastaveno. + +
+ Aplikace byla úspěšně vygenerována +
+ +**Po této fázi:** máte spuštěný server Twenty na [http://localhost:2020](http://localhost:2020) a vaše CLI má oprávnění se k němu synchronizovat. + + +Pokud Docker není nainstalovaný nebo neběží, generátor kostry vám sdělí správný příkaz pro spuštění ve vašem operačním systému. Jakmile Docker poběží, můžete pokračovat pomocí `yarn twenty docker:start` — není potřeba znovu vytvářet kostru. + + +--- + +## Fáze 3 — Synchronizujte své změny + +Toto je vnitřní smyčka, ve které strávíte většinu času. + +```bash filename="Terminal" +cd my-twenty-app +yarn twenty dev +``` + +Tento proces sleduje `src/`, při každé změně znovu sestaví a synchronizuje výsledek na server. Upravte soubor, uložte a během několika vteřin se změna projeví na serveru. V terminálu uvidíte panel se stavem v reálném čase. + +Pro podrobnější výstup (protokoly sestavení, požadavky na synchronizaci, stopy chyb) přidejte `--verbose`. + +
+ Výstup terminálu ve vývojovém režimu +
+ +Otevřete [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer). Vaše aplikace by měla být uvedena v části **Your Apps**. + +
+ Seznam Your Apps se zobrazenou aplikací My twenty app +
+ +Klikněte na **My twenty app** a zobrazí se jeho **registrace aplikace** — záznam na úrovni serveru, který popisuje vaši aplikaci (název, identifikátor, přihlašovací údaje OAuth, zdroj). Jedna registrace může být nainstalována ve více pracovních prostorech na stejném serveru. + +
+ Podrobnosti registrace aplikace +
+ +Klikněte na **View installed app**, abyste zobrazili instalaci v pracovním prostoru. Karta **About** zobrazuje verzi a možnosti správy. + +
+ Nainstalovaná aplikace +
+ +**Po této fázi:** máte průběžný vývojový cyklus. Upravte libovolný soubor v `src/` a projeví se to v UI. + +### Jednorázová synchronizace pro CI a skripty + +Předejte `--once` pro provedení jednoho sestavení + synchronizace a ukončení — stejný postup, bez sledování změn: + +```bash filename="Terminal" +yarn twenty dev --once +``` + +| Příkaz | Chování | Kdy použít | +| ---------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| `yarn twenty dev` | Sleduje změny a při každé změně znovu synchronizuje. Běží, dokud jej nezastavíte. | Interaktivní lokální vývoj. | +| `yarn twenty dev --once` | Jedno sestavení + synchronizace, ukončí se s kódem `0` při úspěchu, `1` při chybě. | CI, pre-commit hooky, AI agenti, skriptované pracovní postupy. | +| `yarn twenty dev --once --dry-run` | Sestaví a vypíše změny metadat **bez jejich použití**. | Kontrola toho, co by synchronizace změnila, ještě před jejím potvrzením. | + +Oba režimy vyžadují autentizovaný vzdálený server. Více informací o `--dry-run` najdete v části [Synchronizace a obnovení](/l/cs/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run). + +### Možnosti vývojového režimu + +| Přepínač | Popis | +| ------------------------------------- | --------------------------------------------------------------------------------------------- | +| `--once` | Jednou sestavit a synchronizovat, poté ukončit. | +| `--dry-run` | Pomocí `--once` zobrazíte náhled změn metadat, aniž byste je použili. Nic nezapisuje. | +| `--debounceMs \` | Nastaví prodlevu pro potlačení zákmitů při změnách souborů v milisekundách (výchozí: `2000`). | +| `--verbose` / `--debug` | Zobrazí podrobné protokoly sestavení, požadavky synchronizace a trasování chyb. | + +## Co můžete vytvořit + +Aplikace se skládají z **entit** — každá je definována jako soubor TypeScriptu s jediným `export default`: + +| Entita | K čemu slouží | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| **Objekty a pole** | Vlastní datové modely (pohlednice, faktura apod.) s typovanými poli | +| **Logické funkce** | Serverový TypeScript spouštěný HTTP trasami, plánovačem cron nebo událostmi databáze | +| **Frontendové komponenty** | Komponenty Reactu, které se vykreslují v uživatelském rozhraní Twenty (postranní panel, widgety, příkazová nabídka) | +| **Dovednosti a agenti** | Schopnosti AI — opakovaně použitelné pokyny a autonomní asistenti | +| **Pohledy a navigace** | Předkonfigurované seznamové pohledy a položky postranní nabídky | +| **Rozvržení stránek** | Vlastní stránky detailu záznamu s kartami a widgety | + +Úplná reference: [Koncepty](/l/cs/developers/extend/apps/getting-started/concepts). + +## Další kroky + + + + Identita aplikace, výchozí role, instalační hooky, veřejná aktiva. + + + Objekty, pole a obousměrné relace. + + + Logické funkce, dovednosti, agenti a připojení přes OAuth. + + + Zobrazení, navigace, rozvržení stránek, frontendové komponenty. + + + CLI, testování, vzdálené repozitáře, CI a publikování vaší aplikace. + + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx new file mode 100644 index 0000000000..204631717d --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/scaffolding.mdx @@ -0,0 +1,58 @@ +--- +title: Vytvoření kostry +description: Interaktivně generujte soubory entit pomocí yarn twenty dev:add – objekty, pole, zobrazení, logické funkce a další. +icon: wand-magic-sparkles +--- + +Místo ručního vytváření souborů entit použijte interaktivní generátor: + +```bash filename="Terminal" +yarn twenty dev:add +``` + +Vyžádá si, abyste vybrali typ entity, provede vás požadovanými poli a poté zapíše připravený soubor s pevně daným `universalIdentifier` a správným voláním `defineEntity()`. + +Můžete také předat typ entity přímo a přeskočit první dotaz: + +```bash filename="Terminal" +yarn twenty dev:add object +yarn twenty dev:add logicFunction +yarn twenty dev:add frontComponent +``` + +## Dostupné typy entit + +| Typ entity | Příkaz | Vygenerovaný soubor | +| ------------------------- | ---------------------------------------- | ------------------------------------------------------- | +| Objekt | `yarn twenty dev:add object` | `src/objects/\.ts` | +| Pole | `yarn twenty dev:add field` | `src/fields/\.ts` | +| Logická funkce | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | +| Frontendová komponenta | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | +| Role | `yarn twenty dev:add role` | `src/roles/\.ts` | +| Dovednost | `yarn twenty dev:add skill` | `src/skills/\.ts` | +| Agent | `yarn twenty dev:add agent` | `src/agents/\.ts` | +| Zobrazení | `yarn twenty dev:add view` | `src/views/\.ts` | +| Položka navigační nabídky | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | +| Rozvržení stránky | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | + +## Co generátor vytváří + +Každý typ entity má vlastní šablonu. Například `yarn twenty dev:add object` se zeptá na: + +1. **Název (jednotné číslo)** — např. `invoice` +2. **Název (množné číslo)** — např. `invoices` +3. **Štítek (jednotné číslo)** — automaticky doplněn z názvu (např. `Invoice`) +4. **Štítek (množné číslo)** — automaticky doplněn (např. `Invoices`) +5. **Vytvořit zobrazení a položku navigace?** — pokud odpovíte ano, generátor také vytvoří odpovídající zobrazení a odkaz v postranním panelu pro nový objekt. + +Ostatní typy entit mají jednodušší dotazy — většinou se ptají pouze na název. + +Typ entity `field` je podrobnější: ptá se na název pole, štítek, typ (ze seznamu všech dostupných typů polí jako `TEXT`, `NUMBER`, `SELECT`, `RELATION` atd.) a `universalIdentifier` cílového objektu. + +## Vlastní výstupní cesta + +Pomocí příznaku `--path` umístíte vygenerovaný soubor do vlastního umístění: + +```bash filename="Terminal" +yarn twenty dev:add logicFunction --path src/custom-folder +``` diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx new file mode 100644 index 0000000000..4112924b19 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/getting-started/troubleshooting.mdx @@ -0,0 +1,14 @@ +--- +title: Řešení potíží +description: Časté problémy při prvním spuštění — Docker, verze Node, Yarn, závislosti. +icon: klíč +--- + +* **Chyby Dockeru** — Před spuštěním `yarn twenty docker:start` se ujistěte, že Docker Desktop (nebo démon) běží. Chybová zpráva ukáže správný příkaz pro spuštění pro váš operační systém. +* **Nesprávná verze Node** — Je potřeba 24+. Ověřte pomocí `node -v`. +* **Chybí Yarn 4** — Spusťte `corepack enable`. +* **Rozbité závislosti** — `rm -rf node_modules && yarn install`. +* **Chyby `twenty-sdk` po upgradu na v2.8.0** — V 2.8.0 byl přesunut z `dependencies` do `devDependencies`. Viz [Struktura projektu → Závislosti](/l/cs/developers/extend/apps/getting-started/project-structure#dependencies). +* **`twenty build` upozorňuje na `twenty-client-sdk` v sekci `dependencies`** — je poskytován za běhu Twenty, takže by měl být přesunut do `devDependencies` vedle `twenty-sdk`. Viz [Struktura projektu → Závislosti](/l/cs/developers/extend/apps/getting-started/project-structure#dependencies). + +Zasekli jste se? Zeptejte se na [Discordu Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx new file mode 100644 index 0000000000..feb8ad48cd --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/command-menu-items.mdx @@ -0,0 +1,148 @@ +--- +title: Položky příkazové nabídky +description: Zpřístupněte front komponenty jako rychlé akce a položky příkazového menu (Cmd+K) pomocí defineCommandMenuItem. +icon: terminal +--- + +**Položka příkazového menu** je most mezi uživatelem a [front komponentou](/l/cs/developers/extend/apps/layout/front-components). Registruje komponentu v příkazovém menu Twenty (Cmd+K) a volitelně také jako připnuté tlačítko rychlé akce v pravém horním rohu stránky. + +```ts src/command-menu-items/open-dashboard.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + label: 'Open Dashboard', + shortLabel: 'Dashboard', + icon: 'IconLayoutDashboard', + isPinned: true, + availabilityType: 'GLOBAL', + frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', +}); +``` + +## Konfigurační pole + +| Pole | Povinné | Popis | +| --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `universalIdentifier` | Ano | Stabilní jedinečné ID pro příkaz | +| `label` | Ano | Plný popisek zobrazený v příkazovém menu (Cmd+K) | +| `frontComponentUniversalIdentifier` | Ano | `universalIdentifier` frontendové komponenty, kterou tento příkaz otevírá | +| `shortLabel` | Ne | Kratší popisek zobrazený na připnutém tlačítku rychlé akce | +| `icon` | Ne | Název ikony zobrazený vedle popisku (např. `'IconBolt'`, `'IconSend'`) | +| `isPinned` | Ne | Pokud je `true`, zobrazí příkaz jako tlačítko rychlé akce v pravém horním rohu stránky | +| `availabilityType` | Ne | Určuje, kde se příkaz zobrazuje: `'GLOBAL'` (vždy dostupné), `'RECORD_SELECTION'` (pouze když jsou vybrány záznamy) nebo `'FALLBACK'` (zobrazeno, když neodpovídají žádné jiné příkazy) | +| `availabilityObjectUniversalIdentifier` | Ne | Omezí příkaz na stránky konkrétního typu objektu (např. pouze u záznamů Company) | +| `conditionalAvailabilityExpression` | Ne | Logický výraz, který dynamicky řídí viditelnost (viz níže) | + +## Příkazy bez rozhraní + +Položka příkazového menu spárovaná s [front komponentou bez rozhraní](/l/cs/developers/extend/apps/layout/front-components#headless-vs-non-headless) je idiomatický způsob, jak dodat akci na jedno kliknutí — spustit kód, přejít na stránku nebo potvrdit a provést. Stránka Front Components popisuje [SDK Command komponenty](/l/cs/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`), které obsluhují pattern akce-a-unmount. + +Typický průběh: + +```tsx src/front-components/run-action.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const RunAction = () => { + const execute = async () => { + const client = new CoreApiClient(); + await client.mutation({ + createTask: { + __args: { data: { title: 'Created by my app' } }, + id: true, + }, + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', + name: 'run-action', + description: 'Creates a task from the command menu', + component: RunAction, + isHeadless: true, +}); +``` + +```ts src/command-menu-items/run-action.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', + label: 'Run my action', + icon: 'IconPlayerPlay', + frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', +}); +``` + +## Výrazy podmíněné dostupnosti + +Pole `conditionalAvailabilityExpression` vám umožní řídit viditelnost příkazu na základě aktuálního kontextu stránky. Pro sestavení výrazů importujte typované proměnné a operátory z `twenty-sdk`: + +```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, + ), +}); +``` + + + `RECORD_SELECTION` již znamená neprázdný výběr — použijte `numberOfSelectedRecords` pouze pro konkrétní počty (např. `>= 2`). + + +### Kontextové proměnné + +Tyto proměnné reprezentují aktuální stav stránky: + +| Proměnná | Typ | Popis | +| ------------------------------ | --------- | -------------------------------------------------------------------- | +| `pageType` | `string` | Aktuální typ stránky (např. `'RecordIndexPage'`, `'RecordShowPage'`) | +| `isInSidePanel` | `boolean` | Zda je komponenta vykreslena v postranním panelu | +| `numberOfSelectedRecords` | `number` | Počet aktuálně vybraných záznamů | +| `isSelectAll` | `boolean` | Zda je aktivní "vybrat vše" | +| `selectedRecords` | `array` | Vybrané objekty záznamů | +| `favoriteRecordIds` | `array` | ID oblíbených záznamů | +| `objectPermissions` | `object` | Oprávnění pro aktuální typ objektu | +| `targetObjectReadPermissions` | `object` | Oprávnění ke čtení pro cílový objekt | +| `targetObjectWritePermissions` | `object` | Oprávnění k zápisu pro cílový objekt | +| `featureFlags` | `object` | Aktivní příznaky funkcí | +| `objectMetadataItem` | `object` | Metadata aktuálního typu objektu | +| `hasAnySoftDeleteFilterOnView` | `boolean` | Zda má aktuální zobrazení filtr soft-delete | + +### Operátory + +Kombinujte proměnné do logických výrazů: + +| Operátor | Popis | +| ----------------------------------- | ------------------------------------------------------------------------------ | +| `isDefined(value)` | `true`, pokud hodnota není null/undefined | +| `isNonEmptyString(value)` | `true`, pokud je hodnota neprázdným řetězcem | +| `includes(array, value)` | `true`, pokud pole obsahuje danou hodnotu | +| `includesEvery(array, prop, value)` | `true`, pokud vlastnost každé položky zahrnuje danou hodnotu | +| `every(array, prop)` | `true`, pokud je vlastnost u každé položky pravdivá (truthy) | +| `everyDefined(array, prop)` | `true`, pokud je vlastnost definována u každé položky | +| `everyEquals(array, prop, value)` | `true`, pokud se vlastnost rovná hodnotě u každé položky | +| `some(array, prop)` | `true`, pokud je vlastnost pravdivá (truthy) alespoň u jedné položky | +| `someDefined(array, prop)` | `true`, pokud je vlastnost definována alespoň u jedné položky | +| `someEquals(array, prop, value)` | `true`, pokud se vlastnost rovná hodnotě alespoň u jedné položky | +| `someNonEmptyString(array, prop)` | `true`, pokud má vlastnost alespoň u jedné položky hodnotu neprázdného řetězce | +| `none(array, prop)` | `true`, pokud je vlastnost u všech položek nepravdivá (falsy) | +| `noneDefined(array, prop)` | `true`, pokud je vlastnost u všech položek nedefinovaná | +| `noneEquals(array, prop, value)` | `true`, pokud se vlastnost nerovná hodnotě u žádné položky | diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/front-components.mdx new file mode 100644 index 0000000000..ded574d80d --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/front-components.mdx @@ -0,0 +1,545 @@ +--- +title: Frontendové komponenty +description: Vytvářejte komponenty Reactu, které se vykreslují uvnitř uživatelského rozhraní Twenty se sandboxovou izolací. +icon: window-maximize +--- + +Frontendové komponenty jsou React komponenty, které se vykreslují přímo v uživatelském rozhraní Twenty. Běží v **izolovaném Web Workeru** s využitím Remote DOM — váš kód je sandboxovaný, ale vykresluje se nativně na stránce, nikoli v iframu. + +## Kde lze použít frontendové komponenty + +Frontendové komponenty se mohou vykreslovat na dvou místech v rámci Twenty: + +* **Postranní panel** — Frontendové komponenty, které nejsou headless, se otevírají v pravém postranním panelu. Toto je výchozí chování, když je frontendová komponenta vyvolána z příkazového menu. +* **Widgety (nástěnky a stránky záznamů)** — front komponenty lze vkládat jako widgety do [rozložení stránky](/l/cs/developers/extend/apps/layout/page-layouts). Při konfiguraci nástěnky nebo rozložení stránky záznamu mohou uživatelé přidat widget frontendové komponenty. + +Samotná frontendová komponenta není z uživatelského rozhraní dostupná — je potřeba ji zpřístupnit. Dva způsoby, jak to udělat, jsou: + +* **Spárujte ji s [položkou příkazové nabídky](/l/cs/developers/extend/apps/layout/command-menu-items)** — zaregistruje ji v příkazové nabídce (Cmd+K) a volitelně také jako připnutou rychlou akci. +* **Vložte ji jako widget do [rozložení stránky](/l/cs/developers/extend/apps/layout/page-layouts)** — umístí ji na detailní stránku záznamu nebo na nástěnku. + +## Základní příklad + +Nejrychlejší způsob, jak vidět front komponentu v akci, je spárovat ji s [`defineCommandMenuItem`](/l/cs/developers/extend/apps/layout/command-menu-items), aby se objevila jako tlačítko rychlé akce v pravém horním rohu stránky: + +```tsx src/front-components/hello-world.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; + +const HelloWorld = () => { + return ( +
+

Hello from my app!

+

This component renders inside Twenty.

+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', + name: 'hello-world', + description: 'A simple front component', + component: HelloWorld, +}); +``` + +```ts src/command-menu-items/hello-world.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345', + shortLabel: 'Hello', + label: 'Hello World', + icon: 'IconBolt', + isPinned: true, + availabilityType: 'GLOBAL', + frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', +}); +``` + +Po synchronizaci pomocí `yarn twenty dev` (nebo po jednorázovém spuštění `yarn twenty dev --once`) se rychlá akce zobrazí v pravém horním rohu stránky: + +
+ Tlačítko rychlé akce v pravém horním rohu +
+ +Kliknutím na něj vykreslíte komponentu přímo ve stránce. + +## Konfigurační pole + +| Pole | Povinné | Popis | +| --------------------- | ------- | ----------------------------------------------------------------- | +| `universalIdentifier` | Ano | Stabilní jedinečné ID pro tuto komponentu | +| `component` | Ano | Funkce komponenty React | +| `name` | Ne | Zobrazovaný název | +| `description` | Ne | Popis toho, co komponenta dělá | +| `isHeadless` | Ne | Nastavte na `true`, pokud komponenta nemá viditelné UI (viz níže) | + +## Umístění frontendové komponenty na stránku + +Mimo příkazy můžete frontendovou komponentu vložit přímo na stránku záznamu přidáním jako widget v **rozvržení stránky**. Podrobnosti viz [Rozložení stránek](/l/cs/developers/extend/apps/layout/page-layouts). + +## Headless vs. ne-headless + +Front-endové komponenty existují ve dvou režimech vykreslování řízených volbou `isHeadless`: + +**Ne-headless (výchozí)** — Komponenta vykreslí viditelné uživatelské rozhraní. Po vyvolání z menu příkazů se otevře v postranním panelu. Toto je výchozí chování, když je `isHeadless` `false` nebo když tato volba není uvedena. + +**Headless (`isHeadless: true`)** — Komponenta se neviditelně inicializuje na pozadí. Neotevírá postranní panel. Headless komponenty jsou určené pro akce, které provedou logiku a poté se odpojí — například spuštění asynchronního úkolu, navigaci na stránku nebo zobrazení potvrzovacího modálního okna. Přirozeně se hodí ke komponentám SDK Command popsaným níže. + +```tsx src/front-components/sync-tracker.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component'; +import { useEffect } from 'react'; + +const SyncTracker = () => { + const recordId = useRecordId(); + + useEffect(() => { + enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' }); + }, [recordId]); + + return null; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'sync-tracker', + description: 'Tracks record views silently', + isHeadless: true, + component: SyncTracker, +}); +``` + +Protože komponenta vrací `null`, Twenty přeskočí vykreslení kontejneru — v rozvržení se neobjeví žádné prázdné místo. Komponenta má však stále přístup ke všem hookům a API komunikace s hostitelem. + +## Komponenty SDK Command + +Balíček `twenty-sdk` poskytuje čtyři pomocné komponenty Command navržené pro headless front-endové komponenty. Každá komponenta při připojení provede akci, chyby zpracuje zobrazením oznámení ve snackbaru a po dokončení automaticky odpojí front-endovou komponentu. + +Importujte je z `twenty-sdk/command`: + +* **`Command`** — Spustí asynchronní callback přes prop `execute`. +* **`CommandLink`** — Naviguje na cestu v aplikaci. Props: `to`, `params`, `queryParams`, `options`. +* **`CommandModal`** — Otevře potvrzovací modální okno. Pokud uživatel potvrdí, provede callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. +* **`CommandOpenSidePanelPage`** — Otevře konkrétní stránku postranního panelu. Props: `page`, `pageTitle`, `pageIcon`. + +Zde je kompletní příklad headless front-endové komponenty, která pomocí `Command` spouští akci z menu příkazů: + +```tsx src/front-components/run-action.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const RunAction = () => { + const execute = async () => { + const client = new CoreApiClient(); + + await client.mutation({ + createTask: { + __args: { data: { title: 'Created by my app' } }, + id: true, + }, + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', + name: 'run-action', + description: 'Creates a task from the command menu', + component: RunAction, + isHeadless: true, +}); +``` + +```ts src/command-menu-items/run-action.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', + label: 'Run my action', + icon: 'IconPlayerPlay', + frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', +}); +``` + +A příklad s použitím `CommandModal` k vyžádání potvrzení před provedením: + +```tsx src/front-components/delete-draft.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { CommandModal } from 'twenty-sdk/command'; + +const DeleteDraft = () => { + const execute = async () => { + // perform the deletion + }; + + return ( + + ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', + name: 'delete-draft', + description: 'Deletes a draft with confirmation', + component: DeleteDraft, + isHeadless: true, +}); +``` + +## Volání logické funkce + +Front komponenty běží v prohlížeči v izolovaném web workeru, zatímco [logické funkce](/l/cs/developers/extend/apps/logic/logic-functions) běží na serveru. Neexistuje mezi nimi žádné přímé volání v rámci jednoho procesu — místo toho se front komponenta k logické funkci připojuje přes HTTP. + +Logická funkce deklarovaná pomocí `httpRouteTriggerSettings` je vystavena pod endpointem `/s/` na `${TWENTY_API_URL}/s\`. Vaše front komponenta volá tuto trasu pomocí `RestApiClient` z `twenty-client-sdk/rest`, který se autentizuje pomocí `TWENTY_APP_ACCESS_TOKEN`, který Twenty do workeru vkládá. + +`RestApiClient` je přesně pro tento účel. Z worker prostředí čte `TWENTY_API_URL` a `TWENTY_APP_ACCESS_TOKEN`, přidává hlavičku `Authorization: Bearer`, serializuje a parsuje JSON a vyhazuje `RestApiClientError`, pokud token nebo URL chybí nebo je odpověď mimo rozsah 2xx — takže nemusíte tento boilerplate znovu implementovat v každé komponentě. + +Headless front komponenta může volání spustit při mountu přes komponentu `Command` a poté se automaticky odmountovat: + +```tsx src/front-components/sync-prs.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { RestApiClient } from 'twenty-client-sdk/rest'; + +const SyncPrs = () => { + const execute = async () => { + const client = new RestApiClient(); + + await client.post('/s/github/fetch-prs', { + owner: 'twentyhq', + repo: 'twenty', + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'sync-prs', + description: 'Triggers the fetch-prs logic function', + isHeadless: true, + component: SyncPrs, +}); +``` + +Cesta předaná klientovi je veřejná cesta trasy — `httpRouteTriggerSettings.path` logické funkce s předponou `/s`. Ponechte `isAuthRequired: true`; klient poskytuje pro vaši komponentu přístupový token aplikace vydaný Twenty: + +```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, + }, +}); +``` + + +`TWENTY_API_URL` a `TWENTY_APP_ACCESS_TOKEN` jsou vloženy automaticky — viz [Proměnné aplikace](#application-variables). Protože tajné proměnné aplikace nejsou nikdy vystaveny front komponentám, ponechte API klíče a další citlivou logiku v logické funkci, ne ve front komponentě. + + +### Reference `RestApiClient` + +Importujte `RestApiClient` z `twenty-client-sdk/rest`. Patří do stejné rodiny klientů jako `CoreApiClient` a `MetadataApiClient`, ale cílí na HTTP trasy vaší aplikace místo na GraphQL API. + +| Metoda | Popis | +| --------------------------------- | ------------------------------------------ | +| `get(path, options?)` | Odešle požadavek `GET` | +| `post(path, body?, options?)` | Odešle požadavek `POST` | +| `put(path, body?, options?)` | Odešle požadavek `PUT` | +| `patch(path, body?, options?)` | Odešle požadavek `PATCH` | +| `delete(path, options?)` | Odešle požadavek `DELETE` | +| `request(method, path, options?)` | Obecný požadavek s libovolnou metodou HTTP | + +`options` přijímá `headers`, `query` (záznam parametrů dotazovacího řetězce; hodnoty typu nullish jsou vynechány) a `AbortSignal` prostřednictvím `signal`. Objekt `body`, který není typu `FormData`, je automaticky serializován do JSON. Při `401` klient jednou obnoví přístupový token prostřednictvím hostitele a požadavek znovu odešle. + +Základní URL a token jsou ve výchozím nastavení odvozeny z prostředí. Podle potřeby předávejte konstruktoru přepsané hodnoty — například v testech: + +```ts +const client = new RestApiClient({ + baseUrl: 'https://api.example.com', + token: 'my-token', +}); +``` + +Neúspěšné požadavky vyvolají `RestApiClientError`, který zpřístupňuje `status`, `statusText`, `url` a parsované `body`: + +```tsx +import { RestApiClient, RestApiClientError } from 'twenty-client-sdk/rest'; + +const client = new RestApiClient(); + +try { + const prs = await client.get('/s/github/fetch-prs', { + query: { state: 'open' }, + }); +} catch (error) { + if (error instanceof RestApiClientError) { + console.error(error.status, error.body); + } +} +``` + +## Přístup k běhovému kontextu + +Uvnitř komponenty použijte hooky SDK pro přístup k aktuálnímu uživateli, záznamu a instanci komponenty: + +```tsx src/front-components/record-info.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { + useUserId, + useRecordId, + useFrontComponentId, +} from 'twenty-sdk/front-component'; + +const RecordInfo = () => { + const userId = useUserId(); + const recordId = useRecordId(); + const componentId = useFrontComponentId(); + + return ( +
+

User: {userId}

+

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

+

Component: {componentId}

+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', + name: 'record-info', + component: RecordInfo, +}); +``` + +Dostupné hooky: + +| Hook | Vrací | Popis | +| --------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------ | +| `useUserId()` | `string` nebo `null` | ID aktuálního uživatele | +| `useSelectedRecordIds()` | `string[]` | Všechna vybraná ID záznamů (prázdné pole, pokud není nic vybráno) | +| `useRecordId()` | `string` nebo `null` | **Zastaralé.** Použijte místo toho `useSelectedRecordIds()` | +| `useFrontComponentId()` | `string` | ID této instance komponenty | +| `useColorScheme()` | `'light'` nebo `'dark'` | Aktivní barevné schéma uživatelského rozhraní hostitele (`System` je již vyhodnocen) | +| `useFrontComponentExecutionContext(selector)` | různé | Přístup k úplnému kontextu běhu pomocí selektorové funkce | + +## Aplikační proměnné + +Aplikační proměnné definované v [`defineApplication()`](/l/cs/developers/extend/apps/config/application) s `isSecret: false` jsou k dispozici ve front-endových komponentách prostřednictvím pomocné funkce `getApplicationVariable`: + +```tsx src/front-components/greeting.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getApplicationVariable } from 'twenty-sdk/front-component'; + +const Greeting = () => { + const recipientName = getApplicationVariable('DEFAULT_RECIPIENT_NAME') ?? 'World'; + + return

Hello, {recipientName}!

; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'greeting', + component: Greeting, +}); +``` + + +Tajné proměnné (`isSecret: true`) **nejsou** zpřístupněny front-endovým komponentám. Jsou k dispozici pouze v [logických funkcích](/l/cs/developers/extend/apps/logic/logic-functions), které běží na straně serveru. Tím se zabrání odesílání citlivých hodnot, jako jsou API klíče, do prohlížeče. + + +Následující systémové proměnné jsou vždy dostupné přes `process.env`: + +| Proměnná | Popis | +| ------------------------- | -------------------------------------------------------------- | +| `TWENTY_API_URL` | Základní URL Twenty API | +| `TWENTY_APP_ACCESS_TOKEN` | Krátkodobý token s oprávněními omezenými na roli vaší aplikace | + +## API komunikace s hostitelem + +Frontendové komponenty mohou pomocí funkcí z `twenty-sdk` vyvolávat navigaci, modály a oznámení: + +| Funkce | Popis | +| ----------------------------------------------- | ------------------------------ | +| `navigate(to, params?, queryParams?, options?)` | Přejít na stránku v aplikaci | +| `openSidePanelPage(params)` | Otevřít postranní panel | +| `closeSidePanel()` | Zavřít postranní panel | +| `openCommandConfirmationModal(params)` | Zobrazit potvrzovací dialog | +| `enqueueSnackbar(params)` | Zobrazit oznámení typu toast | +| `unmountFrontComponent()` | Odpojit komponentu | +| `updateProgress(progress)` | Aktualizovat indikátor průběhu | + +Zde je příklad, který používá hostitelské API k zobrazení snackbaru a zavření postranního panelu po dokončení akce: + +```tsx src/front-components/archive-record.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { useRecordId } from 'twenty-sdk/front-component'; +import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const ArchiveRecord = () => { + const recordId = useRecordId(); + + const handleArchive = async () => { + const client = new CoreApiClient(); + + await client.mutation({ + updateTask: { + __args: { id: recordId, data: { status: 'ARCHIVED' } }, + id: true, + }, + }); + + await enqueueSnackbar({ + message: 'Record archived', + variant: 'success', + }); + + await closeSidePanel(); + }; + + return ( +
+

Archive this record?

+ +
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', + name: 'archive-record', + description: 'Archives the current record', + component: ArchiveRecord, +}); +``` + +### Práce s více záznamy + +Použijte `useSelectedRecordIds()` pro zpracování více vybraných záznamů. To je užitečné pro hromadné operace: + +```tsx src/front-components/bulk-export.tsx +import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define'; +import { useSelectedRecordIds } from 'twenty-sdk/front-component'; +import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const BulkExport = () => { + const selectedRecordIds = useSelectedRecordIds(); + + const handleExport = async () => { + const client = new CoreApiClient(); + + for (const recordId of selectedRecordIds) { + await client.mutation({ + updateTask: { + __args: { id: recordId, data: { exported: true } }, + id: true, + }, + }); + } + + await enqueueSnackbar({ + message: `Exported ${selectedRecordIds.length} records`, + variant: 'success', + }); + + await closeSidePanel(); + }; + + return ( +
+

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

+ +
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901', + name: 'bulk-export', + description: 'Export selected records', + component: BulkExport, + command: { + universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902', + label: 'Bulk Export', + availabilityType: 'RECORD_SELECTION', + conditionalAvailabilityExpression: numberOfSelectedRecords > 0, + }, +}); +``` + +## Veřejné soubory + +Frontendové komponenty mohou přistupovat k souborům ze složky aplikace `public/` pomocí `getPublicAssetUrl`: + +```tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const Logo = () => Logo; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'logo', + component: Logo, +}); +``` + +Podrobnosti viz [sekci veřejných souborů](/l/cs/developers/extend/apps/config/public-assets). + +## Stylování + +Frontendové komponenty podporují více přístupů ke stylování. Můžete použít: + +* **Inline styly** — `style={{ color: 'red' }}` +* **Komponenty Twenty UI** — import z `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar a další) +* **Emotion** — CSS-in-JS s `@emotion/react` +* **Styled-components** — vzory `styled.div` +* **Tailwind CSS** — utilitní třídy +* **Jakákoli CSS-in-JS knihovna** kompatibilní s Reactem + +```tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Button, Tag, Status } from 'twenty-sdk/ui'; + +const StyledWidget = () => { + return ( +
+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', + name: 'styled-widget', + component: StyledWidget, +}); +``` diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx new file mode 100644 index 0000000000..13e713a105 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/navigation-menu-items.mdx @@ -0,0 +1,44 @@ +--- +title: Položky navigační nabídky +description: Přidejte vlastní položky do postranního panelu pracovního prostoru — odkazy na uložená zobrazení nebo externí adresy URL. +icon: bars +--- + +**Položka navigační nabídky** je položka v levém postranním panelu. Použijte `defineNavigationMenuItem()` k přidání vlastních odkazů do postranního panelu — obvykle jeden pro každé [zobrazení](/l/cs/developers/extend/apps/layout/views), které dodáváte — nebo pro odkaz na externí adresy URL. + +```ts src/navigation-menu-items/example-navigation-menu-item.ts +import { defineNavigationMenuItem, NavigationMenuItemType } from 'twenty-sdk/define'; +import { EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER } from '../views/example-view'; + +export default defineNavigationMenuItem({ + universalIdentifier: '9327db91-afa1-41b6-bd9d-2b51a26efb4c', + name: 'example-navigation-menu-item', + icon: 'IconList', + color: 'blue', + position: 0, + type: NavigationMenuItemType.VIEW, + viewUniversalIdentifier: EXAMPLE_VIEW_UNIVERSAL_IDENTIFIER, +}); +``` + +## Hlavní body + +* `type` určuje, na co položka nabídky odkazuje. Každý typ je spárován s konkrétním identifikačním polem: + + | Typ | K čemu slouží | Povinné pole | + | ------------------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------- | + | `NavigationMenuItemType.VIEW` | Otevře uložené zobrazení | `viewUniversalIdentifier` | + | `NavigationMenuItemType.LINK` | Otevře externí adresu URL | `link` | + | `NavigationMenuItemType.FOLDER` | Seskupuje vnořené položky pod štítkem | `name` (a podřízené položky odkazují na složku prostřednictvím `folderUniversalIdentifier`) | + | `NavigationMenuItemType.OBJECT` | Otevře výchozí indexovou stránku objektu | `targetObjectUniversalIdentifier` | + | `NavigationMenuItemType.PAGE_LAYOUT` | Otevře samostatné rozvržení stránky | `pageLayoutUniversalIdentifier` | + +* `position` určuje pořadí v postranním panelu. + +* `icon` a `color` jsou volitelné a upravují, jak položka vypadá. + +* `folderUniversalIdentifier` je k dispozici také na libovolné položce, aby ji bylo možné vložit do nadřazené položky typu `FOLDER`. + + +**Častý problém:** vytvoření objektu bez souvisejícího zobrazení a položky navigační nabídky způsobí, že je tento objekt pro uživatele neviditelný. Pokud nejde o technický/interní objekt, měl by mít každý vlastní objekt výchozí zobrazení *a* položku v postranním panelu, která na něj odkazuje. + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/overview.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/overview.mdx new file mode 100644 index 0000000000..3070c3f96f --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/overview.mdx @@ -0,0 +1,56 @@ +--- +title: Přehled +description: Umístěte svou aplikaci do uživatelského rozhraní Twenty – položky v postranním panelu, uložená zobrazení, karty na stránce záznamu a sandboxované komponenty Reactu. +icon: table-columns +--- + +**Vrstva rozvržení** aplikace Twenty zahrnuje vše, co uživatel vidí: kde se aplikace zobrazuje v postranním panelu, jaká seznamová zobrazení obsahuje, jak jsou uspořádány její stránky s podrobnostmi záznamů a které vlastní komponenty Reactu se na těchto stránkách vykreslují. + +```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 └─────────────────────┘ +``` + +## V této části + + + + `defineView` — uložené konfigurace seznamu: viditelné sloupce, filtry, skupiny. + + + `defineNavigationMenuItem` — položky v postranním panelu odkazující na zobrazení nebo externí adresy URL. + + + `definePageLayout` a `definePageLayoutTab` — karty a widgety na stránce s podrobnostmi záznamu. + + + `defineFrontComponent` — sandboxované komponenty Reactu, které se vykreslují uvnitř Twenty. + + + `defineCommandMenuItem` — zaregistruje frontendové komponenty jako položky Cmd+K a rychlé akce. + + + +## Kde se aplikace zobrazuje + +| Umístění | Co řídí | Entita | +| --------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------- | +| **Postranní panel** | Vlastní položka odkazující na uložené zobrazení nebo externí adresu URL | `defineNavigationMenuItem` | +| **Seznam záznamů** | Uložené nastavení pro objekt — viditelné sloupce, pořadí, filtry, skupiny | `defineView` | +| **Stránka s podrobnostmi záznamu** | Karty a widgety na stránce záznamu (vašeho vlastního objektu nebo standardního) | `definePageLayout`, `definePageLayoutTab` | +| **Uvnitř kteréhokoli z výše uvedených** | Vlastní widget Reactu — tlačítka, formuláře, přehledové panely, integrace | `defineFrontComponent` | +| **Příkazová nabídka (Cmd+K)** | Připnutá rychlá akce nebo skrytý příkaz | `defineCommandMenuItem` | + +Frontendové komponenty běží uvnitř izolovaného Web Workeru pomocí Remote DOM — vykreslují se na stránce nativně (ne uvnitř iframe), ale nemají přímý přístup k hostitelské stránce ani DOM. Komunikace s Twenty probíhá prostřednictvím hostitelského API pro předávání zpráv. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/page-layouts.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/page-layouts.mdx new file mode 100644 index 0000000000..5b1e284d80 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/page-layouts.mdx @@ -0,0 +1,132 @@ +--- +title: Rozvržení stránek +description: Přizpůsobte stránky s detailem záznamu – karty, widgety a místa, kde se vykreslují frontendové komponenty – pomocí `definePageLayout` a `definePageLayoutTab`. +icon: table-columns +--- + +**Rozvržení stránky** určuje, jak je uspořádána stránka s detailem záznamu: které karty se zobrazí a jaké widgety obsahují. Použijte `definePageLayout()` k deklaraci rozvržení pro objekt, který vlastníte, nebo `definePageLayoutTab()` k přidání jedné karty do rozvržení, které již existuje (vašeho nebo standardního rozvržení Twenty). + +| Případ použití | Entita | +| ----------------------------------------------------------------------------------------------------------- | --------------------- | +| Definujte celé rozvržení pro stránku záznamu u objektu, který vlastníte | `definePageLayout` | +| Přidejte jednu kartu do existujícího rozvržení (k rozvržení vašeho vlastního objektu nebo ke standardnímu). | `definePageLayoutTab` | + +## definePageLayout + +Použijte to, když vlastníte celou stránku s detailem záznamu – typicky pro vlastní objekt, který jste si definovali sami. + +```ts src/page-layouts/example-record-page-layout.ts +import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define'; +import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; +import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; + +export default definePageLayout({ + universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134', + name: 'Example Record Page', + type: 'RECORD_PAGE', + objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, + tabs: [ + { + universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5', + title: 'Hello World', + position: 50, + icon: 'IconWorld', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + widgets: [ + { + universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d', + title: 'Hello World', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], + }, + ], +}); +``` + +### Hlavní body + +* `type` je obvykle `'RECORD_PAGE'` pro úpravu detailního zobrazení konkrétního objektu. +* `objectUniversalIdentifier` určuje, na který objekt se toto rozvržení vztahuje. +* Každá `tab` definuje sekci stránky s `title`, `position` a `layoutMode` (`CANVAS` pro volné rozvržení). +* Každý `widget` uvnitř karty může vykreslit [front component](/l/cs/developers/extend/apps/layout/front-components), seznam relací nebo jiné vestavěné typy widgetů. +* `position` na kartách určuje jejich pořadí. Použijte vyšší hodnoty (např. 50) pro umístění vlastních karet za vestavěné. + +## definePageLayoutTab + +Použijte to, když chcete do existujícího rozvržení pouze **přidat** kartu – například kartu analytiky na standardní stránce Company nebo kartu se souhrnem AI připojenou k rozvržení vašeho vlastního objektu. + +```ts src/page-layouts/example-extra-tab.ts +import { + definePageLayoutTab, + PageLayoutTabLayoutMode, + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; +import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; + +export default definePageLayoutTab({ + universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', + pageLayoutUniversalIdentifier: + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage + .universalIdentifier, + title: 'Hello World', + position: 1000, + icon: 'IconWorld', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + widgets: [ + { + universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002', + title: 'Hello World', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], +}); +``` + +### Hlavní body + +* `pageLayoutUniversalIdentifier` je **povinný** a musí odkazovat na rozvržení stránky, které již existuje v době instalace – buď na standardní rozvržení Twenty, nebo na rozvržení definované vaší vlastní aplikací. Meziaplikační odkazy na rozvržení ve vlastnictví jiné nainstalované aplikace nejsou v současnosti podporovány. Když nadřazené rozvržení chybí, instalace selže s jasnou validační chybou. + +* Pro standardní rozvržení Twenty importujte identifikátory z `twenty-sdk/define`: + + ```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 + // … + ``` + + Každá položka rozvržení také zpřístupňuje své `tabs` a jejich `widgets`, takže můžete odkazovat na libovolnou úroveň: + + ```ts + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.universalIdentifier + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets.fields.universalIdentifier + ``` + + K dispozici je také krátký alias `STANDARD_PAGE_LAYOUT`: + + ```ts + import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define'; + + STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier; + ``` + +* `widgets` mají rozsah pouze pro tuto kartu – odkazují na [front components](/l/cs/developers/extend/apps/layout/front-components), zobrazení apod. úplně stejně jako widgety definované přímo v `definePageLayout`. + +* `position` určuje pořadí vzhledem ke stávajícím kartám v cílovém rozvržení. Zvolte hodnotu, která umístí vaši kartu tam, kde ji chcete mít, relativně k vestavěným kartám. + +* Použijte to místo `definePageLayout`, když chcete do existujícího rozvržení pouze přidat. Použijte `definePageLayout`, když vlastníte celé rozvržení. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx new file mode 100644 index 0000000000..9eb7384258 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/layout/views.mdx @@ -0,0 +1,97 @@ +--- +title: Zobrazení +description: Dodávejte předem nakonfigurovaná uložená zobrazení – pořadí sloupců, filtry, seskupení – pro objekty ve své aplikaci. +icon: list +--- + +**Zobrazení** je uložená konfigurace toho, jak se zobrazují záznamy objektu: která pole se zobrazují, v jakém pořadí, zda jsou viditelná a jaké filtry nebo seskupení jsou použity. Pomocí `defineView()` můžete s aplikací dodávat předem nakonfigurovaná zobrazení – obvykle výchozí indexové zobrazení pro každý vlastní objekt, který vytvoříte. + +```ts src/views/example-view.ts +import { defineView, ViewKey } from 'twenty-sdk/define'; +import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; +import { NAME_FIELD_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; + +export default defineView({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + name: 'All example items', + objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, + icon: 'IconList', + key: ViewKey.INDEX, + position: 0, + fields: [ + { + universalIdentifier: 'f926bdb7-6af7-4683-9a09-adbca56c29f0', + fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER, + position: 0, + isVisible: true, + size: 200, + }, + ], +}); +``` + +## Hlavní body + +* `objectUniversalIdentifier` určuje, na který objekt se toto zobrazení vztahuje. Může to být vlastní objekt, který jste definovali, nebo standardní objekt Twenty. +* `key` určuje typ zobrazení — `ViewKey.INDEX` je hlavní seznamové zobrazení pro daný objekt. +* `fields` určuje, které sloupce se zobrazí a v jakém pořadí. Každé pole odkazuje na `fieldMetadataUniversalIdentifier`. +* Pro pokročilé konfigurace můžete také deklarovat `filters`, `filterGroups`, `groups` a `fieldGroups`. +* `position` určuje pořadí, pokud pro stejný objekt existuje více zobrazení. + +## Filtry + +Zobrazení může být dodáno s předem aplikovanými filtry. Každý filtr má tři souřadnice: **pole**, které se filtruje, **operand** (jak porovnávat) a **hodnotu** (proti čemu porovnávat). Všechny tři musí být v souladu — použití operandu, který se nehodí k typu pole, bude při synchronizaci odmítnuto. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Podporované operandy podle typu pole + +| Typ pole | Podporované operandy | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `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` | + +> Typy polí s podobnými názvy mohou používat zcela odlišné operandy — běžným případem jsou `SELECT` a `MULTI_SELECT`. + +### Tvar hodnoty podle operandu + +Pole `value` je vždy hodnota serializovatelná do JSON, ale její očekávaný tvar závisí na operandu: + +| Skupina operandů | Tvar hodnoty | Příklad | +| --------------------------------------------------------------------- | ----------------------------- | ------------------------ | +| `IS`, `IS_NOT` na `SELECT` | pole klíčů možností (řetězce) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` na `MULTI_SELECT` | pole klíčů možností (řetězce) | `['TAG_A']` | +| `IS`, `IS_NOT` na `RELATION` | pole ID záznamů (uuid) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` na textových polích a podobných typech | textový řetězec | `'acme'` | +| `IS`, `IS_NOT` na `NUMBER` | řetězec (hodnota) | `'5'` | +| `IS` na `RATING` / `UUID` | řetězec (hodnota) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | řetězec (mezní hodnota) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` na `DATE` / `DATE_TIME` | řetězec ve formátu ISO 8601 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | prázdný řetězec | `''` | +| `IS` na `BOOLEAN` | `'true'` nebo `'false'` | `'true'` | + +## Jak se zobrazení objevují v uživatelském rozhraní + +Samotné zobrazení není z postranního panelu dostupné. Aby se tam zobrazilo, spárujte ho s [položkou navigačního menu](/l/cs/developers/extend/apps/layout/navigation-menu-items) typu `VIEW`, která odkazuje na `universalIdentifier` daného zobrazení. To je kanonický vzor: každý vlastní objekt obvykle dodává výchozí zobrazení + položku v postranním panelu, která ho otevírá. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic/connections.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic/connections.mdx new file mode 100644 index 0000000000..48840d4a0e --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/logic/connections.mdx @@ -0,0 +1,192 @@ +--- +title: Připojení +description: Umožněte své aplikaci jednat jménem uživatele ve službách třetích stran prostřednictvím OAuth. +icon: plug +--- + +Připojení jsou pověření, která uživatel uchovává pro externí službu (Linear, GitHub, Slack, ...). Vaše aplikace deklaruje, **jak** se tato pověření získávají — **poskytovatel připojení** — a za běhu je používá k provádění ověřených volání na rozhraní API třetí strany. + +V současnosti je podporován pouze OAuth 2.0. Budoucí typy pověření (osobní přístupové tokeny, klíče API, základní autentizace) se připojí ke stejnému rozhraní — aplikace, které již používají `defineConnectionProvider({ type: 'oauth', ... })` nebudou muset migrovat. + + + + + +Poskytovatel připojení popisuje OAuth handshake, který vaše aplikace potřebuje. Uživatel klikne v nastavení vaší aplikace na "Přidat připojení", projde souhlasovou obrazovkou poskytovatele a v jeho pracovním prostoru se vytvoří řádek `ConnectedAccount`. + +Funkční nastavení vyžaduje **dva soubory** — poskytovatele připojení a odpovídající deklaraci `serverVariables` v `defineApplication`, která obsahuje klientská pověření OAuth. + +```ts src/connection-providers/linear-connection.ts +import { defineConnectionProvider } from 'twenty-sdk/define'; + +export default defineConnectionProvider({ + universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f', + name: 'linear', + displayName: 'Linear', + icon: 'IconBrandLinear', + type: 'oauth', + oauth: { + authorizationEndpoint: 'https://linear.app/oauth/authorize', + tokenEndpoint: 'https://api.linear.app/oauth/token', + scopes: ['read', 'write'], + // These must match keys in `defineApplication.serverVariables` below. + clientIdVariable: 'LINEAR_CLIENT_ID', + clientSecretVariable: 'LINEAR_CLIENT_SECRET', + // Optional: defaults to 'json'. Some providers (Linear, Slack) want + // 'form-urlencoded' for the token request. + tokenRequestContentType: 'form-urlencoded', + // Optional: defaults to true. Disable only if the provider rejects PKCE. + usePkce: false, + // Optional: extra query params on the authorize URL. + // authorizationParams: { prompt: 'consent' }, + // Optional: provider's RFC 7009 token revocation endpoint, called on disconnect. + // revokeEndpoint: 'https://example.com/oauth/revoke', + }, +}); +``` + +```ts src/application.config.ts +import { defineApplication } from 'twenty-sdk/define'; + +export default defineApplication({ + universalIdentifier: '...', + displayName: 'Linear', + description: 'Connect Linear to Twenty.', + // OAuth client credentials live on the app registration (one OAuth app per + // Twenty server, configured by the admin) — not per-workspace. Declare them + // as serverVariables so the admin can fill them in once for all installs. + serverVariables: { + LINEAR_CLIENT_ID: { + description: 'OAuth client ID from your Linear OAuth application.', + isSecret: false, + isRequired: true, + }, + LINEAR_CLIENT_SECRET: { + description: 'OAuth client secret from your Linear OAuth application.', + isSecret: true, + isRequired: true, + }, + }, +}); +``` + +Hlavní body: + +* `name` je jedinečný identifikátor (řetězec) používaný v `listConnections({ providerName })` (kebab-case, musí odpovídat `^[a-z][a-z0-9-]*$`). +* `displayName` se zobrazuje na kartě nastavení jednotlivé aplikace a v seznamu nástrojů AI. +* `clientIdVariable` / `clientSecretVariable` jsou názvy, ne hodnoty — musí odpovídat klíčům deklarovaným v `defineApplication.serverVariables`. Skutečné `client_id` a `client_secret` zadává správce serveru prostřednictvím rozhraní pro registraci aplikace, nikdy se necommitují do vašeho repozitáře. +* Použijte `serverVariables` (nikoli `applicationVariables`) — pověření OAuth jsou celoserverová a na jeden server Twenty je jedna aplikace OAuth. +* Dokud nejsou vyplněny obě `serverVariables`, karta nastavení aplikace zobrazuje nápovědu "vyžaduje správce serveru" a tlačítko "Přidat připojení" je zakázané. +* `type: 'oauth'` je dnes jediná podporovaná hodnota. Rozlišovač je kompatibilní do budoucna: budoucí typy (`'pat'`, `'api-key'`, ...) přidají nové podbloky konfigurace vedle `oauth`. + +URL zpětného volání OAuth, kterou musí váš poskytovatel zařadit na seznam povolených, je: + +``` +https:///auth/apps/callback +``` + + + + + +Uvnitř handleru logické funkce vrací `listConnections({ providerName })` řádky `ConnectedAccount` této aplikace pro daného poskytovatele s obnovenými přístupovými tokeny. + +```ts src/logic-functions/handlers/create-linear-issue-handler.ts +import { listConnections } from 'twenty-sdk/logic-function'; + +export const createLinearIssueHandler = async (input: { + teamId?: string; + title?: string; +}) => { + if (!input.teamId || !input.title) { + return { success: false, error: 'teamId and title are required' }; + } + + const connections = await listConnections({ providerName: 'linear' }); + + // Workspace-shared credentials win when present; fall back to the first + // user-visibility one. For HTTP-route triggers you typically pick the + // request user's connection via event.userWorkspaceId instead. + const connection = + connections.find((c) => c.visibility === 'workspace') ?? connections[0]; + + if (!connection) { + return { + success: false, + error: + 'Linear is not connected. Open the app settings and click "Add connection".', + }; + } + + // Use connection.accessToken to call the third-party API. + const response = await fetch('https://api.linear.app/graphql', { + method: 'POST', + headers: { + Authorization: `Bearer ${connection.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`, + }), + }); + + return { success: response.ok }; +}; +``` + +Každé připojení má: + +| Pole | Popis | +| ----------------- | ---------------------------------------------------------------------------------------------------------- | +| `id` | Jedinečné ID řádku; předejte do `getConnection(id)` pro opětovné načtení jednoho záznamu | +| `visibility` | `'user'` (soukromé pro jednoho člena pracovního prostoru) nebo `'workspace'` (sdílené se všemi členy) | +| `scopes` | Oprávnění OAuth udělená poskytovatelem (odlišná od `visibility` — ty spolu nesouvisejí) | +| `userWorkspaceId` | ID `userWorkspace` vlastníka — užitečné pro výběr "připojení uživatele požadavku" ve spouštěčích tras HTTP | +| `accessToken` | Aktuální přístupový token OAuth (v případě vypršení je automaticky obnoven) | +| `name` / `handle` | Zobrazovaný název připojení (automaticky odvozený při OAuth callbacku, uživatelem přejmenovatelný) | +| `authFailedAt` | Nastaveno, když poslední obnovení selhalo; uživatel se musí znovu připojit | + +Hlavní body: + +* Předejte `{ providerName }` pro filtrování podle poskytovatele; vynechejte jej, chcete-li získat všechna připojení, která tato aplikace vlastní napříč všemi poskytovateli. +* Server před vrácením výsledku transparentně obnoví přístupový token. Váš handler vždy uvidí použitelný token (nebo nastavené `authFailedAt`). +* `getConnection(id)` je jednořádkový ekvivalent. + + + + + +Když uživatel klikne na "Přidat připojení", je vyzván k výběru viditelnosti: + +* **Jen pro mě** — pověření je soukromé pro připojujícího se uživatele. Jakákoli logická funkce volaná jejich jménem (spouštěč HTTP trasy s `isAuthRequired: true`) jej uvidí; spouštěče cron a události databáze nikoli. +* **Sdíleno v pracovním prostoru** — jakýkoli člen pracovního prostoru může pověření použít. Spouštěče cron/databáze jej také uvidí, protože nemají žádného uživatele požadavku. + +Pro každý handler použijte tu správnou variantu: + +```ts +// HTTP-route trigger — prefer the request user's own connection. +const conn = + connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ?? + connections.find((c) => c.visibility === 'workspace'); + +// Cron trigger — no request user; only shared credentials are sensible. +const conn = connections.find((c) => c.visibility === 'workspace'); +``` + +Více připojení na (uživatele, poskytovatele) je povoleno, takže tentýž uživatel může mít vedle sebe "Personal Linear" a "Work Linear". + + + + + +Pro každého poskytovatele připojení musí správce serveru nejprve zaregistrovat u třetí strany aplikaci OAuth. + +1. Přejděte do vývojářského nastavení poskytovatele (např. https://linear.app/settings/api/applications/new). +2. Nastavte **Redirect URI** na `\/auth/apps/callback`. +3. Zkopírujte vygenerované **Client ID** a **Client Secret**. +4. Otevřete nainstalovanou aplikaci v Twenty jako správce serveru → nastavte hodnoty na odpovídajících `serverVariables`. +5. Členové pracovního prostoru pak mohou přidávat připojení v sekci aplikace **Připojení**. + + + + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx new file mode 100644 index 0000000000..8d296f03cd --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/logic/logic-functions.mdx @@ -0,0 +1,515 @@ +--- +title: Logické funkce +description: Definujte serverové funkce v TypeScriptu se spouštěči pro HTTP, cron a databázové události. +icon: bolt +--- + +Logické funkce jsou serverové funkce v TypeScriptu, které běží na platformě Twenty. Mohou být spouštěny požadavky HTTP, plány cronu nebo databázovými událostmi — a lze je také zpřístupnit jako nástroje pro agenty AI. + + + + +Každý soubor funkce používá `defineLogicFunction()` k exportu konfigurace s obslužnou funkcí (handlerem) a volitelnými spouštěči. + +```ts src/logic-functions/createPostCard.logic-function.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import type { RoutePayload } from 'twenty-sdk/logic-function'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (params: RoutePayload) => { + const client = new CoreApiClient(); + const body = (params.body ?? {}) as { name?: string }; + const name = body.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'; + + const result = await client.mutation({ + createPostCard: { + __args: { data: { name } }, + id: true, + name: true, + }, + }); + return result; +}; + +export default defineLogicFunction({ + universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', + name: 'create-new-post-card', + timeoutSeconds: 2, + handler, + httpRouteTriggerSettings: { + path: '/post-card/create', + httpMethod: 'POST', + isAuthRequired: true, + }, + /*databaseEventTriggerSettings: { + eventName: 'people.created', + },*/ + /*cronTriggerSettings: { + pattern: '0 0 1 1 *', + },*/ +}); +``` + +Dostupné typy spouštěčů: +* **httpRoute**: Zpřístupní vaši funkci na HTTP cestě a metodě **pod koncovým bodem `/s/`**: +> např. `path: '/post-card/create'` je volatelné na `https://your-twenty-server.com/s/post-card/create` + + +Chcete-li vyvolat logickou funkci spuštěnou trasou z (bezhlavé) front-endové komponenty, podívejte se na [Volání logické funkce](/l/cs/developers/extend/apps/layout/front-components#calling-a-logic-function). + +* **cron**: Spouští vaši funkci podle plánu pomocí výrazu CRON. +* **databaseEvent**: Spouští se při událostech životního cyklu objektů v pracovním prostoru. Když je operace události `updated`, lze konkrétní sledovaná pole určit v poli `updatedFields`. Pokud zůstane nedefinované nebo prázdné, spustí funkci jakákoli aktualizace. +> např. `person.updated`, `*.created`, `company.*` + + +Funkci můžete také spustit ručně pomocí CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}' +``` + +```bash filename="Terminal" +yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf +``` + +Logy můžete sledovat pomocí: + +```bash filename="Terminal" +yarn twenty dev:function:logs +``` + + +#### Payload spouštěče trasy + +Když spouštěč typu route vyvolá vaši logickou funkci, ta obdrží objekt `RoutePayload`, který odpovídá +[AWS HTTP API v2 formátu](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). +Importujte typ `RoutePayload` z `twenty-sdk/logic-function`: + +```ts +import type { RoutePayload } from 'twenty-sdk/logic-function'; + +const handler = async (event: RoutePayload) => { + const { headers, queryStringParameters, pathParameters, body } = event; + const { method, path } = event.requestContext.http; + + return { message: 'Success' }; +}; +``` + +Typ `RoutePayload` má následující strukturu: + + | Vlastnost | Typ | Popis | Příklad | + | ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | + | `headers` | `Record\` | Záhlaví HTTP (pouze ta uvedená v `forwardedRequestHeaders`) | viz sekci níže | + | `queryStringParameters` | `Record\` | Parametry query stringu (více hodnot spojených čárkami) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | + | `pathParameters` | `Record\` | Parametry cesty extrahované ze vzoru trasy | `/users/:id`, `/users/123` -> `{ id: '123' }` | + | `body` | `object \| null` | Parsované tělo požadavku (JSON) | `{ id: 1 }` -> `{ id: 1 }` | + | `rawBody` | `string \| undefined` | Původní tělo požadavku v UTF-8, před parsováním JSONu. Užitečné pro ověřování podpisů webhooků typu HMAC (např. GitHubův `X-Hub-Signature-256`, Stripe). `undefined`, pokud jej běhové prostředí nezachovalo. | | + | `isBase64Encoded` | `boolean` | Zda je tělo kódováno base64 | | + | `requestContext.http.method` | `string` | Metoda HTTP (GET, POST, PUT, PATCH, DELETE) | | + | `requestContext.http.path` | `string` | Nezpracovaná cesta požadavku | | + + +#### forwardedRequestHeaders + +Ve výchozím nastavení se záhlaví HTTP z příchozích požadavků z bezpečnostních důvodů do vaší logické funkce **ne** předávají. +Chcete-li zpřístupnit konkrétní záhlaví, výslovně je uveďte v poli `forwardedRequestHeaders`: + +```ts +export default defineLogicFunction({ + universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', + name: 'webhook-handler', + handler, + httpRouteTriggerSettings: { + path: '/webhook', + httpMethod: 'POST', + isAuthRequired: false, + forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], + }, +}); +``` + +Ve vašem handleru k přeposlaným záhlavím přistupujte takto: + +```ts +const handler = async (event: RoutePayload) => { + const signature = event.headers['x-webhook-signature']; + const contentType = event.headers['content-type']; + + // Validate webhook signature... + return { received: true }; +}; +``` + + +Názvy záhlaví jsou normalizovány na malá písmena. Přistupujte k nim pomocí klíčů s malými písmeny (například `event.headers['content-type']`). + + +#### Vlastní odpověď HTTP + +Ve výchozím nastavení vrácení prosté hodnoty z vašeho handleru odešle tuto hodnotu zpět jako odpověď `200` (JSON pro objekty, `text/plain` pro řetězce). Pro kontrolu stavového kódu a hlaviček odpovědi vraťte `Response` z `twenty-sdk/logic-function`: + +```ts +import { Response } from 'twenty-sdk/logic-function'; + +const handler = async (event: RoutePayload) => { + return new Response('

Hello

', { + status: 201, + headers: { 'content-type': 'text/html' }, + }); +}; +``` + +Z bezpečnostních důvodů jsou hlavičky odpovědi omezeny na seznam povolených položek. Jakákoli hlavička, která není na seznamu (např. `Set-Cookie`, CORS hlavičky jako `Access-Control-Allow-Origin` nebo vlastní hlavičky `X-*`), je tiše zahozena před odesláním odpovědi. Povolené hlavičky odpovědi jsou: + +* `content-type` +* `content-language` +* `content-disposition` +* `cache-control` +* `retry-after` + + +Stavový kód musí být platný stavový kód HTTP (mezi 100 a 599). Názvy hlaviček odpovědi se porovnávají bez rozlišení velikosti písmen. + + +#### Payload spouštěče databázové události + +Když spouštěč databázové události vyvolá vaši logickou funkci, obdrží jeden `DatabaseEventPayload` pro každý změněný záznam. Payload kombinuje metadata o zdrojovém pracovním prostoru a objektu s událostí na úrovni záznamu. + +```ts +import type { + DatabaseEventPayload, + ObjectRecordCreateEvent, + ObjectRecordDestroyEvent, + ObjectRecordUpdateEvent, +} from 'twenty-sdk/logic-function'; + +type Person = { + id: string; + emails?: { primaryEmail?: string }; +}; +``` + +Tělo zprávy obsahuje: + +| Vlastnost | Popis | +| ------------------------------------------------ | ------------------------------------------------------------------------------------------------ | +| `name` | Název události, například `person.updated`. | +| `workspaceId` | Pracovní prostor, ve kterém k události došlo. | +| `objectMetadata` | Metadata objektu, který se změnil. | +| `recordId` | ID změněného záznamu. | +| `userId`, `userWorkspaceId`, `workspaceMemberId` | Pole aktéra, pokud byla událost způsobena uživatelem pracovního prostoru. | +| `properties` | Data záznamu pro událost, s `before`, `after`, `diff` a `updatedFields` v závislosti na operaci. | + +| Událost | Data záznamu | +| ------------------ | -------------------------------------------------------------------------------------------------------------- | +| `person.created` | `event.properties.after` | +| `person.updated` | `event.properties.before`, `event.properties.after`, `event.properties.diff`, `event.properties.updatedFields` | +| `person.destroyed` | `event.properties.before` | + +U logických smazání má `.deleted` podobu jako u aktualizace, protože se změní pole `deletedAt` záznamu. +Pro trvalá smazání použijte `.destroyed`. + + +`databaseEventTriggerSettings.updatedFields` filtruje, které události aktualizace spustí funkci. +`event.properties.updatedFields` říká, která pole se v aktuální události skutečně změnila. + + +Příklad události vytvoření: + +```ts +type PersonCreatedEvent = DatabaseEventPayload< + ObjectRecordCreateEvent +>; + +const handler = async (event: PersonCreatedEvent) => { + const person = event.properties.after; + + return { + personId: event.recordId, + email: person.emails?.primaryEmail, + }; +}; +``` + +Příklad události aktualizace: + +```ts +type PersonUpdatedEvent = DatabaseEventPayload< + ObjectRecordUpdateEvent +>; + +const handler = async (event: PersonUpdatedEvent) => { + const { before, after, diff, updatedFields } = event.properties; + + return { + personId: event.recordId, + updatedFields, + previousEmail: before.emails?.primaryEmail, + currentEmail: after.emails?.primaryEmail, + emailDiff: diff.emails, + }; +}; +``` + +Spouštění pouze při aktualizacích e‑mailu: + +```ts +export default defineLogicFunction({ + ..., + databaseEventTriggerSettings: { + eventName: 'person.updated', + updatedFields: ['emails'], + }, +}); +``` + +Příklad události smazání: + +```ts +type PersonDestroyedEvent = DatabaseEventPayload< + ObjectRecordDestroyEvent +>; + +const handler = async (event: PersonDestroyedEvent) => { + const personBeforeDestroy = event.properties.before; + + return { + personId: event.recordId, + email: personBeforeDestroy.emails?.primaryEmail, + }; +}; +``` + +#### Zpřístupnění funkce jako nástroje AI nebo akce pracovního postupu + +Logické funkce lze zpřístupnit na dvou rozhraních, z nichž každé má vlastní spouštěč: + +* **`toolTriggerSettings`** — zpřístupní funkci AI funkcím Twenty (chat, MCP, volání funkcí). Používá standardní JSON Schema, formát, kterému modely LLM nativně rozumějí. +* **`workflowActionTriggerSettings`** — zobrazí funkci jako krok ve vizuálním builderu workflow. Používá bohaté `InputSchema` od Twenty, aby builder mohl vykreslit správné editory polí, voliče proměnných a štítky. + +Funkce se může rozhodnout pro jedno, druhé nebo obě. Stojí po boku `cronTriggerSettings`, `databaseEventTriggerSettings` a `httpRouteTriggerSettings` — stejný vzor, stejná struktura. + +```ts src/logic-functions/enrich-company.logic-function.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (params: { companyName: string; domain?: string }) => { + const client = new CoreApiClient(); + + const result = await client.mutation({ + createTask: { + __args: { + data: { + title: `Enrich data for ${params.companyName}`, + body: `Domain: ${params.domain ?? 'unknown'}`, + }, + }, + id: true, + }, + }); + + return { taskId: result.createTask.id }; +}; + +export default defineLogicFunction({ + universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479', + name: 'enrich-company', + description: 'Enrich a company record with external data', + timeoutSeconds: 10, + handler, + toolTriggerSettings: {}, +}); +``` + +Hlavní body: + +* Funkce může míchat rozhraní — deklarujte jak `toolTriggerSettings`, tak `workflowActionTriggerSettings`, abyste ji zpřístupnili v chatu i ve workflow builderu. +* `toolTriggerSettings.inputSchema` a `workflowActionTriggerSettings.inputSchema` jsou obě volitelné. Pokud jsou vynechány, sestavovač manifestu je odvodí ze zdrojového kódu handleru (JSON Schema pro nástroj AI, `InputSchema` od Twenty pro akci workflow). Uveďte jej explicitně, když chcete bohatší typování — například u polí s podporou `FieldMetadataType`, jako `CURRENCY` nebo `RELATION` pro workflow builder, nebo s poli `description`, která si AI agent může přečíst: + +```ts +export default defineLogicFunction({ + ..., + toolTriggerSettings: { + inputSchema: { + type: 'object', + properties: { + companyName: { + type: 'string', + description: 'The name of the company to enrich', + }, + domain: { + type: 'string', + description: 'The company website domain (optional)', + }, + }, + required: ['companyName'], + }, + }, +}); +``` + + +**Napište kvalitní `description`.** Agenti AI se spoléhají na pole funkce `description` při rozhodování, kdy nástroj použít. Buďte konkrétní ohledně toho, co nástroj dělá a kdy se má volat. + + +
+
+ + +**Instalační hooky** — předinstalační a poinstalační handlery — sdílejí toto běhové prostředí, ale deklarují se vlastními funkcemi `define` a nepřebírají nastavení spouštěče (triggeru). Viz [Instalační hooky](/l/cs/developers/extend/apps/config/install-hooks) pro `definePreInstallLogicFunction` a `definePostInstallLogicFunction`. + + +## Typovaní klienti API (twenty-client-sdk) + +Balíček `twenty-client-sdk` poskytuje dva typované klienty GraphQL pro práci s Twenty API z vašich logických funkcí a frontendových komponent. + +| Klient | Importovat | Koncový bod | Generováno? | +| ------------------- | ---------------------------- | ---------------------------------------------------------------- | ------------------------------ | +| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — data pracovního prostoru (záznamy, objekty) | Ano, při vývoji/sestavení | +| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — konfigurace pracovního prostoru, nahrávání souborů | Ne, dodává se předem sestavený | + + + + +`CoreApiClient` je hlavní klient pro dotazování a mutace dat pracovního prostoru. Generuje se **z vašeho schématu pracovního prostoru** během `yarn twenty dev` nebo `yarn twenty dev:build`, takže je plně typovaný tak, aby odpovídal vašim objektům a polím. + +```ts +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const client = new CoreApiClient(); + +// Query records +const { companies } = await client.query({ + companies: { + edges: { + node: { + id: true, + name: true, + domainName: { + primaryLinkLabel: true, + primaryLinkUrl: true, + }, + }, + }, + }, +}); + +// Create a record +const { createCompany } = await client.mutation({ + createCompany: { + __args: { + data: { + name: 'Acme Corp', + }, + }, + id: true, + name: true, + }, +}); +``` + +Klient používá syntaxi výběrové sady (selection-set): předáním `true` zahrnete pole, pro argumenty použijte `__args` a pro relace vnořujte objekty. Získáte plné automatické doplňování a kontrolu typů založené na schématu vašeho pracovního prostoru. + + +**CoreApiClient je generován při vývoji/sestavení.** Pokud jej použijete bez předchozího spuštění `yarn twenty dev` nebo `yarn twenty dev:build`, vyvolá chybu. Generování probíhá automaticky — CLI prozkoumá GraphQL schéma vašeho pracovního prostoru a vygeneruje typovaného klienta pomocí `@genql/cli`. + + +#### Použití CoreSchema pro anotace typů + +`CoreSchema` poskytuje typy TypeScriptu odpovídající objektům vašeho pracovního prostoru — hodí se pro typování stavu komponent nebo parametrů funkcí: + +```ts +import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; +import { useState } from 'react'; + +const [company, setCompany] = useState< + Pick | undefined +>(undefined); + +const client = new CoreApiClient(); +const result = await client.query({ + company: { + __args: { filter: { position: { eq: 1 } } }, + id: true, + name: true, + }, +}); +setCompany(result.company); +``` + + + + +`MetadataApiClient` je dodáván předem sestavený v rámci SDK (není vyžadováno žádné generování). Odesílá dotazy na endpoint `/metadata` pro konfiguraci pracovního prostoru, aplikace a nahrávání souborů. + +```ts +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; + +const metadataClient = new MetadataApiClient(); + +// List first 10 objects in the workspace +const { objects } = await metadataClient.query({ + objects: { + edges: { + node: { + id: true, + nameSingular: true, + namePlural: true, + labelSingular: true, + isCustom: true, + }, + }, + __args: { + filter: {}, + paging: { first: 10 }, + }, + }, +}); +``` + +#### Nahrávání souborů + +`MetadataApiClient` obsahuje metodu `uploadFile` pro připojování souborů k polím typu souboru: + +```ts +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import * as fs from 'fs'; + +const metadataClient = new MetadataApiClient(); + +const fileBuffer = fs.readFileSync('./invoice.pdf'); + +const uploadedFile = await metadataClient.uploadFile( + fileBuffer, // file contents as a Buffer + 'invoice.pdf', // filename + 'application/pdf', // MIME type + '58a0a314-d7ea-4865-9850-7fb84e72f30b', // field universalIdentifier +); + +console.log(uploadedFile); +// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' } +``` + +| Parametr | Typ | Popis | +| ---------------------------------- | -------- | ------------------------------------------------------------------- | +| `fileBuffer` | `Buffer` | Surový obsah souboru | +| `filename` | `string` | Název souboru (používá se pro ukládání a zobrazení) | +| `contentType` | `string` | Typ MIME (pokud je vynechán, výchozí je `application/octet-stream`) | +| `fieldMetadataUniversalIdentifier` | `string` | `universalIdentifier` pole typu souboru ve vašem objektu | + +Hlavní body: +* Používá `universalIdentifier` pole (nikoli jeho ID specifické pro pracovní prostor), takže váš kód pro nahrávání funguje v jakémkoli pracovním prostoru, kde je vaše aplikace nainstalována. +* Vrácená hodnota `url` je podepsaná adresa URL, kterou můžete použít k přístupu k nahranému souboru. + + + + + + Když váš kód běží na Twenty (logické funkce nebo frontendové komponenty), platforma vloží přihlašovací údaje jako proměnné prostředí: + + * `TWENTY_API_URL` — Základní URL Twenty API + * `TWENTY_APP_ACCESS_TOKEN` — krátkodobý klíč s rozsahem omezeným na výchozí roli funkce vaší aplikace + + Není nutné je předávat klientům — čtou je automaticky z `process.env`. Oprávnění API klíče jsou určena rolí deklarovanou pomocí `defineApplicationRole()` (nebo odkazovanou prostřednictvím `defaultRoleUniversalIdentifier` v `application-config.ts`). + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx new file mode 100644 index 0000000000..6a58244f47 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/logic/overview.mdx @@ -0,0 +1,55 @@ +--- +title: Přehled +description: Server-side TypeScript, který běží uvnitř Twenty — spouštěný pomocí HTTP rout, plánů CRON, databázových událostí, nástrojů AI nebo akcí pracovního postupu. +icon: bolt +--- + +**Logická vrstva** aplikace Twenty je kód, který *běží* — server-side TypeScript handlery reagující na HTTP požadavky, plány CRON a změny záznamů; AI dovednosti a agenti, kteří fungují uvnitř pracovního prostoru; a připojení OAuth, která umožňují vašim funkcím jednat jménem uživatele ve službách třetích stran. + +```text + ┌─ HTTP route ──┐ + │ Cron schedule │ + │ Database event │ ┌────────────────────┐ + triggers ─┤ AI tool call ├─────▶│ Logic function │ + │ Workflow action │ │ (your handler) │ + │ Manual exec │ └────────────────────┘ + └────────────────────┘ │ + ▼ + ┌────────────────────────────┐ + │ Twenty API (records) │ + │ Third-party API │ + │ (via Connection token) │ + └────────────────────────────┘ +``` + +## V této části + + + + Základní stavební blok — typy spouštěčů, payloady a typovaný klient API. + + + Opakovaně použitelné pokyny pro agenty AI a asistenti s vlastními systémovými prompty. + + + Přihlašovací údaje OAuth, které vaše aplikace uchovává pro služby třetích stran — Linear, GitHub, Slack a další. + + + +## Přehled typů spouštěčů + +Logická funkce volí jeden nebo více spouštěčů — každá z níže uvedených položek je samostatné pole v `defineLogicFunction()`: + +| Spouštěč | Kdy se spouští | Nastavení | +| --------------------------- | ----------------------------------------------------------------- | ------------------------------- | +| **HTTP route** | Požadavek dorazí na váš koncový bod `/s/\` | `httpRouteTriggerSettings` | +| **Cron** | CRON výraz se shoduje | `cronTriggerSettings` | +| **Událost databáze** | Záznam v pracovním prostoru je vytvořen, aktualizován nebo smazán | `databaseEventTriggerSettings` | +| **Nástroj AI** | Funkce Twenty AI se rozhodne zavolat vaši funkci | `toolTriggerSettings` | +| **Akce pracovního postupu** | Krok pracovního postupu vyvolá vaši funkci | `workflowActionTriggerSettings` | + +Funkce běží v izolovaných sandboxovaných procesech Node.js a přistupují k pracovnímu prostoru přes typovaného klienta API omezeného na roli deklarovanou v [`defineApplication()`](/l/cs/developers/extend/apps/config/application). + + +**Instalační hooky** — kód, který běží před nebo po instalaci — sdílejí toto běhové prostředí, ale používají vlastní funkce `define` a nacházejí se pod [Config → Install Hooks](/l/cs/developers/extend/apps/config/install-hooks). + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/logic/skills-and-agents.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/logic/skills-and-agents.mdx new file mode 100644 index 0000000000..c8eda98bf8 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/logic/skills-and-agents.mdx @@ -0,0 +1,138 @@ +--- +title: Dovednosti a agenti +description: Definujte dovednosti a agenty AI pro svou aplikaci. +icon: robot +--- + + + Dovednosti a agenti jsou aktuálně v alfa fázi. Funkce funguje, ale stále se vyvíjí. + + +Aplikace mohou definovat schopnosti AI, které fungují přímo v pracovním prostoru — znovupoužitelné pokyny pro dovednosti a agenty s vlastními systémovými prompty. + + + + +Dovednosti definují znovupoužitelné pokyny a schopnosti, které mohou agenti AI používat ve vašem pracovním prostoru. K definování dovedností s vestavěnou validací použijte `defineSkill()`: + +```ts src/skills/example-skill.ts +import { defineSkill } from 'twenty-sdk/define'; + +export default defineSkill({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + name: 'sales-outreach', + label: 'Sales Outreach', + description: 'Guides the AI agent through a structured sales outreach process', + icon: 'IconBrain', + content: `You are a sales outreach assistant. When reaching out to a prospect: +1. Research the company and recent news +2. Identify the prospect's role and likely pain points +3. Draft a personalized message referencing specific details +4. Keep the tone professional but conversational`, +}); +``` + +Hlavní body: +* `name` je jedinečný identifikátor dovednosti (doporučuje se kebab-case). +* `label` je uživatelsky čitelný název zobrazovaný v UI. +* `content` obsahuje pokyny dovednosti — je to text, který agent AI používá. +* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI. +* `description` (volitelné) poskytuje doplňující kontext o účelu dovednosti. + + + + +Agenti jsou asistenti AI, kteří běží ve vašem pracovním prostoru. K vytvoření agentů s vlastním systémovým promptem použijte `defineAgent()`: + +```ts src/agents/example-agent.ts +import { defineAgent } from 'twenty-sdk/define'; + +export default defineAgent({ + universalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123', + name: 'sales-assistant', + label: 'Sales Assistant', + description: 'Helps the sales team draft outreach emails and research prospects', + icon: 'IconRobot', + prompt: 'You are a helpful sales assistant. Help users with their questions and tasks.', +}); +``` + +Hlavní body: +* `name` je jedinečný identifikátor agenta (doporučuje se kebab-case). +* `label` je zobrazovaný název v UI. +* `prompt` je systémový prompt, který definuje chování agenta. +* `description` (volitelné) poskytuje kontext o tom, co agent dělá. +* `icon` (volitelné) nastavuje ikonu zobrazovanou v UI. +* `modelId` (volitelné) přepíše výchozí model AI používaný agentem. +* `responseFormat` (volitelně) určuje tvar výstupu agenta. Výchozí hodnota je `{ type: 'text' }` pro volný text. Použijte `{ type: 'json', schema }` k vynucení strukturovaného výstupu ve formátu JSON. + +Ve výchozím nastavení agent vrací volný text. Chcete-li získat strukturovaný výstup, nastavte `responseFormat` na `{ type: 'json' }` a poskytněte `schema`: + +```ts src/agents/structured-agent.ts +import { defineAgent } from 'twenty-sdk/define'; + +export default defineAgent({ + universalIdentifier: 'c4d5e6f7-a8b9-0123-cdef-456789012345', + name: 'lead-scorer', + label: 'Lead Scorer', + prompt: 'Score the lead and explain your reasoning.', + responseFormat: { + type: 'json', + schema: { + type: 'object', + properties: { + score: { type: 'number', description: 'Lead score from 0 to 100' }, + summary: { type: 'string', description: 'Short reasoning for the score' }, + }, + required: ['score', 'summary'], + additionalProperties: false, + }, + }, +}); +``` + +Poznámky ke schématu: +* Schéma je plochý objekt: `type` každé vlastnosti musí být primitivní typ (`string`, `number` nebo `boolean`). Vnořené objekty a pole nejsou podporovány. +* `description` (volitelně) u každé vlastnosti navádí model, co má na toto místo doplnit. +* `required` (volitelně) vypisuje vlastnosti, které musí model vždy vrátit. +* `additionalProperties: false` (volitelně) zakáže jakoukoli vlastnost, která není deklarována v `properties`. + + + + +`runAgent()` umožňuje logické funkci spustit jednoho z agentů vaší aplikace (s jeho dovednostmi a nástroji). Identifikujte agenta pomocí `universalIdentifier`, který jste předali do `defineAgent()`: + +```ts src/logic-functions/run-enricher.ts +import { runAgent } from 'twenty-sdk/logic-function'; + +const { result, error, success } = await runAgent({ + agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123', + prompt: 'Enrich House Ad : fill empty fields from its listing URL.', +}); +``` + +Hlavní body: +* Agent běží **synchronně** a může sám číst/aktualizovat záznamy pomocí vlastních nástrojů — `runAgent()` vrátí výsledek až po dokončení běhu. +* Aplikace může spouštět pouze své vlastní agenty. +* [Výchozí role](/l/cs/developers/extend/apps/config/roles) aplikace musí udělovat příznak oprávnění `AI` — přidejte `SystemPermissionFlag.AI` do `permissionFlagUniversalIdentifiers` (nebo nastavte `canAccessAllTools: true`). + Bez něj `runAgent()` selže s chybou oprávnění. +* Nastavte u logické funkce velkorysou hodnotu `timeoutSeconds` — běh agenta může trvat několik sekund. +* `success` je `true` a `result` není null po dokončení běhu; při chybě je `success` `false`, `result` je `null` a `error` obsahuje důvod (například když během běhu workspace vyčerpal AI kredity). + +```ts src/roles/default-role.ts +import { defineApplicationRole, SystemPermissionFlag } from 'twenty-sdk/define'; + +export default defineApplicationRole({ + universalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061', + label: 'Default function role', + // runAgent() requires the AI permission flag on the app's default role. + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.AI], +}); +``` + + + **Vyhněte se smyčkám:** pokud voláte `runAgent()` z databázového triggeru `*.updated` a agent aktualizuje stejný záznam, omezte trigger pomocí `updatedFields` na pole, do kterého agent nikdy nezapisuje (např. zdrojovou URL), nebo před voláním `runAgent()` zkontrolujte, zda je některé cílové pole stále prázdné. + + + + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx new file mode 100644 index 0000000000..be09dd9da0 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/cli.mdx @@ -0,0 +1,105 @@ +--- +title: CLI +description: příkazy `yarn twenty` pro spouštění funkcí, streamování logů, správu instalací aplikací a přepínání vzdálených serverů. +icon: terminal +--- + +Kromě `dev`, `dev:build`, `dev:add` a `dev:typecheck` poskytuje `yarn twenty` CLI příkazy pro spouštění funkcí, zobrazení logů a správu instalací aplikací. + +## Spouštění funkcí (`yarn twenty dev:function:exec`) + +Spusťte logickou funkci ručně bez vyvolání přes HTTP, cron nebo databázovou událost: + +```bash filename="Terminal" +# Execute by function name +yarn twenty dev:function:exec -n create-new-post-card + +# Execute by universalIdentifier +yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf + +# Pass a JSON payload +yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}' + +# Execute the post-install function +yarn twenty dev:function:exec --postInstall +``` + +## Zobrazení logů funkcí (`yarn twenty dev:function:logs`) + +Streamujte výstupní logy běhu logických funkcí vaší aplikace: + +```bash filename="Terminal" +# Stream all function logs +yarn twenty dev:function:logs + +# Filter by function name +yarn twenty dev:function:logs -n create-new-post-card + +# Filter by universalIdentifier +yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf +``` + + +To se liší od `yarn twenty docker:logs`, který zobrazuje logy kontejneru Docker. `yarn twenty dev:function:logs` zobrazuje logy běhu funkcí vaší aplikace ze serveru Twenty. + + +## Generování typovaného klienta (`yarn twenty dev:generate-client`) + +Znovu vygenerujte typovaného klienta API (`twenty-client-sdk`) ze schématu aktivního vzdáleného serveru, bez sestavování nebo synchronizace aplikace. Použijte jej k získání typovaného klienta v libovolném projektu – například backendové služby v samostatném repozitáři – který komunikuje s vaší instancí Twenty: + +```bash filename="Terminal" +# In your project (no Twenty app definition required) +yarn add twenty-sdk twenty-client-sdk + +# Connect to the Twenty instance to generate the client from +yarn twenty remote:add + +# Generate the typed client into node_modules/twenty-client-sdk +yarn twenty dev:generate-client +``` + +Poté klienta importujte ve svém kódu: + +```typescript +import { CoreApiClient } from 'twenty-client-sdk/core'; +``` + +Spusťte příkaz znovu pokaždé, když se změní váš datový model, abyste aktualizovali vygenerované typy. + + +Klient je vygenerován uvnitř `node_modules`, takže není verzován spolu s vaším kódem. Spusťte `yarn twenty dev:generate-client` po každé instalaci (například ve skriptu `postinstall` nebo v CI). + + +## Odinstalace aplikace (`yarn twenty app:uninstall`) + +Odeberte svou aplikaci z aktivního pracovního prostoru: + +```bash filename="Terminal" +yarn twenty app:uninstall + +# Skip the confirmation prompt +yarn twenty app:uninstall --yes +``` + +## Správa vzdálených serverů + +**Remote** je server Twenty, ke kterému se vaše aplikace připojuje. Během nastavení jej generátor kostry automaticky vytvoří. Můžete kdykoli přidat další vzdálené servery nebo mezi nimi přepínat. + +```bash filename="Terminal" +# Add a new remote (opens a browser for OAuth login) +yarn twenty remote:add + +# Connect to a local Twenty server (auto-detects port 2020 or 3000) +yarn twenty remote:add --local + +# Add a remote non-interactively (useful for CI) +yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote + +# List all configured remotes +yarn twenty remote:list + +# Set the active remote +yarn twenty remote:use +``` + +Vaše přihlašovací údaje jsou uloženy v `~/.twenty/config.json`. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/overview.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/overview.mdx new file mode 100644 index 0000000000..e53072d1c6 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/overview.mdx @@ -0,0 +1,32 @@ +--- +title: Přehled +description: Sestavte, otestujte a doručte svou aplikaci — příkazy CLI, integrační testy, CI a publikování na server nebo do npm. +icon: rocket +--- + +**Provozní vrstva** je všechno, co děláte *na* své aplikaci, nikoli *pomocí* ní: spouštění příkazů CLI, provádění integračních testů proti reálnému serveru Twenty, konfigurace CI a vydávání verzí — buď jako tarball nasazený na jednom serveru, nebo jako balíček npm uvedený v Marketplace. + +```text + develop ─▶ test ─▶ build ─▶ deploy / publish + ─────── ──── ───── ───────────────── + yarn yarn yarn yarn twenty app:publish --private (tarball → one server) + twenty test twenty + dev dev:build yarn twenty app:publish (npm → marketplace) +``` + +## V této části + + + + Referenční přehled `yarn twenty` — exec, logs, uninstall, remotes. + + + Který příkaz kdy použít, jak číst diff synchronizace a postup obnovy. + + + Nastavení Vitestu, integrační testy, kontrola typů, workflow CI. + + + Sestavit, nasadit tarball, publikovat do npm, nainstalovat. + + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx new file mode 100644 index 0000000000..7454d2d080 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/publishing.mdx @@ -0,0 +1,294 @@ +--- +title: Publikování +icon: nahrát +description: Distribuujte svou aplikaci Twenty do Marketplace nebo ji nasaďte interně. +--- + +## Přehled + +Jakmile je vaše aplikace [sestavena a otestována lokálně](/l/cs/developers/extend/apps/getting-started/concepts), máte dvě cesty, jak ji distribuovat: + +* **Nasaďte tarball** — nahrajte svou aplikaci přímo na konkrétní server Twenty pro interní nebo soukromé použití. +* **Publish to npm** — uveďte svou aplikaci v Marketplace Twenty, aby ji mohl kterýkoli pracovní prostor objevit a nainstalovat. + +Obě cesty začínají stejným krokem **build**. + +## Sestavení vaší aplikace + +Spusťte příkaz build ke zkompilování své aplikace a k vygenerování souboru `manifest.json` připraveného k distribuci: + +```bash filename="Terminal" +yarn twenty dev:build +``` + +Tím se zkompilují zdrojové soubory TypeScriptu, transpilují logické funkce a frontendové komponenty a vše se zapíše do `.twenty/output/`. Přidejte `--tarball`, abyste také vytvořili balíček `.tgz` pro ruční distribuci nebo příkaz publish. + +## Nasazení na server (tarball) + +U aplikací, které nechcete zpřístupnit veřejně — proprietární nástroje, integrace pouze pro enterprise nebo experimentální buildy — můžete nasadit tarball přímo na server Twenty. + +### Předpoklady + +Před nasazením potřebujete nakonfigurovaný vzdálený cíl směřující na cílový server. Vzdálené cíle ukládají adresu URL serveru a přihlašovací údaje lokálně v `~/.twenty/config.json`. + +Přidat vzdálený cíl: + +```bash filename="Terminal" +yarn twenty remote:add --url https://your-twenty-server.com --as production +``` + +### Nasazení + +Sestavte a nahrajte svou aplikaci na server v jednom kroku: + +```bash filename="Terminal" +yarn twenty app:publish --private +# To deploy to a specific remote: +# yarn twenty app:publish --private --remote production +``` + +### Sdílení nasazené aplikace + + +Sdílení soukromých (tarball) aplikací napříč pracovními prostory je funkcí **Enterprise**. Karta **Distribuce** bude místo ovládacích prvků sdílení zobrazovat výzvu k upgradu, dokud váš pracovní prostor nebude mít platný klíč Enterprise. Přejděte do [Nastavení > Admin Panel > Enterprise](/settings/admin-panel#enterprise) a aktivujte ji. + + +Aplikace ve formě tarball nejsou uvedeny ve veřejném tržišti, takže je ostatní pracovní prostory na tomtéž serveru procházením neobjeví. Jakmile je váš pracovní prostor na tarifu Enterprise, můžete sdílet nasazenou aplikaci takto: + +1. Přejděte do **Nastavení > Aplikace > Registrace** a otevřete svou aplikaci +2. Na kartě **Distribuce** klikněte na **Zkopírovat odkaz ke sdílení** +3. Sdílejte tento odkaz s uživateli v jiných pracovních prostorech — zavede je přímo na instalační stránku aplikace + +Odkaz ke sdílení používá základní adresu URL serveru (bez jakékoli subdomény pracovního prostoru), takže funguje pro libovolný pracovní prostor na serveru. + +### Správa verzí + +Při aktualizaci již nasazené tarballové aplikace server vyžaduje, aby hodnota `version` v `package.json` byla **přísně vyšší** (podle řazení [semver](https://semver.org)) než aktuálně nasazená verze. Opětovné nasazení stejné verze nebo odeslání nižší verze je odmítnuto ještě před uložením tarballu — v CLI uvidíte chybu `VERSION_ALREADY_EXISTS`. + +Chcete-li vydat aktualizaci: + +1. Zvyšte hodnotu pole `version` v souboru `package.json` (např. `1.2.3` → `1.2.4`, `1.3.0` nebo `2.0.0`) +2. Spusťte `yarn twenty app:publish --private` (nebo `yarn twenty app:publish --private --remote production`) +3. Pracovní prostory, které mají aplikaci nainstalovanou, uvidí dostupnou aktualizaci ve svém nastavení + + +Předběžné tagy fungují podle očekávání: zvýšení z `1.0.0-rc.1` → `1.0.0-rc.2` je povoleno a finální vydání jako `1.0.0` je správně rozpoznáno jako vyšší než `1.0.0-rc.5`. Verze v `package.json` musí být platným řetězcem semver. + + +{/* TODO: add screenshot of the Upgrade button */} + +### Kompatibilita verze serveru + +Pokud vaše aplikace používá funkci zavedenou v konkrétní verzi serveru Twenty (například poskytovatelé OAuth přidaní ve verzi 2.3.0), měli byste deklarovat minimální verzi serveru, kterou vaše aplikace vyžaduje, pomocí pole `engines.twenty` v `package.json`: + +```json filename="package.json" +{ + "name": "twenty-my-app", + "version": "1.0.0", + "engines": { + "node": "^24.5.0", + "twenty": ">=2.3.0" + } +} +``` + +Hodnota je standardní [rozsah SemVer](https://github.com/npm/node-semver#ranges). Běžné vzory: + +| Rozsah | Význam | +| ---------------------------------- | ---------------------------------------------- | +| `>=2.3.0` | Jakýkoli server od verze 2.3.0 výše | +| `>=2.3.0 \<3.0.0` | 2.3.0 nebo novější, ale pod další hlavní verzí | +| `^2.3.0` | Stejné jako `>=2.3.0 \<3.0.0` | + +**Co se děje při nasazení a instalaci:** + +* Pokud je `engines.twenty` nastaveno a verze cílového serveru nevyhovuje rozsahu, nasazení (nahrání tarballu) nebo instalace je odmítnuto chybou `SERVER_VERSION_INCOMPATIBLE` a zprávou, která uvádí jak požadovaný rozsah, tak skutečnou verzi serveru. +* Pokud `engines.twenty` **není nastaveno**, aplikace je přijata na jakékoli verzi serveru (zpětně kompatibilní se stávajícími aplikacemi). +* Pokud server nemá nakonfigurované `APP_VERSION`, kontrola se přeskočí. + + +Server je rozhodující autoritou — ověřuje `engines.twenty` jak při nahrání tarballu, tak při instalaci do pracovního prostoru. Pokud nasazujete tarball mimo standardní proces nebo instalujete z marketplace, server přesto vynucuje kompatibilitu. + + +## Automatizované CI/CD (předpřipravené workflowy) + +Aplikace vygenerované pomocí `create-twenty-app` jsou hned připravené se dvěma workflowy GitHub Actions ve složce `.github/workflows/`. Jsou připravené ke spuštění hned, jakmile repozitář pushnete na GitHub — pro CI není potřeba žádné další nastavení a CD vyžaduje pouze jeden secret. + +### CI — `ci.yml` + +Automaticky spouští integrační testy při každém pushi do `main` a u pull requestů. + +**K čemu slouží:** + +1. Provede checkout zdrojového kódu vaší aplikace. +2. Spustí izolovanou testovací instanci Twenty pomocí složené akce `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (ekvivalent v CI k `yarn twenty docker:start --test`). +3. Povolí Corepack, nastaví Node.js podle vašeho `.nvmrc` a nainstaluje závislosti pomocí `yarn install --immutable`. +4. Spustí `yarn test` a předá `TWENTY_API_URL` a `TWENTY_API_KEY` ze spuštěné instance, aby vaše testy mohly komunikovat se skutečným serverem. + +**Konfigurační volby:** + +* `TWENTY_VERSION` (env, výchozí hodnota `latest`) — uzamkněte v CI používanou verzi serveru Twenty úpravou této hodnoty v `ci.yml`. +* Souběžné běhy jsou seskupeny podle `github.ref` a při nových pushích ruší právě probíhající běhy. + +Nejsou potřeba žádné secrety — testovací instance je efemérní a existuje pouze po dobu běhu úlohy. + +### CD — `cd.yml` + +Nasazuje vaši aplikaci na nakonfigurovaný server Twenty při každém pushi do `main` a volitelně také z pull requestu, pokud je přidán štítek `deploy`. + +**K čemu slouží:** + +1. Provede checkout headu PR (u označených PR) nebo pushnutého commitu. +2. Spustí `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — ekvivalent v CI k `yarn twenty app:publish --private`. +3. Spustí `twentyhq/twenty/.github/actions/install-twenty-app@main`, aby se nově nasazená verze nainstalovala do cílového pracovního prostoru. + +**Požadovaná konfigurace:** + +| Nastavení | Kde | Účel | +| ----------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `TWENTY_DEPLOY_URL` | `env` v `cd.yml` (výchozí `http://localhost:3000`) | Server Twenty, na který se nasazuje. Před prvním použitím to změňte na skutečnou URL vašeho serveru. | +| `TWENTY_DEPLOY_API_KEY` | GitHub repozitář **Settings → Secrets and variables → Actions** | API klíč s oprávněním k nasazení na cílovém serveru. | + + +Výchozí `TWENTY_DEPLOY_URL` `http://localhost:3000` je pouze zástupná hodnota — z runneru hostovaného GitHubem tato adresa nebude dosažitelná. Před povolením CD ji aktualizujte na veřejnou URL vašeho serveru (nebo použijte self-hosted runner s přístupem do sítě). + + +**Spuštění náhledového nasazení z PR:** + +Přidejte k pull requestu štítek `deploy`. Podmínka `if:` v `cd.yml` spustí úlohu pro dané PR s použitím head commitu PR, což vám umožní ověřit změnu na cílovém serveru před sloučením. + +### Připnutí verzí znovupoužitelných akcí + +Obě workflowy odkazují na znovupoužitelné akce na `@main`, takže aktualizace akcí v repozitáři `twentyhq/twenty` se přeberou automaticky. Pokud chcete deterministická sestavení, nahraďte `@main` v každém řádku `uses:` za commit SHA nebo tag vydání. + +## Publikování na npm + +Publikování na npm zajistí, že bude vaše aplikace dohledatelná v Marketplace Twenty. Jakýkoli pracovní prostor Twenty může procházet, instalovat a aktualizovat aplikace z Marketplace přímo z UI. + +### Požadavky + +* Účet na [npm](https://www.npmjs.com) +* Klíčové slovo `twenty-app` ve vašem poli `keywords` v souboru `package.json` (přidejte ho ručně — ve výchozím nastavení není zahrnuto v šabloně `create-twenty-app`) + +```json filename="package.json" +{ + "name": "twenty-app-postcard-sender", + "version": "1.0.0", + "keywords": ["twenty-app"] +} +``` + +### Metadata tržiště + +Konfigurace `defineApplication()` podporuje volitelná pole, která určují, jak se vaše aplikace zobrazuje v tržišti. Použijte `logoUrl` a `screenshots` k odkazování na obrázky ze složky `public/`: + +```ts src/application-config.ts +export default defineApplication({ + universalIdentifier: '...', + displayName: 'My App', + description: 'A great app', + logoUrl: 'public/logo.png', + screenshots: [ + 'public/screenshot-1.png', + 'public/screenshot-2.png', + ], +}); +``` + +Podívejte se na [sekci defineApplication](/l/cs/developers/extend/apps/config/application#marketplace-metadata) na stránce Building Apps pro úplný seznam polí tržiště (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl` atd.). + +#### Doporučené rozměry snímků obrazovky + +Tržiště zobrazuje `screenshots` v pevném kontejneru s poměrem stran `8:5` (například `1600×1000 px`). + + +Snímky obrazovky libovolného poměru stran se zobrazují celé a nikdy se neořezávají, ale cokoli výrazně vyššího nebo užšího než `8:5` bude mít po stranách prázdné pruhy. + + +### Publikování + +```bash filename="Terminal" +yarn twenty app:publish +``` + +Chcete-li publikovat pod konkrétním dist-tagem (např. `beta` nebo `next`): + +```bash filename="Terminal" +yarn twenty app:publish --tag beta +``` + +### Jak funguje objevování v tržišti + +Server Twenty synchronizuje svůj katalog tržiště z registru npm **každou hodinu**. + +Synchronizaci můžete spustit okamžitě místo čekání: + +```bash filename="Terminal" +yarn twenty dev:catalog-sync +# To target a specific remote: +# yarn twenty dev:catalog-sync --remote production +``` + +Metadata zobrazená v tržišti pocházejí z vaší konfigurace `defineApplication()` — z polí jako `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` a `termsUrl`. + + +Pokud vaše aplikace nedefinuje `aboutDescription` v `defineApplication()`, tržiště automaticky použije soubor `README.md` vašeho balíčku z npm jako obsah stránky O aplikaci. To znamená, že můžete spravovat jediný soubor README jak pro npm, tak pro tržiště Twenty. Pokud chcete v tržišti jiný popis, explicitně nastavte `aboutDescription`. + + +### Publikování pomocí CI + +Použijte tento pracovní postup GitHub Actions k automatickému publikování při každém vydání (používá [OIDC](https://docs.npmjs.com/trusted-publishers)): + +```yaml filename=".github/workflows/publish.yml" +name: Publish +on: + release: + types: [published] + +permissions: + contents: read + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: https://registry.npmjs.org + - run: yarn install --immutable + - run: npx twenty dev:build + - run: npm publish --provenance --access public + working-directory: .twenty/output +``` + +Pro jiné systémy CI (GitLab CI, CircleCI atd.) platí stejné tři příkazy: `yarn install`, `yarn twenty dev:build` a poté `npm publish` z `.twenty/output`. + + +**npm provenance** je volitelné, ale doporučené. Publikování s `--provenance` přidá k vašemu záznamu na npm odznak důvěryhodnosti a umožní uživatelům ověřit, že balíček byl sestaven z konkrétního commitu ve veřejné CI pipeline. Pokyny k nastavení najdete v [dokumentaci k npm provenance](https://docs.npmjs.com/generating-provenance-statements). + + +## Instalace aplikací + +Jakmile je aplikace publikována (npm) nebo nasazena (tarball), mohou ji pracovní prostory nainstalovat prostřednictvím uživatelského rozhraní. + +Přejděte na stránku **Nastavení > Aplikace** v Twenty, kde lze procházet a instalovat jak aplikace z tržiště, tak aplikace nasazené jako tarball. + +{/* TODO: add screenshot of the UI when the app is registered */} + +Aplikace můžete nainstalovat také z příkazového řádku: + +```bash filename="Terminal" +yarn twenty app:install +``` + + +Server při instalaci vynucuje verzování semver a zrcadlí pravidla pro nasazení: + +* Instalace stejné verze, která je již nainstalována ve vašem pracovním prostoru, je odmítnuta s chybou `APP_ALREADY_INSTALLED`. +* Instalace nižší verze, než je aktuálně nainstalovaná, je odmítnuta s chybou `CANNOT_DOWNGRADE_APPLICATION`. + +K instalaci novější verze ji nejprve nasaďte nebo publikujte, poté znovu spusťte `yarn twenty app:install`. + diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx new file mode 100644 index 0000000000..b7661be6c4 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/sync-and-recovery.mdx @@ -0,0 +1,111 @@ +--- +title: Synchronizace a obnovení +description: Který příkaz kdy použít, jak číst výstup synchronizace a jak postupovat po jednotlivých krocích při obnově v případě odchýlení lokálních metadat — ještě předtím, než sáhnete k úplnému resetu. +icon: kompas +--- + +Lokální vývoj aplikací se točí kolem **synchronizace**: CLI znovu sestaví váš manifest a server aplikuje pouze rozdíly mezi ním a metadaty, která už jsou ve vašem pracovním prostoru. Tato stránka popisuje, po kterém příkazu sáhnout, jak číst, co synchronizace změnila, a co dělat — v daném pořadí — když lokální stav vypadá nekonzistentně. + +## Jaký příkaz, kdy + + +Pro každodenní lokální iteraci téměř vždy chcete `yarn twenty dev`. Nasazování a publikování slouží k vydávání verzí, **ne** pro lokální vývojovou smyčku. + + +| Chcete… | Příkaz | Poznámky | +| --------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| Iterujte lokálně s živou synchronizací | `yarn twenty dev` | Sleduje vaše soubory a při každé změně spustí synchronizaci. | +| Jednorázová synchronizace a ukončení (CI, skripty, hooky) | `yarn twenty dev --once` | Provede jedno sestavení + synchronizaci a skončí. | +| Náhled změn **bez jejich aplikování** | `yarn twenty dev --once --dry-run` | Spočítá a vypíše rozdíly; nic nezapisuje. | +| Odebrat aplikaci z pracovního prostoru | `yarn twenty app:uninstall` | Přidejte `--yes` pro přeskočení výzvy. | +| Odeslat tarball na server | `yarn twenty app:publish --private` | Vyžaduje **přísně vyšší** verzi v `package.json` — viz [Publikování](/l/cs/developers/extend/apps/operations/publishing). | +| Publikovat na marketplace (npm) | `yarn twenty app:publish` | — | +| Nainstalovat / aktualizovat nasazenou verzi | `yarn twenty app:install` | Nainstaluje aktuálně nasazenou verzi. | +| Vymazat lokální server a začít znovu | `yarn twenty docker:reset` | Smaže **všechna** lokální data — krajní řešení. | + +### Lokální synchronizace nevyžaduje zvýšení verze + +Pravidlo striktně rostoucí `version` (`VERSION_ALREADY_EXISTS` při nasazení, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` při instalaci) platí pro **`app:publish` / `app:install`** — cestu vydání. `yarn twenty dev` synchronizuje váš manifest na místě a nikdy nevyžaduje změnu verze, takže kvůli iteraci nemusíte sahat na `package.json`. Pokud zvyšujete verzi, abyste otestovali lokální změnu, používáte cestu vydání, i když chcete vývojovou smyčku. + +## Čtení výstupu synchronizace + +Každá synchronizace vypíše změny metadat, které aplikovala (nebo by aplikovala s `--dry-run`): + +```text filename="Terminal" +Metadata changes: 2 created, 1 updated, 1 deleted + created objectMetadata rocket + created fieldMetadata timelineActivities + updated fieldMetadata launchedAt + deleted pageLayout legacyTab +✓ Synced +``` + +To je váš první diagnostický nástroj: přesně ukazuje, které objekty, pole a rozložení se změnily, takže si můžete ověřit, že synchronizace udělala to, co jste očekávali, ještě před kontrolou v UI. + +Když synchronizace selže na jedné entitě, chyba uvede problematickou entitu a její `universalIdentifier`, například: + +```text +Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed +``` + +Tento identifikátor použijte k nalezení entity ve vašem manifestu (a případně v pracovním prostoru) místo hádání, která je v konfliktu. + +## Náhled změn (dry run) + +`yarn twenty dev --once --dry-run` sestaví váš manifest, požádá server o migrační plán a vypíše ho — aniž by cokoli aplikoval. Je to bezpečný způsob, jak si předem zodpovědět otázku „co by tato synchronizace změnila?“ ještě předtím, než se k ní zavážete. + +```bash filename="Terminal" +yarn twenty dev --once --dry-run +``` + +```text filename="Terminal" +Building manifest... +Computing metadata diff (dry run, nothing will be applied)... +Metadata changes: 1 created, 1 updated + created fieldMetadata timelineActivities + updated objectMetadata rocket +✓ Dry run complete for My App — no changes were applied +``` + +Spuštění nanečisto: + +* **Nic nezapisuje** — žádná migrace metadat, žádná aktualizace záznamu aplikace, žádné změny výchozí role / karty a žádná generace API klienta. +* Vrací **stejný diff**, jaký by aplikovala skutečná synchronizace, takže můžete předem zkontrolovat vytvořené / aktualizované / smazané entity. +* Je užitečný před rizikovou změnou, při kontrole změny vygenerované pomocí AI nebo ve skriptu, který má selhat, pokud se má provést neočekávaná změna. + + +Režim dry run zobrazuje pouze náhled změn **metadat** a vyžaduje, aby byla aplikace alespoň jednou synchronizovaná (aby o ní pracovní prostor věděl). Pokud jej spustíte proti aplikaci, která nikdy nebyla synchronizovaná, server ohlásí, že aplikace není nainstalovaná — nejprve jednou spusťte `yarn twenty dev`. + + +## Postup obnovy + +Když lokální metadata vypadají špatně, postupujte v tomto pořadí a zastavte se, jakmile se problém vyřeší. Každý další krok je rušivější než ten předchozí. + +1. **Znovu synchronizujte.** Znovu spusťte `yarn twenty dev --once`. Synchronizace jsou idempotentní — znovu spuštěný čistý manifest je bezpečný a často vyřeší přechodný problém. +2. **Prohlédněte si plán.** Spusťte `yarn twenty dev --once --dry-run`, abyste přesně viděli, co chce další synchronizace změnit, aniž by to aplikovala. +3. **Přečtěte si pojmenovanou chybu.** Pokud synchronizace selže, poznamenejte si typ metadat a `universalIdentifier` ve zprávě (viz výše) a tuto entitu najděte ve svém manifestu. Konflikt obvykle ukazuje na duplicitní nebo znovu použitý identifikátor. +4. **Odinstalujte a znovu nainstalujte.** `yarn twenty app:uninstall`, poté znovu synchronizujte (`yarn twenty dev`). Tím znovu vybudujete metadata aplikace z čistého stavu, zatímco zbytek vašeho pracovního prostoru zůstane nedotčený. +5. **Úplný reset (krajní řešení).** `yarn twenty docker:reset`, poté znovu naplňte data a synchronizujte. + + +`yarn twenty docker:reset` smaže **veškerá** data ve vaší lokální instanci — každý pracovní prostor, záznam i aplikaci. Použijte jej až tehdy, když selžou předchozí kroky. + + + +Nastala chyba v metadatech? Prosíme, [vytvořte issue](https://github.com/twentyhq/twenty/issues/new/choose) a přiložte chybovou zprávu selhané migrace (s typem metadat a `universalIdentifier`), výstup `Metadata changes` ze synchronizace a příkazy, které jste spustili. + + +## Vyhněte se souběžným synchronizacím v jednom pracovním prostoru + +Synchronizace aplikuje migrace metadat. Spouštění několika synchronizačních, nasazovacích nebo instalačních operací proti **stejnému pracovnímu prostoru ve stejnou dobu** — například z víc terminálů nebo od více AI agentů iterujících paralelně — může tyto migrace prokládat a zanechat metadata v částečně aplikovaném stavu. + +Server serializuje synchronizace pro každý pracovní prostor, aby tomu zabránil, ale přesto byste citlivé operace s metadaty měli směrovat přes **jeden jediný** proces místo toho, abyste je spouštěli souběžně. Pokud orchestrujete vývoj s více agenty, směrujte jejich volání sync/deploy/install přes jednu frontu, aby vždy běžel jen jeden proces. + +## Rozlišení typů selhání + +Když se něco pokazí, diff metadat a pojmenované chyby vám umožní lokalizovat selhání: + +* **Chyba sestavení manifestu** — CLI selže ještě před synchronizací (`MANIFEST_BUILD_FAILED`, `TYPECHECK_FAILED`); opravte zdrojový kód své aplikace. +* **Chyba synchronizace / migrace** — sestavení proběhne úspěšně, ale aplikování diffu selže a uvede entitu a `universalIdentifier`; opravte konfliktní metadata. +* **Chyba za běhu aplikačního kódu** — synchronizace proběhne úspěšně, ale vaše logické funkce nebo komponenty se za běhu chovají nesprávně; zkontrolujte [protokoly funkcí](/l/cs/developers/extend/apps/operations/cli). +* **Lokální stav instance** — neplatí nic z výše uvedeného a pracovní prostor stále vypadá chybně; pokračujte dolů po žebříčku obnovy. diff --git a/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx new file mode 100644 index 0000000000..e9b454f9c1 --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/extend/apps/operations/testing.mdx @@ -0,0 +1,301 @@ +--- +title: Testování +description: Nastavení Vitestu, integrační testy proti reálnému serveru Twenty, kontrola typů a CI s GitHub Actions. +icon: flask +--- + +SDK poskytuje programová rozhraní, která vám umožní z testovacího kódu aplikaci sestavit, nasadit, nainstalovat a odinstalovat. V kombinaci s [Vitest](https://vitest.dev/) a typovanými klienty API můžete psát integrační testy, které ověří, že vaše aplikace funguje end-to-end proti reálnému serveru Twenty. + +## Používání balíčků npm + +Ve své aplikaci můžete nainstalovat a používat libovolný balíček npm. Logické funkce i frontendové komponenty se bundlují pomocí [esbuild](https://esbuild.github.io/), který vloží všechny závislosti přímo do výstupu — za běhu nejsou potřeba žádné `node_modules`. + +### Instalace balíčku + +```bash filename="Terminal" +yarn add axios +``` + +Poté jej importujte ve svém kódu: + +```ts src/logic-functions/fetch-data.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import axios from 'axios'; + +const handler = async (): Promise => { + const { data } = await axios.get('https://api.example.com/data'); + + return { data }; +}; + +export default defineLogicFunction({ + universalIdentifier: '...', + name: 'fetch-data', + description: 'Fetches data from an external API', + timeoutSeconds: 10, + handler, +}); +``` + +Stejně to funguje i pro frontendové komponenty: + +```tsx src/front-components/chart.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { format } from 'date-fns'; + +const DateWidget = () => { + return

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

; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'date-widget', + component: DateWidget, +}); +``` + +### Jak funguje bundlování + +Krok sestavení používá esbuild k vytvoření jediného samostatného souboru pro každou logickou funkci a každou frontendovou komponentu. Všechny importované balíčky jsou vloženy přímo do bundlu. + +**Logické funkce** běží v prostředí Node.js. Vestavěné moduly Node (`fs`, `path`, `crypto`, `http` atd.) jsou k dispozici a není je třeba instalovat. + +**Frontendové komponenty** běží ve Web Workeru. Vestavěné moduly Node nejsou k dispozici — pouze prohlížečová API a balíčky npm, které fungují v prohlížečovém prostředí. + +V obou prostředích jsou jako předpřipravené moduly k dispozici `twenty-client-sdk/core` a `twenty-client-sdk/metadata` — nejsou součástí bundlu, ale server je za běhu načítá. + +## Nastavení + +Vygenerovaná aplikace již obsahuje Vitest. Pokud to nastavujete ručně, nainstalujte závislosti: + +```bash filename="Terminal" +yarn add -D vitest vite-tsconfig-paths +``` + +Vytvořte `vitest.config.ts` v kořeni vaší aplikace: + +```ts vitest.config.ts +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + tsconfigPaths({ + projects: ['tsconfig.spec.json'], + ignoreConfigErrors: true, + }), + ], + test: { + testTimeout: 120_000, + hookTimeout: 120_000, + include: ['src/**/*.integration-test.ts'], + setupFiles: ['src/__tests__/setup-test.ts'], + env: { + TWENTY_API_URL: 'http://localhost:2020', + TWENTY_API_KEY: 'your-api-key', + }, + }, +}); +``` + +Vytvořte soubor nastavení, který před spuštěním testů ověří dostupnost serveru: + +```ts src/__tests__/setup-test.ts +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { beforeAll } from 'vitest'; + +const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020'; +const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test'); + +beforeAll(async () => { + // Verify the server is running + const response = await fetch(`${TWENTY_API_URL}/healthz`); + + if (!response.ok) { + throw new Error( + `Twenty server is not reachable at ${TWENTY_API_URL}. ` + + 'Start the server before running integration tests.', + ); + } + + // Write a temporary config for the SDK + fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true }); + + fs.writeFileSync( + path.join(TEST_CONFIG_DIR, 'config.json'), + JSON.stringify({ + remotes: { + local: { + apiUrl: process.env.TWENTY_API_URL, + apiKey: process.env.TWENTY_API_KEY, + }, + }, + defaultRemote: 'local', + }, null, 2), + ); +}); +``` + +## Programová rozhraní SDK + +Subcesta `twenty-sdk/cli` exportuje funkce, které můžete volat přímo z testovacího kódu: + +| Funkce | Popis | +| -------------- | ----------------------------------------------------- | +| `appBuild` | Sestaví aplikaci a volitelně zabalí tarball | +| `appDeploy` | Nahraje tarball na server | +| `appInstall` | Nainstaluje aplikaci do aktivního pracovního prostoru | +| `appUninstall` | Odinstaluje aplikaci z aktivního pracovního prostoru | + +Každá funkce vrací objekt výsledku se `success: boolean` a buď `data`, nebo `error`. + +## Psání integračního testu + +Zde je kompletní příklad, který aplikaci sestaví, nasadí a nainstaluje a poté ověří, že se objeví v pracovním prostoru: + +```ts src/__tests__/app-install.integration-test.ts +import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config'; +import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli'; +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +const APP_PATH = process.cwd(); + +describe('App installation', () => { + beforeAll(async () => { + const buildResult = await appBuild({ + appPath: APP_PATH, + tarball: true, + onProgress: (message: string) => console.log(`[build] ${message}`), + }); + + if (!buildResult.success) { + throw new Error(`Build failed: ${buildResult.error?.message}`); + } + + const deployResult = await appDeploy({ + tarballPath: buildResult.data.tarballPath!, + onProgress: (message: string) => console.log(`[deploy] ${message}`), + }); + + if (!deployResult.success) { + throw new Error(`Deploy failed: ${deployResult.error?.message}`); + } + + const installResult = await appInstall({ appPath: APP_PATH }); + + if (!installResult.success) { + throw new Error(`Install failed: ${installResult.error?.message}`); + } + }); + + afterAll(async () => { + await appUninstall({ appPath: APP_PATH }); + }); + + it('should find the installed app in the workspace', async () => { + const metadataClient = new MetadataApiClient(); + + const result = await metadataClient.query({ + findManyApplications: { + id: true, + name: true, + universalIdentifier: true, + }, + }); + + const installedApp = result.findManyApplications.find( + (app: { universalIdentifier: string }) => + app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER, + ); + + expect(installedApp).toBeDefined(); + }); +}); +``` + +## Spuštění testů + +Ujistěte se, že běží váš lokální server Twenty, a poté: + +```bash filename="Terminal" +yarn test +``` + +Nebo v režimu watch během vývoje: + +```bash filename="Terminal" +yarn test:watch +``` + +## Kontrola typů + +Kontrolu typů můžete spustit i na vaší aplikaci bez spuštění testů: + +```bash filename="Terminal" +yarn twenty dev:typecheck +``` + +Spustí se `tsc --noEmit` a nahlásí se případné chyby typů. + +## CI s GitHub Actions + +Generátor kostry vytvoří připravený k použití workflow GitHub Actions v `.github/workflows/ci.yml`. Automaticky spouští integrační testy při každém pushi do `main` a u pull requestů. + +Workflow: + +1. Načte váš kód (checkout). +2. Spustí dočasný server Twenty pomocí akce `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` +3. Nainstaluje závislosti pomocí `yarn install --immutable` +4. Spustí `yarn test` s proměnnými `TWENTY_API_URL` a `TWENTY_API_KEY` vloženými z výstupů akce + +```yaml .github/workflows/ci.yml +name: CI + +on: + push: + branches: + - main + pull_request: {} + +env: + TWENTY_VERSION: latest + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Spawn Twenty instance + id: twenty + uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main + with: + twenty-version: ${{ env.TWENTY_VERSION }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable Corepack + run: corepack enable + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + + - name: Run integration tests + run: yarn test + env: + TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} + TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }} +``` + +Není potřeba konfigurovat žádné secrets — akce `spawn-twenty-docker-image` spustí dočasný server Twenty přímo v runneru a vypíše podrobnosti připojení. Secret `GITHUB_TOKEN` je poskytován GitHubem automaticky. + +Chcete-li připnout konkrétní verzi Twenty místo `latest`, změňte proměnnou prostředí `TWENTY_VERSION` na začátku workflow. diff --git a/packages/twenty-docs/l/cs/developers/extend/capabilities/apis.mdx b/packages/twenty-docs/l/cs/developers/extend/capabilities/apis.mdx index e9f738487e..b6406f571f 100644 --- a/packages/twenty-docs/l/cs/developers/extend/capabilities/apis.mdx +++ b/packages/twenty-docs/l/cs/developers/extend/capabilities/apis.mdx @@ -88,7 +88,7 @@ Váš klíč API poskytuje přístup k citlivým datům. Nesdílejte ho s nedův Pro vyšší bezpečnost přiřaďte konkrétní roli, abyste omezili přístup: -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Klikněte na roli, kterou chcete přiřadit 3. Otevřete záložku **Přiřazení** 4. V části **API Keys** klikněte na **+ Přiřadit ke klíči API** diff --git a/packages/twenty-docs/l/cs/developers/self-host/capabilities/docker-compose.mdx b/packages/twenty-docs/l/cs/developers/self-host/capabilities/docker-compose.mdx index 76b0fbe650..e7a3f7c46d 100644 --- a/packages/twenty-docs/l/cs/developers/self-host/capabilities/docker-compose.mdx +++ b/packages/twenty-docs/l/cs/developers/self-host/capabilities/docker-compose.mdx @@ -51,7 +51,7 @@ Postupujte podle těchto kroků pro ruční nastavení. curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example ``` -2. **Vygenerujte tajné tokeny** +2. **Vygenerujte šifrovací klíč** Spusťte následující příkaz k generování jedinečného náhodného řetězce: @@ -59,16 +59,18 @@ Postupujte podle těchto kroků pro ruční nastavení. openssl rand -base64 32 ``` - **Důležité:** Udržujte tuto hodnotu v tajnosti / nesdílejte ji. + **Důležité:** Udržujte tuto hodnotu v tajnosti / nesdílejte ji. Ztráta `ENCRYPTION_KEY` znamená ztrátu přístupu ke všem tajným údajům uloženým v databázi (OAuth tokeny, aplikační proměnné, TOTP tajemství atd.). 3. **Aktualizujte `.env` soubor** Nahraďte místoblokovou hodnotu ve svém .env souboru vygenerovaným tokenem: ```ini - APP_SECRET=první_náhodný_řetězec + ENCRYPTION_KEY=random_string ``` + Podívejte se na [průvodce rotací klíče](/l/cs/developers/self-host/capabilities/key-rotation) pro pokyny, jak jej rotovat bez prostojů. + 4. **Nastavte Heslo pro Postgres** Aktualizujte hodnotu `PG_DATABASE_PASSWORD` ve vašem .env souboru silným heslem bez speciálních znaků. diff --git a/packages/twenty-docs/l/cs/developers/self-host/capabilities/key-rotation.mdx b/packages/twenty-docs/l/cs/developers/self-host/capabilities/key-rotation.mdx new file mode 100644 index 0000000000..3b48efc2bf --- /dev/null +++ b/packages/twenty-docs/l/cs/developers/self-host/capabilities/key-rotation.mdx @@ -0,0 +1,60 @@ +--- +title: Rotace klíčů +icon: rotate +--- + +Twenty má dvě nezávislé rodiny klíčů: + +* **Klíče pro podepisování JWT** — asymetrické páry klíčů ES256 (označené `kid`), uložené v `core."signingKey"`, používané k podepisování a ověřování přístupových/obnovovacích tokenů. +* **Šifrovací klíč pro data v klidu** — `ENCRYPTION_KEY`, používaný k šifrování OAuth tokenů, aplikačních proměnných, soukromých klíčů podepisovacích klíčů, citlivých konfiguračních hodnot a TOTP tajemství uvnitř obálky `enc:v2:`. + +`APP_SECRET` je starší tajný klíč ponechaný kvůli zpětné kompatibilitě: pokud `ENCRYPTION_KEY` není nastaven, slouží jako záložní řešení pro šifrování dat v klidu i pro soubory cookie relace a stále ověřuje dříve existující HS256 přístupové tokeny. Bude označen jako zastaralý (deprecated). + +## Klíče pro podepisování JWT + +Každý klíč nese `publicKey` (uchovávaný neomezeně dlouho, aby mohl ověřovat dříve vydané tokeny), zašifrovaný `privateKey` (používaný pouze tehdy, když je klíč aktuální), příznak `isCurrent` (vždy přesně jeden záznam) a volitelné `revokedAt`. + +### Rotace aktuálního klíče + +Nastavte `SIGNING_KEY_ROTATION_DAYS`, chcete-li funkci povolit: denní cron poté vydá nový klíč jako aktuální, jakmile je stávající starší než zadaný práh. Předchozí klíče *nejsou* odvolány, takže tokeny pod nimi podepsané se dál úspěšně ověřují. Ponechte proměnnou nenastavenou, abyste deaktivovali automatickou rotaci. + +Automatická rotace je k dispozici od verze v2.6+. + +### Odvolání klíče (pouze při úniku / v nouzi) + +**Settings → Admin Panel → Signing keys → Revoke** na neaktuálním řádku. Smaže zašifrovaný privátní materiál, nastaví `revokedAt` a odmítne každý existující token podepsaný pod daným `kid`. + +## Rotace `ENCRYPTION_KEY` + +Příkaz `secret-encryption:rotate` popsaný níže je k dispozici od verze v2.6+. + +Každá zašifrovaná hodnota je zabalena jako `enc:v2:\:\`, kde `\` je 8místný hexadecimální prefix odvozený ze surového klíče. Rotace probíhá online a je možné ji kdykoli znovu spustit. + +1. **Vygenerujte nový klíč**: `openssl rand -base64 32`. + +2. **Nakonfigurujte oba klíče vedle sebe** v `.env` a poté restartujte: + ```ini + ENCRYPTION_KEY=NEW_VALUE + FALLBACK_ENCRYPTION_KEY=OLD_VALUE + ``` + Nové zápisy používají nový klíč, existující řádky se stále dešifrují pomocí záložního klíče. + +3. **Znovu zašifrujte existující řádky**: + + ```bash + docker exec -it {server_container} yarn command:prod secret-encryption:rotate + ``` + + Příkaz prochází šest míst (`connected-account-tokens`, `application-variable`, `application-registration-variable`, `signing-key-private-keys`, `sensitive-config-storage`, `totp-secrets`). SQL filtr přeskočí řádky, které už jsou na novém `\`, takže příkaz je idempotentní: přerušte ho a znovu spusťte podle potřeby. Ukončí se s nenulovým kódem, pokud jakýkoli řádek selže — spusťte znovu pro opakování. + + | Přepínač | Popis | + | ---------------------------------------- | -------------------------------------------------------- | + | `-s, --site \` | Omezí běh na jedno místo (site). | + | `-b, --batch-size \` | Počet řádků v dávce (výchozí `200`, maximum `5000`). | + | `-d, --dry-run` | Dešifruje + znovu zašifruje v paměti, přeskočí `UPDATE`. | + +4. **Odstraňte záložní klíč** jakmile `--dry-run` ukáže, že nezbývají žádné řádky: odeberte `FALLBACK_ENCRYPTION_KEY` a restartujte. + +## Podpora staršího `APP_SECRET` + +Starší instance, které nikdy nenastavily `ENCRYPTION_KEY`, používají `APP_SECRET` jako šifrovací klíč pro data v klidu (a jako tajemství pro soubor cookie relace, odvozené z něj). Tato cesta je zachována kvůli zpětné kompatibilitě, ale je **zastaralá (deprecated)** — nastavte vyhrazený `ENCRYPTION_KEY` a podle výše uvedeného postupu rotace proveďte migraci pryč od něj. Samotný `APP_SECRET` zůstává v používání pro ověřování starších HS256 přístupových tokenů. diff --git a/packages/twenty-docs/l/cs/developers/self-host/capabilities/setup.mdx b/packages/twenty-docs/l/cs/developers/self-host/capabilities/setup.mdx index 7be493c838..846407eef0 100644 --- a/packages/twenty-docs/l/cs/developers/self-host/capabilities/setup.mdx +++ b/packages/twenty-docs/l/cs/developers/self-host/capabilities/setup.mdx @@ -43,11 +43,26 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # výchozí Každá proměnná je dokumentována s popisy ve vašem administračním panelu v sekci **Nastavení → Admin panel → Konfigurační proměnné**. -Některá nastavení infrastruktury, jako připojení k databázi (`PG_DATABASE_URL`), URL serveru (`SERVER_URL`) a tajemství aplikace (`APP_SECRET`), lze konfigurovat pouze prostřednictvím souboru `.env`. +Některá nastavení infrastruktury, jako připojení k databázi (`PG_DATABASE_URL`), URL serveru (`SERVER_URL`) a tajné klíče (`ENCRYPTION_KEY`, `FALLBACK_ENCRYPTION_KEY`), lze konfigurovat pouze prostřednictvím souboru `.env`. [Kompletní technická referenční příručka →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts) +## Šifrovací klíče + +Twenty používá dva šifrovací klíče dostupné pouze přes proměnné prostředí: + +| Proměnná | Účel | Povinné | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | +| `ENCRYPTION_KEY` | Primární klíč používaný k šifrování tajných hodnot „v klidu“ (tokeny OAuth, aplikační proměnné, privátní klíče podepisovacích klíčů, TOTP tajné kódy, citlivé konfigurační hodnoty). | Ano pro nové instalace (starší instalace se mohou místo toho spoléhat na `APP_SECRET` — viz níže) | +| `FALLBACK_ENCRYPTION_KEY` | Klíč používaný pouze k ověřování. Během rotace se nastavuje na *předchozí* hodnotu `ENCRYPTION_KEY`, aby bylo možné dešifrovat stávající řádky. | Pouze během rotace | + +Kvůli zpětné kompatibilitě, pokud `ENCRYPTION_KEY` není nastaven, Twenty používá pro šifrování „v klidu“ záložně `APP_SECRET`, což odpovídá původnímu chování starších nasazení. Nové instalace by vždy měly nastavit dedikovaný `ENCRYPTION_KEY`. + +Hodnoty vygenerujte pomocí `openssl rand -base64 32` a uložte je na bezpečné místo (správce tajných klíčů, zapečetěná konfigurace apod.). Ztráta `ENCRYPTION_KEY` znamená ztrátu přístupu ke všem tajným hodnotám uloženým v databázi. + +Jak provést rotaci `ENCRYPTION_KEY` bez výpadku, najdete v [průvodci rotací klíče](/l/cs/developers/self-host/capabilities/key-rotation). + ## 2. Konfigurace pouze přes prostředí ```bash diff --git a/packages/twenty-docs/l/cs/developers/self-host/capabilities/upgrade-guide.mdx b/packages/twenty-docs/l/cs/developers/self-host/capabilities/upgrade-guide.mdx index 9f2b4a2b0b..0bc8cd6f05 100644 --- a/packages/twenty-docs/l/cs/developers/self-host/capabilities/upgrade-guide.mdx +++ b/packages/twenty-docs/l/cs/developers/self-host/capabilities/upgrade-guide.mdx @@ -31,6 +31,18 @@ Počínaje **v1.22** podporuje Twenty upgrade napříč verzemi. Můžete přej Například přechod z v1.22 přímo na v2.0 je plně podporován. +## Aktualizace na v2.5+ — obálka pro šifrování dat v klidu + +Od **v2.5** ukládá Twenty tajné údaje v klidu (OAuth tokeny, aplikační proměnné, privátní klíče pro podepisování, citlivé konfigurační hodnoty, TOTP tajemství) do verzované obálky `enc:v2:`, která je šifrovaná pomocí `ENCRYPTION_KEY` (nebo `APP_SECRET`, pokud není `ENCRYPTION_KEY` nastavený). + +Při prvním startu na v2.5 se spustí pomalé aktualizační příkazy, které **zpětně doplní** stávající řádky do nové obálky. Jsou idempotentní — při přerušení a opětovném spuštění server pokračuje tam, kde skončil — ale u velkých databází to může chvíli trvat. Postup můžete sledovat pomocí `upgrade:status`. + +Měli byste nastavit vyhrazený `ENCRYPTION_KEY` **před** upgradem na v2.5, aby zpětné doplňování od začátku zapisovalo řádky pod tímto klíčem. Změna klíčů po dokončení doplnění vyžaduje [rotaci](/l/cs/developers/self-host/capabilities/key-rotation). + +## Rotace tajemství a podepisovacích klíčů + +Pro běžné provozní úkony, jako je rotace `ENCRYPTION_KEY`, rotace podepisovacího klíče JWT nebo zneplatnění uniklého podepisovacího klíče, viz samostatný [průvodce rotací klíčů](/l/cs/developers/self-host/capabilities/key-rotation). + ## Kontrola stavu upgradu Příkaz `upgrade:status` vám umožní zkontrolovat aktuální stav vaší instance a migrací pracovních prostorů. Je užitečný pro ladění problémů s upgradem nebo při podávání požadavku na podporu. diff --git a/packages/twenty-docs/l/cs/navigation.json b/packages/twenty-docs/l/cs/navigation.json index 0e2312bdee..796d5edacb 100644 --- a/packages/twenty-docs/l/cs/navigation.json +++ b/packages/twenty-docs/l/cs/navigation.json @@ -155,7 +155,27 @@ "label": "Přehled" }, "apps": { - "label": "Aplikace" + "label": "Aplikace", + "groups": { + "appsGettingStarted": { + "label": "Začínáme" + }, + "appsConfig": { + "label": "Konfigurace" + }, + "appsData": { + "label": "Data" + }, + "appsLogic": { + "label": "Logika" + }, + "appsLayout": { + "label": "Rozvržení" + }, + "appsOperations": { + "label": "Operace" + } + } }, "api": { "label": "API" diff --git a/packages/twenty-docs/l/cs/twenty-ui/input/text.mdx b/packages/twenty-docs/l/cs/twenty-ui/input/text.mdx index c323d68ddb..a6e7e6ce66 100644 --- a/packages/twenty-docs/l/cs/twenty-ui/input/text.mdx +++ b/packages/twenty-docs/l/cs/twenty-ui/input/text.mdx @@ -39,7 +39,7 @@ export const MyComponent = () => { /> ); }; -},{ + ``` diff --git a/packages/twenty-docs/l/cs/user-guide/ai/capabilities/mcp.mdx b/packages/twenty-docs/l/cs/user-guide/ai/capabilities/mcp.mdx index c1d971065a..1e1a45a055 100644 --- a/packages/twenty-docs/l/cs/user-guide/ai/capabilities/mcp.mdx +++ b/packages/twenty-docs/l/cs/user-guide/ai/capabilities/mcp.mdx @@ -103,11 +103,10 @@ Požádejte svého AI asistenta, aby pracoval s vaším CRM: Po připojení server MCP zpřístupní nástroje, které odpovídají rozhraní Twenty API. Doporučený pracovní postup je: -1. **`get_tool_catalog`** — zjistit všechny dostupné nástroje -2. **`learn_tools`** — získat vstupní schéma pro konkrétní nástroje -3. **`execute_tool`** — spustit nástroj +1. **`learn_tools`** — získat vstupní schéma pro konkrétní nástroje +2. **`execute_tool`** — spustit nástroj -Není potřeba si pamatovat názvy nástrojů. Zeptejte se svého AI asistenta, co umí, a automaticky zavolá `get_tool_catalog`. +Není potřeba si pamatovat názvy nástrojů. Zeptejte se svého AI asistenta, co umí, a automaticky zavolá `learn_tools`. ## Oprávnění diff --git a/packages/twenty-docs/l/cs/user-guide/ai/capabilities/permissions-access-control.mdx b/packages/twenty-docs/l/cs/user-guide/ai/capabilities/permissions-access-control.mdx index 915c707a18..ce678b9ade 100644 --- a/packages/twenty-docs/l/cs/user-guide/ai/capabilities/permissions-access-control.mdx +++ b/packages/twenty-docs/l/cs/user-guide/ai/capabilities/permissions-access-control.mdx @@ -9,7 +9,7 @@ Agenti AI respektují vaši stávající strukturu oprávnění. To je obzvláš ## Přiřadit roli agentovi AI -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Klikněte na roli, kterou chcete přiřadit 3. Otevřete záložku **Přiřazení** 4. V části **Agenti AI** klikněte na **+ Přiřadit agentovi AI** diff --git a/packages/twenty-docs/l/cs/user-guide/ai/how-tos/ai-faq.mdx b/packages/twenty-docs/l/cs/user-guide/ai/how-tos/ai-faq.mdx index 3811ecb18d..e5f36562c4 100644 --- a/packages/twenty-docs/l/cs/user-guide/ai/how-tos/ai-faq.mdx +++ b/packages/twenty-docs/l/cs/user-guide/ai/how-tos/ai-faq.mdx @@ -17,7 +17,7 @@ description: Často kladené otázky k funkcím AI v Twenty.
- AI agenti budou fungovat v rámci systému oprávnění. V části **Nastavení → Role** můžete AI agentům přiřadit konkrétní role, čímž získáte plnou kontrolu nad tím, k jakým datům mají přístup a jaké akce mohou provádět. + AI agenti budou fungovat v rámci systému oprávnění. V části **Nastavení → Členové → Role** můžete AI agentům přiřadit konkrétní role, čímž získáte plnou kontrolu nad tím, k jakým datům mají přístup a jaké akce mohou provádět. diff --git a/packages/twenty-docs/l/cs/user-guide/ai/overview.mdx b/packages/twenty-docs/l/cs/user-guide/ai/overview.mdx index 37c61d0b37..162ff64cc2 100644 --- a/packages/twenty-docs/l/cs/user-guide/ai/overview.mdx +++ b/packages/twenty-docs/l/cs/user-guide/ai/overview.mdx @@ -48,7 +48,7 @@ Rozšiřte své pracovní postupy o akce poháněné AI a autonomní agenty. AI agenti budou spravováni prostřednictvím stávajícího systému oprávnění: -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Nastavte, k jakým datům může mít každý AI agent přístup 3. Nastavte oprávnění pro čtení/zápis pro jednotlivé objekty diff --git a/packages/twenty-docs/l/cs/user-guide/billing/capabilities/pricing-plans.mdx b/packages/twenty-docs/l/cs/user-guide/billing/capabilities/pricing-plans.mdx index 17a7eeb54d..88a2f743f9 100644 --- a/packages/twenty-docs/l/cs/user-guide/billing/capabilities/pricing-plans.mdx +++ b/packages/twenty-docs/l/cs/user-guide/billing/capabilities/pricing-plans.mdx @@ -19,7 +19,7 @@ Pro týmy připravené škálovat: * Standardní podpora -Premium features (SSO, row-level permissions and AI usage data) are not included in the Pro plan. +Prémiové funkce (SSO, oprávnění na úrovni řádků a údaje o využití AI) nejsou součástí plánu Pro. ### Organizace (Cloud) @@ -27,7 +27,7 @@ Premium features (SSO, row-level permissions and AI usage data) are not included Pro větší týmy s pokročilými potřebami: * Vše z plánu Pro -* **Premium features**: SSO integration, row-level permissions and AI usage data +* **Prémiové funkce**: integrace SSO, oprávnění na úrovni řádků a údaje o využití AI * Prioritní podpora ## Plány pro self‑hosting @@ -45,7 +45,7 @@ Hostujte Twenty na vlastní infrastruktuře bez poplatků: Pro týmy, které při self‑hostingu potřebují prémiové funkce: * Všechny funkce plánu Pro -* **Premium features**: SSO integration, row-level permissions and AI usage data +* **Prémiové funkce**: integrace SSO, oprávnění na úrovni řádků a údaje o využití AI * Podpora týmu Twenty * Není vyžadováno zveřejnit vlastní kód jako open‑source před distribucí @@ -55,7 +55,7 @@ Prémiové funkce jsou dostupné pouze v plánech Organizace (Cloud nebo self‑ * **Integrace SSO**: jednotné přihlášení s vaším poskytovatelem identity * **Oprávnění na úrovni řádků**: jemně odstupňované řízení přístupu na úrovni záznamu -* **AI usage data**: Track AI consumption across the workspace +* **Údaje o využití AI**: Sledujte spotřebu AI napříč pracovním prostorem ## Přepínání plánů @@ -79,14 +79,14 @@ Chcete-li přejít na nižší plán, kontaktujte podporu. Chcete-li přepnout zpět na měsíční fakturaci, kontaktujte podporu. -## Obtain an Enterprise Key for Organization (Self-Hosted) +## Získejte klíč Enterprise pro Organization (Self-Hosted) -To use the Organization (Self-Hosted) plan, you need to obtain an Enterprise key: +Chcete-li používat plán Organization (Self-Hosted), musíte získat klíč Enterprise: -1. Go to **Settings → Admin Panel → Enterprise** +1. Jděte na **Nastavení → Admin panel → Enterprise** -Enterprise key +Klíč Enterprise -2. Click **Get Enterprise Key** -3. When you are redirected to Stripe, enter your payment details and confirm -4. When your Enterprise key is displayed, paste it into the Enterprise settings page and activate the Organization license +2. Klikněte na **Získat klíč Enterprise** +3. Po přesměrování na Stripe zadejte platební údaje a potvrďte +4. Jakmile se zobrazí váš klíč Enterprise, vložte jej na stránku nastavení Enterprise a aktivujte licenci Organization diff --git a/packages/twenty-docs/l/cs/user-guide/calendar-emails/overview.mdx b/packages/twenty-docs/l/cs/user-guide/calendar-emails/overview.mdx index 5c2cc77b01..db08603d89 100644 --- a/packages/twenty-docs/l/cs/user-guide/calendar-emails/overview.mdx +++ b/packages/twenty-docs/l/cs/user-guide/calendar-emails/overview.mdx @@ -25,15 +25,25 @@ description: Připojte své účty e-mailu a kalendáře k Twenty. 6. Nastavte synchronizaci kalendáře (viditelnost, automatické vytváření) → klikněte na **Přidat účet** 7. Vaše e-maily a události v kalendáři se začnou synchronizovat automaticky -### Nastavení SMTP/CalDAV (Další Poskytovatelé) +### Nastavení IMAP/SMTP/CalDAV (Další poskytovatelé) Pro další poskytovatele emailu a kalendáře: 1. Přejděte na **Nastavení → Účty** -2. Nakonfigurujte nastavení SMTP pro email +2. Nakonfigurujte nastavení IMAP pro synchronizaci příchozích e-mailů a nastavení SMTP pro odesílání e-mailů 3. Nakonfigurujte nastavení CalDAV pro kalendář 4. Otestujte připojení + +**Vlastní hostování v oddělené (air-gapped) nebo interní síti**: Ve výchozím nastavení Twenty odmítá odchozí připojení na soukromé/interní IP adresy (ochrana proti SSRF). Pokud váš poštovní nebo kalendářový server běží na místní/soukromé IP adrese (např. on-premise server v síti LAN), připojení k němu budou blokována. Abyste je povolili, nastavte na serveru následující proměnnou prostředí: + +``` +OUTBOUND_HTTP_SAFE_MODE_ENABLED=false +``` + +Tímto deaktivujete bezpečný režim pro **všechny** odchozí požadavky (HTTP akce v pracovních postupech, webhooky a IMAP/SMTP/CalDAV připojení), takže jej nastavujte pouze v důvěryhodných, izolovaných sítích, kde není ochrana proti SSRF potřeba. + + ### Více Poštovních Schránek * **Neomezené Účty**: Připojte více emailových účtů na uživatele @@ -59,10 +69,19 @@ Vyberte různé úrovně viditelnosti pro vaše emaily: * **Deaktivováno**: Žádné automatické vytváření kontaktů * **Pro zprávy odeslané a přijaté**: Vytvářejte kontakty pro všechny externí emailové interakce * **Pouze pro odeslané zprávy**: Vytvářejte kontakty pouze pro emaily, které posíláte -* **Poznámka**: Interní emaily (stejná doména) nejsou synchronizovány kvůli zachování soukromí +* **Poznámka**: Ve výchozím nastavení se interní e-maily (kde všichni účastníci sdílejí vaši doménu) nesynchronizují z důvodu ochrany soukromí Když je povoleno, kontakty se automaticky propojí se svými záznamy společnosti podle jejich e-mailové domény. Pokud společnost ještě neexistuje, Twenty ji pro vás vytvoří. + +**Synchronizace interních e-mailů**: Chování „interní e-maily se nesynchronizují“ je výchozí, ale lze jej vypnout. Přepínač se nachází v pokročilém nastavení: +1. Otevřete **Nastavení** a v dolní části stránky s nastavením zapněte přepínač **Pokročilé** +2. Přejděte na **Obecné → Zabezpečení** +3. Zapněte přepínač **Synchronizovat interní e-maily**, aby byly zahrnuty e-maily, kde všichni účastníci sdílejí stejnou doménu + +Jedná se o nastavení pro celý workspace (užitečné pro univerzity nebo organizace se sdílenou doménou). + + ### Nastavte, které e-maily se budou synchronizovat pomocí Výběru Složek Zpráv Ovládejte, které emailové složky se synchronizují s Twenty: @@ -79,7 +98,7 @@ Toto vám dává přesnou kontrolu nad tím, které emaily se objeví ve vašem **Co se Synchronizuje:** * **Externí Emaily**: Všechny emaily s externími kontakty z vybraných složek -* **Interní Emaily**: Není synchronizováno (emaily ve stejné doméně zůstávají soukromé) +* **Interní e-maily**: Ve výchozím nastavení se nesynchronizují (e-maily ve stejné doméně zůstávají soukromé). V dolní části **Nastavení** zapněte přepínač **Pokročilé** a poté v **Obecné → Zabezpečení** zapněte **Synchronizovat interní e-maily**, abyste je zahrnuli v rámci celého workspace. * **Přílohy**: Přichází v H1 2026 **Poznámka**: Neposkytujeme CC emailovou adresu pro selektivní synchronizaci. Místo toho použijte výše uvedenou funkci Složka Zpráv pro dosažení stejné úrovně kontroly nad tím, které emaily se synchronizují s Twenty. diff --git a/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/field-mapping.mdx b/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/field-mapping.mdx index 2df3363703..c78c5045df 100644 --- a/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/field-mapping.mdx +++ b/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/field-mapping.mdx @@ -56,7 +56,7 @@ Adresa je vnořené pole s více sloupci. Některé lze ponechat prázdné. Použijte následující formát: ``` -[\"value1\",\"value2\"] +["value1","value2"] ``` ### Booleovská pole @@ -94,7 +94,7 @@ Podporované formáty: * Pro další e‑maily: použijte **E‑maily / Primární e‑mail** pro hlavní e‑mail a **E‑maily / Další e‑maily** v tomto formátu: ``` -[\"jane@twenty.com\",\"jane.doe@twenty.com\"] +["jane@twenty.com","jane.doe@twenty.com"] ``` ### Pole ID @@ -125,7 +125,7 @@ Podobně jako u doménových polí: * Pro sekundární odkazy použijte sloupec **Odkazy / Sekundární odkazy** v tomto formátu: ``` -[{\"url\":\"https://twenty.com\",\"label\":\"Twenty\"}] +[{"url":"https://twenty.com","label":"Twenty"}] ``` ### Pole s vícenásobným výběrem @@ -133,7 +133,7 @@ Podobně jako u doménových polí: Použijte **názvy API** (nikoli zobrazované popisky) v následujícím formátu: ``` -[\"VALUE1\",\"VALUE2\"] +["VALUE1","VALUE2"] ``` Viz [zde](#finding-api-names-for-select-fields), kde najdete názvy API. @@ -143,7 +143,7 @@ Viz [zde](#finding-api-names-for-select-fields), kde najdete názvy API. **Import přepisuje, nepřidává.** -Pokud má záznam již vybrány `VALUE2` a `VALUE3` a vy importujete `[\"VALUE1\"]`, bude mít po importu pouze `VALUE1`. Předchozí výběry se nahrazují, neslučují. +Pokud má záznam již vybrány `VALUE2` a `VALUE3` a vy importujete `["VALUE1"]`, bude mít po importu pouze `VALUE1`. Předchozí výběry se nahrazují, neslučují. ### Číselná pole diff --git a/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/file-formats.mdx b/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/file-formats.mdx index 121d6c0291..d34b6262c2 100644 --- a/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/file-formats.mdx +++ b/packages/twenty-docs/l/cs/user-guide/data-migration/capabilities/file-formats.mdx @@ -25,7 +25,7 @@ Twenty podporuje pro import tři formáty souborů: ## Osvědčené postupy pro CSV * **Oddělovač**: Použijte čárku (`,`) nebo středník (`;`) -* **Textový kvalifikátor**: Použijte dvojité uvozovky (`\"`) pro text obsahující čárky +* **Textový kvalifikátor**: Použijte dvojité uvozovky (`"`) pro text obsahující čárky * **Konce řádků**: Windows (CRLF) nebo Unix (LF), obojí je podporováno * **Prázdné hodnoty**: Nechte buňky prázdné, nepoužívejte "NULL" ani "N/A" diff --git a/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx b/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx index 7ea5b8fedb..90f68adb07 100644 --- a/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx +++ b/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-other-crms.mdx @@ -214,7 +214,7 @@ Po importu dat dokončete konfiguraci pracovního prostoru: ### Nakonfigurujte role a oprávnění -* Nakonfigurujte role v **Settings → Roles** +* Nakonfigurujte role v **Settings → Members → Roles** * Přiřaďte uživatele ke vhodným rolím ### Propojte e-mail a kalendář diff --git a/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx b/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx index d36620215e..2bf1149eff 100644 --- a/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx +++ b/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud.mdx @@ -127,7 +127,7 @@ Po importu dat ručně znovu vytvořte: ### Role a oprávnění -* Nakonfigurujte role v **Settings → Roles** +* Nakonfigurujte role v **Settings → Members → Roles** * Přiřaďte uživatele ke vhodným rolím ### Integrace diff --git a/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx b/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx index 0c3c5d1313..8e40e9cbc3 100644 --- a/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx +++ b/packages/twenty-docs/l/cs/user-guide/data-migration/how-tos/prepare-your-csv-files.mdx @@ -65,7 +65,7 @@ Různé typy polí vyžadují specifické formáty. Zde je úplný přehled: * Pro další e‑maily použijte tento formát ve sloupci **E‑maily / Další e‑maily**: ``` -[\"jane@twenty.com\",\"jane.doe@twenty.com\"] +["jane@twenty.com","jane.doe@twenty.com"] ``` ### Doménová pole diff --git a/packages/twenty-docs/l/cs/user-guide/data-model/capabilities/fields.mdx b/packages/twenty-docs/l/cs/user-guide/data-model/capabilities/fields.mdx index 5c8f1dbf12..8571338d9c 100644 --- a/packages/twenty-docs/l/cs/user-guide/data-model/capabilities/fields.mdx +++ b/packages/twenty-docs/l/cs/user-guide/data-model/capabilities/fields.mdx @@ -101,6 +101,10 @@ Udělte poli unikátnost, abyste zajistili, že různé záznamy nemohou mít st Pokud narazíte na chybu při nastavování unikátnosti, zkontrolujte duplicitní hodnoty ve svých datech (včetně smazaných záznamů). +## Indexy (pokročilé) + +Databázové indexy jsou spravovány automaticky — přidávání vlastních je jen zřídka nutné a je snadné je udělat špatně. Když je zapnutý pokročilý režim, má každý objekt sekci **Indexy** v `Settings → Data Model → ` pro případy, kdy víte, že ho potřebujete. + ## Nejlepší praktiky pro konfiguraci polí ### Nejlepší praktiky pro konfiguraci polí diff --git a/packages/twenty-docs/l/cs/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/cs/user-guide/permissions-access/capabilities/permissions.mdx index 717f626338..18d230f24d 100644 --- a/packages/twenty-docs/l/cs/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/cs/user-guide/permissions-access/capabilities/permissions.mdx @@ -13,7 +13,7 @@ Systém oprávnění v Twenty vám umožňuje řídit přístup ke třem hlavní Chcete-li vytvořit novou roli: -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. V sekci **Všechny role** klikněte na **+ Vytvořit roli** 3. Zadejte název role 4. Na výchozí kartě **Oprávnění** [nakonfigurujte oprávnění](#customize-permissions) @@ -23,7 +23,7 @@ Chcete-li vytvořit novou roli: Chcete-li smazat roli: -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Klikněte na roli, kterou chcete odebrat 3. Otevřete záložku **Nastavení** a klikněte na **Smazat roli** 4. Klikněte na **Potvrdit** v modálním okně @@ -36,13 +36,13 @@ Pokud je role smazána, každý člen pracovního prostoru přiřazený k této ### Zobrazit aktuální přiřazení -* Přejděte na **Nastavení → Role** +* Přejděte na **Nastavení → Členové → Role** * Zjistěte všechny role a kolik členů je ke každé přiřazeno * Zobrazit, kteří členové mají jaké role ### Přiřadit roli členu -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Klikněte na roli, kterou chcete přiřadit 3. Otevřete záložku **Přiřazení** 4. Klikněte na **+ Přiřadit členu** @@ -51,7 +51,7 @@ Pokud je role smazána, každý člen pracovního prostoru přiřazený k této ### Nastavit výchozí roli -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. V sekci **Možnosti** najděte **Výchozí roli** 3. Vyberte, kterou roli by noví členové měli automaticky obdržet 4. Noví členové pracovního prostoru budou při příchodu k této roli přiřazeni. @@ -98,6 +98,22 @@ Klikněte na **+ Přidat pravidlo** a vyberte objekt pro vytvoření výjimky. | Příležitosti → zakažte "Zobrazit záznamy" | Stážista vůbec nemůže vidět objekt Příležitosti | | Lidé → povolte "Upravovat záznamy" | Stážista může upravovat záznamy objektu Lidé (ale ne ostatní objekty) | +### Oprávnění na úrovni řádků + + +Oprávnění na úrovni řádků jsou **prémiová funkce** dostupná v tarifu **Organization** (Cloud i Self-Hosted). + + +Oprávnění na úrovni řádků vám umožňují omezit, které jednotlivé záznamy může role zobrazit nebo upravovat, a to na základě dynamických kritérií. Na rozdíl od oprávnění na úrovni objektu (která se vztahují na celý typ objektu) vyhodnocují oprávnění na úrovni řádků každý záznam samostatně. + +**Příklady použití:** + +* Obchodní zástupci mohou vidět pouze své vlastní příležitosti +* Manažeři mohou vidět všechny záznamy ve svém regionu +* Pracovníci podpory mohou zobrazit pouze tikety, které jsou jim přiřazené + +Chcete-li nakonfigurovat oprávnění na úrovni řádků, otevřete roli, přejděte na kartu **Objects** a v části **Row-Level** definujte podmínky filtru pro konkrétní objekt. + ### Oprávnění k polím V rámci každého pravidla na úrovni objektu můžete jít dál a nakonfigurovat **oprávnění na úrovni pole** pro řízení přístupu ke konkrétním polím. @@ -168,7 +184,7 @@ Kromě členů pracovního prostoru lze role přiřadit také ke **klíčům API ### Přiřaďte roli klíči API -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Klikněte na roli, kterou chcete přiřadit 3. Otevřete záložku **Přiřazení** 4. V části **Klíče API** klikněte na **+ Přiřadit ke klíči API** @@ -183,7 +199,7 @@ Klíče API bez přiřazené role používají výchozí oprávnění. Pro vyš ### Přiřaďte roli agentovi AI -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Klikněte na roli, kterou chcete přiřadit 3. Otevřete záložku **Přiřazení** 4. V části **Agenti AI** klikněte na **+ Přiřadit agentovi AI** diff --git a/packages/twenty-docs/l/cs/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/cs/user-guide/permissions-access/how-tos/permissions-faq.mdx index acc4800148..eac41092e9 100644 --- a/packages/twenty-docs/l/cs/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/cs/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -19,7 +19,7 @@ Každý člen pracovního prostoru přiřazený k této roli bude automaticky p -Přejděte do **Nastavení → Role**, najděte možnost **Výchozí role** a vyberte, kterou roli mají noví členové po připojení automaticky získat. +Přejděte na **Nastavení → Členové → Role**, najděte možnost **Výchozí role** a vyberte, kterou roli mají noví členové po připojení automaticky získat. @@ -60,11 +60,11 @@ Pro pole: -Oprávnění na úrovni řádků budou k dispozici v tarifu **Organization** v 1. čtvrtletí 2026. To umožní omezit přístup ke konkrétním záznamům na základě kritérií (např. zobrazovat pouze vlastní příležitosti). +Oprávnění na úrovni řádků jsou k dispozici v tarifu **Organization**. To umožní omezit přístup ke konkrétním záznamům na základě kritérií (např. zobrazovat pouze vlastní příležitosti). -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Vyberte roli 3. Přejděte k objektu, který pole obsahuje 4. Nastavte oprávnění pole na **Zobrazit pole** (bez Upravit pole) diff --git a/packages/twenty-docs/l/cs/user-guide/settings/capabilities/domains-settings.mdx b/packages/twenty-docs/l/cs/user-guide/settings/capabilities/domains-settings.mdx index c1c25d101b..d0224178f9 100644 --- a/packages/twenty-docs/l/cs/user-guide/settings/capabilities/domains-settings.mdx +++ b/packages/twenty-docs/l/cs/user-guide/settings/capabilities/domains-settings.mdx @@ -3,10 +3,12 @@ title: Nastavení domén description: Nastavte doménu pracovního prostoru, schválené přístupové domény a veřejné domény. --- -Nakonfigurujte nastavení domén v **Settings → Domains**. +Nastavení domény se nachází na třech místech v závislosti na tom, co právě konfigurujete. ## Doména pracovního prostoru +Nastavte v **Nastavení → Obecné → Doména pracovního prostoru**. + Upravte název své subdomény nebo nastavte vlastní doménu pro svůj pracovní prostor. ### Přizpůsobit doménu @@ -19,6 +21,8 @@ U vlastních domén budete muset u svého poskytovatele domény nakonfigurovat n ## Schválené domény +Nastavte v **Nastavení → Členové → Pozvat**. + Kdokoli s e-mailovou adresou na těchto doménách se může do tohoto pracovního prostoru automaticky zaregistrovat. ### Přidat schválenou přístupovou doménu @@ -35,13 +39,16 @@ To je užitečné, protože umožní celému vašemu týmu registrovat se samost ## Veřejné domény -Zajistěte kompletní a bezpečné hostingové prostředí na těchto doménách. +Nastavte v **Nastavení → Aplikace → Vývojář**. + +Zajistěte kompletní a bezpečné hostingové prostředí na těchto doménách. Veřejnou doménu lze přiřadit ke konkrétní aplikaci — pokud je přiřazená, jsou na této doméně dostupné pouze funkce aplikační logiky této aplikace směrované přes HTTP. Nechte přiřazení prázdné, aby byly zpřístupněny všechny HTTP trasy pracovního prostoru. ### Přidat veřejnou doménu 1. Klikněte na **Přidat veřejnou doménu** 2. Zadejte doménu, kterou chcete použít -3. Nakonfigurujte nastavení DNS podle pokynů -4. Ověřte doménu +3. Volitelně ji přiřaďte k aplikaci +4. Nakonfigurujte nastavení DNS podle pokynů +5. Ověřte doménu Pro veřejné domény se certifikáty SSL zřizují automaticky. diff --git a/packages/twenty-docs/l/cs/user-guide/settings/capabilities/member-management.mdx b/packages/twenty-docs/l/cs/user-guide/settings/capabilities/member-management.mdx index 0e8771afe4..e5a7755d10 100644 --- a/packages/twenty-docs/l/cs/user-guide/settings/capabilities/member-management.mdx +++ b/packages/twenty-docs/l/cs/user-guide/settings/capabilities/member-management.mdx @@ -77,7 +77,7 @@ Spravujte pozvánky, které dosud nebyly přijaty: Umožněte členům týmu připojit se automaticky podle jejich e-mailové domény: -1. Přejděte na **Nastavení → Domény** +1. Přejděte na **Nastavení → Členové → Pozvat** 2. Přidejte doménu své společnosti (např. `yourcompany.com`) 3. Kdokoli s touto e-mailovou doménou se může připojit bez pozvánky diff --git a/packages/twenty-docs/l/cs/user-guide/settings/how-tos/settings-faq.mdx b/packages/twenty-docs/l/cs/user-guide/settings/how-tos/settings-faq.mdx index d376275547..d33568e2bc 100644 --- a/packages/twenty-docs/l/cs/user-guide/settings/how-tos/settings-faq.mdx +++ b/packages/twenty-docs/l/cs/user-guide/settings/how-tos/settings-faq.mdx @@ -137,7 +137,7 @@ Ano, můžete připojit více e-mailových účtů. Přejděte do **Nastavení -Ano! Přejděte do **Nastavení → Domény** a klikněte na **Přizpůsobit doménu**. Máte dvě možnosti: +Ano! Přejděte do **Nastavení → Obecné → Doména pracovního prostoru** a klikněte na **Přizpůsobit doménu**. Máte dvě možnosti: * **Poddoména**: Použijte poddoménu Twenty, například `yourcompany.twenty.com` * **Vlastní doména**: Použijte svou vlastní doménu, například `crm.yourcompany.com` (vyžaduje konfiguraci DNS) @@ -146,7 +146,7 @@ Poddoménu nastavíte rychle, zatímco vlastní doména poskytne vašemu týmu p -Můžete nastavit schválené přístupové domény tak, aby se členové týmu s firemními e-mailovými adresami mohli automaticky připojit k vašemu pracovnímu prostoru. Přejděte do **Nastavení → Domény** a přidejte svou firemní doménu (např. `yourcompany.com`). +Můžete nastavit schválené přístupové domény tak, aby se členové týmu s firemními e-mailovými adresami mohli automaticky připojit k vašemu pracovnímu prostoru. Přejděte do **Nastavení → Členové → Pozvat** a přidejte svou firemní doménu (např. `yourcompany.com`). diff --git a/packages/twenty-docs/l/cs/user-guide/settings/overview.mdx b/packages/twenty-docs/l/cs/user-guide/settings/overview.mdx index 2adc82d169..91c28f2f77 100644 --- a/packages/twenty-docs/l/cs/user-guide/settings/overview.mdx +++ b/packages/twenty-docs/l/cs/user-guide/settings/overview.mdx @@ -44,7 +44,7 @@ Přidejte členy týmu do svého pracovního prostoru: 4. Přiřaďte příslušné role -Před pozváním týmu zkontrolujte výchozí roli v **Nastavení → Role**. Novým členům je tato role automaticky přiřazena, když se připojí. +Před pozváním týmu zkontrolujte výchozí roli v **Nastavení → Členové → Role**. Novým členům je tato role automaticky přiřazena, když se připojí. ## Kontrolní seznam nastavení pracovního prostoru diff --git a/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx b/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx index 0ec4adcdd5..4f4cb86a33 100644 --- a/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx +++ b/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline.mdx @@ -38,7 +38,7 @@ Na objektu Příležitosti potřebujete dvě vlastní pole. Pokud nechcete, aby uživatelé ručně upravovali tato vypočtená pole: -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Vyberte roli k nastavení 3. Najděte objekt Příležitosti 4. Nastavte pole **Pravděpodobnost** a **Očekávaná částka** jako pouze pro čtení diff --git a/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx b/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx index bd1305db76..9b7d5f8b5d 100644 --- a/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx +++ b/packages/twenty-docs/l/cs/user-guide/views-pipelines/how-tos/track-time-in-stage.mdx @@ -61,7 +61,7 @@ Pole "Dny v" pro Uzavřeno – vyhráno a Uzavřeno – prohráno nepotřebujete Nechcete-li, aby uživatelé ručně upravovali tato vypočítávaná pole: -1. Přejděte na **Nastavení → Role** +1. Přejděte na **Nastavení → Členové → Role** 2. Vyberte roli k nastavení 3. Najděte objekt Obchodní příležitosti 4. Nastavte pole "Poslední vstup" a "Dny v" jako jen pro čtení diff --git a/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/send-emails-from-workflows.mdx b/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/send-emails-from-workflows.mdx index 018c690526..440894cfad 100644 --- a/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/send-emails-from-workflows.mdx +++ b/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/send-emails-from-workflows.mdx @@ -71,7 +71,7 @@ Tým 1. **Spouštěč**: Záznam je vytvořen (Lidé) 2. **Přidat akci Filtr**: - * Podmínka: `{{trigger.object.source}}` se rovná `\"Website\"` + * Podmínka: `{{trigger.object.source}}` se rovná `"Website"` * Pokud ano → pokračujte k vítacímu e-mailu pro web 3. **Větev pro jiné zdroje**: diff --git a/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/workflow-actions.mdx b/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/workflow-actions.mdx index ce310dcc6c..7e982fa64d 100644 --- a/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/workflow-actions.mdx +++ b/packages/twenty-docs/l/cs/user-guide/workflows/capabilities/workflow-actions.mdx @@ -307,5 +307,5 @@ Akce agenta AI spotřebovávají kredity pracovního postupu podle použitého m -Agenti AI respektují oprávnění založená na rolích. V části **Nastavení → Role** můžete agentům přiřadit konkrétní role, abyste řídili, k jakým datům mají přístup. Podrobnosti viz [Oprávnění](/l/cs/user-guide/permissions-access/capabilities/permissions). +Agenti AI respektují oprávnění založená na rolích. V části **Nastavení → Členové → Role** můžete agentům přiřadit konkrétní role, abyste řídili, k jakým datům mají přístup. Podrobnosti viz [Oprávnění](/l/cs/user-guide/permissions-access/capabilities/permissions). diff --git a/packages/twenty-docs/l/cs/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx b/packages/twenty-docs/l/cs/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx index 4edd4fc8f8..b0d84aeca9 100644 --- a/packages/twenty-docs/l/cs/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx +++ b/packages/twenty-docs/l/cs/user-guide/workflows/how-tos/need-more-help/workflows-faq.mdx @@ -8,7 +8,7 @@ description: Často kladené otázky k pracovním postupům v Twenty. Pravděpodobně jde o problém s oprávněními. K vytváření a aktivaci potřebujete přístup k pracovním postupům. - **Řešení**: Kontaktujte správce pracovního prostoru, aby vám v **Settings → Roles** udělil přístup k pracovním postupům. + **Řešení**: Kontaktujte správce pracovního prostoru, aby vám v **Settings → Members → Roles** udělil přístup k pracovním postupům. Pokud v postranním panelu vůbec nevidíte sekci Pracovní postupy, potvrzuje to, že jde o problém s oprávněními. diff --git a/packages/twenty-docs/l/de/twenty-ui/input/text.mdx b/packages/twenty-docs/l/de/twenty-ui/input/text.mdx index 1cd1f85464..bf7320e22e 100644 --- a/packages/twenty-docs/l/de/twenty-ui/input/text.mdx +++ b/packages/twenty-docs/l/de/twenty-ui/input/text.mdx @@ -39,7 +39,7 @@ export const MyComponent = () => { /> ); }; -},{ + ``` diff --git a/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/field-mapping.mdx b/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/field-mapping.mdx index f747829b27..815b82749d 100644 --- a/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/field-mapping.mdx +++ b/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/field-mapping.mdx @@ -143,7 +143,7 @@ Siehe [hier](#finding-api-names-for-select-fields), wo Sie die API-Namen finden. **Der Import überschreibt, er fügt nichts hinzu.** -Wenn ein Datensatz bereits `VALUE2` und `VALUE3` ausgewählt hat und Sie `[\"VALUE1\"]` importieren, hat der Datensatz nach dem Import nur noch `VALUE1`. Die bisherigen Auswahlen werden ersetzt, nicht zusammengeführt. +Wenn ein Datensatz bereits `VALUE2` und `VALUE3` ausgewählt hat und Sie `["VALUE1"]` importieren, hat der Datensatz nach dem Import nur noch `VALUE1`. Die bisherigen Auswahlen werden ersetzt, nicht zusammengeführt. ### Zahlenfelder diff --git a/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/file-formats.mdx b/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/file-formats.mdx index 53bc98c081..17bd390ada 100644 --- a/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/file-formats.mdx +++ b/packages/twenty-docs/l/de/user-guide/data-migration/capabilities/file-formats.mdx @@ -25,7 +25,7 @@ Twenty unterstützt drei Dateiformate für den Import: ## CSV: Beste Praktiken * **Trennzeichen**: Verwenden Sie Komma (`,`) oder Semikolon (`;`) -* **Textbegrenzer**: Verwenden Sie doppelte Anführungszeichen (`\"`) für Text, der Kommas enthält +* **Textbegrenzer**: Verwenden Sie doppelte Anführungszeichen (`"`) für Text, der Kommas enthält * **Zeilenenden**: Sowohl Windows (CRLF) als auch Unix (LF) werden unterstützt * **Leere Werte**: Zellen leer lassen, nicht "NULL" oder "N/A" verwenden diff --git a/packages/twenty-docs/l/de/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx b/packages/twenty-docs/l/de/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx index a02bcab049..818b81ef7d 100644 --- a/packages/twenty-docs/l/de/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx +++ b/packages/twenty-docs/l/de/user-guide/data-migration/how-tos/update-existing-records-via-import.mdx @@ -27,9 +27,9 @@ Wenn Sie eine Datei importieren, die einen **eindeutigen Bezeichner** enthält, **Mehrfachauswahl-Felder werden überschrieben, nicht zusammengeführt.** -Wenn in einem Datensatz `Option A` und `Option B` ausgewählt sind und Sie `[\"Option C\"]` importieren, hat der Datensatz nach dem Import nur noch `Option C`. Der Import ersetzt alle bisherigen Auswahlen — er fügt ihnen nichts hinzu. +Wenn in einem Datensatz `Option A` und `Option B` ausgewählt sind und Sie `["Option C"]` importieren, hat der Datensatz nach dem Import nur noch `Option C`. Der Import ersetzt alle bisherigen Auswahlen — er fügt ihnen nichts hinzu. -Um vorhandene Werte beizubehalten, schließen Sie sie alle in Ihren Import ein: `[\"Option A\",\"Option B\",\"Option C\"]` +Um vorhandene Werte beizubehalten, schließen Sie sie alle in Ihren Import ein: `["Option A","Option B","Option C"]` ## Schritt 1: Exportieren Sie Ihre aktuellen Daten diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/custom-objects.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/custom-objects.mdx index 7ce5c14c0b..b7295557bc 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/custom-objects.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/custom-objects.mdx @@ -11,7 +11,7 @@ Los objetos personalizados son objetos que puedes crear para almacenar informaci ## Esquema de alto nivel
- Esquema de alto nivel + Esquema de alto nivel

@@ -27,7 +27,7 @@ Los objetos personalizados provienen de tablas de metadatos que determinan la fo Para añadir un objeto personalizado, el workspaceMember consultará la API de /metadata. Esto actualiza los metadatos de acuerdo y calcula un esquema GraphQL basado en los metadatos, almacenándolo en un caché de GQL para su uso posterior.
- Consultando la API de /metadata para añadir objetos personalizados + Consultando la API de /metadata para añadir objetos personalizados

@@ -35,5 +35,5 @@ Para añadir un objeto personalizado, el workspaceMember consultará la API de / Para obtener datos, el proceso implica hacer consultas a través del endpoint /graphql y pasarlos a través del Query Resolver.
- Consulta el endpoint /graphql para obtener datos + Consulta el endpoint /graphql para obtener datos
diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/server-commands.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/server-commands.mdx index e1b96c639e..76c6b24e66 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/server-commands.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/server-commands.mdx @@ -1,5 +1,6 @@ --- title: Comandos de Backend +icon: terminal --- ## Comandos útiles diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/zapier.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/zapier.mdx index 3a853d4d22..b8e4019c1f 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/zapier.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/backend-development/zapier.mdx @@ -43,7 +43,9 @@ Reemplaza el valor de **YOUR_API_KEY** en el archivo `.env` con la clave API que ## Desarrollo - Asegúrate de ejecutar `yarn build` antes de cualquier comando `zapier`. + +Asegúrate de ejecutar `yarn build` antes de cualquier comando `zapier`. + ### Prueba diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/bug-and-requests.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/bug-and-requests.mdx index fe5ba34c84..4cbff0d22b 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/bug-and-requests.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/bug-and-requests.mdx @@ -1,5 +1,6 @@ --- title: Errores, solicitudes y pull requests +icon: bug info: Informa de problemas, solicita funcionalidades y contribuye con código --- diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/best-practices-front.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/best-practices-front.mdx index 909d940370..eb7ba200f5 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/best-practices-front.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/best-practices-front.mdx @@ -1,5 +1,6 @@ --- title: Mejores prácticas +icon: star --- Este documento describe las mejores prácticas que debes seguir al trabajar en el frontend. @@ -8,12 +9,14 @@ Este documento describe las mejores prácticas que debes seguir al trabajar en e React y Jotai manejan la gestión de estado en la base de código. -### Usa `useAtomState` para almacenar el estado +### Usa átomos de Jotai para almacenar el estado Es buena práctica crear tantos átomos como necesites para almacenar tu estado. - Es mejor usar átomos adicionales que intentar ser demasiado concisos con la perforación de props. + +Es mejor usar átomos adicionales que intentar ser demasiado concisos con la perforación de props. + ```tsx @@ -43,7 +46,7 @@ export const MyComponent = () => { Evita usar `useRef` para almacenar el estado. -If you want to store state, you should use `useState` or `useAtomState`. +Si deseas almacenar el estado, deberías usar `useState` o átomos de Jotai con `useAtomState`. Consulta [cómo gestionar las re-renderizaciones](#managing-re-renders) si sientes que necesitas `useRef` para evitar algunas re-renderizaciones. @@ -80,7 +83,7 @@ If you feel like you need to add a `useEffect` in your root component, you shoul Puedes aplicar lo mismo para la lógica de obtención de datos, con hooks de Apollo. ```tsx -// ❌ Malo, provocará re-renderizados incluso si los datos no cambian, +// ❌ Malo, provocará re-renderizados incluso si los datos no están cambiando, // porque useEffect necesita volver a evaluarse export const PageComponent = () => { const [data, setData] = useAtomState(dataState); @@ -101,7 +104,7 @@ export const App = () => ( ``` ```tsx -// ✅ Bueno, no provocará re-renderizados si los datos no cambian, +// ✅ Bueno, no provocará re-renderizados si los datos no están cambiando, // porque useEffect se vuelve a evaluar en otro componente hermano export const PageComponent = () => { const [data, setData] = useAtomState(dataState); @@ -130,9 +133,9 @@ export const App = () => ( ); ``` -### Usa estados de familia de jotai y selectores de familia de jotai +### Usa familias de átomos y familias de selectores -Los estados y selectores de familia de jotai son una gran manera de evitar re-renderizaciones. +Las familias de átomos y las familias de selectores son una gran manera de evitar re-renderizados. Son útiles cuando necesitas almacenar una lista de elementos. diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/folder-architecture-front.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/folder-architecture-front.mdx index c0b423302f..533d75a69c 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/folder-architecture-front.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/folder-architecture-front.mdx @@ -1,5 +1,6 @@ --- title: Arquitectura de Carpetas +icon: folder-tree info: Una mirada detallada a nuestra arquitectura de carpetas --- @@ -84,7 +85,7 @@ Ver [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) para más d Contiene la lógica de gestión de estado. [Jotai](https://jotai.org) maneja esto. -* Selectores: Los átomos derivados (usando `createAtomSelector`) calculan valores a partir de otros átomos y se memorizan automáticamente. +* Selectores: Los átomos derivados (usando `createAtomSelector`) calculan valores a partir de otros átomos y se memoizan automáticamente. La gestión de estado incorporada de React todavía maneja el estado dentro de un componente. diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/frontend-commands.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/frontend-commands.mdx index e03c832861..5d14803994 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/frontend-commands.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/frontend-commands.mdx @@ -1,5 +1,6 @@ --- title: Comandos del Frontend +icon: terminal --- ## Comandos útiles @@ -77,7 +78,7 @@ To avoid unnecessary [re-renders](/l/es/developers/contribute/capabilities/front ### Gestión del Estado -[Jotai](https://jotai.org/) maneja la gestión del estado. +[Jotai](https://jotai.org/) gestiona el estado. Ver [mejores prácticas](/l/es/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) para más información sobre la gestión del estado. diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/hotkeys.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/hotkeys.mdx index 248e1bb240..9f14f30cbd 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/hotkeys.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/hotkeys.mdx @@ -160,7 +160,7 @@ export enum PageHotkeyScope { } ``` -Internamente, el ámbito seleccionado se almacena en un estado de Jotai que se comparte en toda la aplicación: +Internamente, el ámbito seleccionado actualmente se almacena en un átomo de Jotai que se comparte en toda la aplicación: ```tsx export const currentHotkeyScopeState = createState({ @@ -169,10 +169,10 @@ export const currentHotkeyScopeState = createState({ }); ``` -¡Pero este estado de Jotai nunca debe manejarse manualmente! Veremos cómo usarlo en la siguiente sección. +¡Pero este átomo nunca debe manejarse manualmente! Veremos cómo usarlo en la siguiente sección. ## ¿Cómo funciona internamente? Hicimos un contenedor delgado sobre [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) que lo hace más eficiente y evita renders innecesarios. -También creamos un estado de Jotai para manejar el estado del ámbito de atajos de teclado y hacerlo disponible en toda la aplicación. +También creamos un átomo de Jotai para manejar el estado del ámbito de atajos de teclado y hacerlo disponible en toda la aplicación. diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/style-guide.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/style-guide.mdx index f413ec87cf..2e5c16fdc9 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/style-guide.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/style-guide.mdx @@ -1,5 +1,6 @@ --- title: Guía de Estilo +icon: pincel --- Este documento incluye las reglas a seguir al escribir código. @@ -281,7 +282,7 @@ import { Meta, StoryObj } from '@storybook/react'; * **Mantenibilidad**: Mejora la mantenibilidad de la base de código porque los desarrolladores pueden identificar y localizar importaciones solo de tipo al revisar o modificar el código. -### regla de Oxlint +### Regla de Oxlint Una regla de Oxlint, `typescript/consistent-type-imports`, impone el estándar de no usar "type" en las importaciones. Esta regla generará errores o advertencias sobre cualquier violación de importación de tipo. diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/work-with-figma.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/work-with-figma.mdx index 85382f32b1..27812d0a63 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/work-with-figma.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/frontend-development/work-with-figma.mdx @@ -13,7 +13,9 @@ Esta guía explica cómo puedes colaborar con Figma. Las características clave solo están disponibles para usuarios registrados, como el modo desarrollador y la capacidad de seleccionar un marco dedicado. - No podrás colaborar efectivamente sin una cuenta. + +No podrás colaborar efectivamente sin una cuenta. + ## Estructura de Figma diff --git a/packages/twenty-docs/l/es/developers/contribute/capabilities/local-setup.mdx b/packages/twenty-docs/l/es/developers/contribute/capabilities/local-setup.mdx index e3ec80dbdf..0a0f28ea43 100644 --- a/packages/twenty-docs/l/es/developers/contribute/capabilities/local-setup.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/capabilities/local-setup.mdx @@ -1,71 +1,72 @@ --- title: Configuración Local +icon: laptop-code description: La guía para los colaboradores (o desarrolladores curiosos) que quieren ejecutar Twenty localmente. --- ## Prerrequisitos - - Antes de que puedas instalar y usar Twenty, asegúrate de instalar lo siguiente en tu computadora: + - * [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - * [Node v24.5.0](https://nodejs.org/en/download) - * [yarn v4](https://yarnpkg.com/getting-started/install) - * [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md) +Antes de que puedas instalar y usar Twenty, asegúrate de instalar lo siguiente en tu computadora: +* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Node v24.5.0](https://nodejs.org/en/download) +* [yarn v4](https://yarnpkg.com/getting-started/install) +* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md) - - `npm` no funcionará, deberías usar `yarn` en su lugar. Yarn ahora se envía con Node.js, por lo que no necesitas instalarlo por separado. - Solo tienes que ejecutar `corepack enable` para habilitar Yarn si aún no lo has hecho. - - + +`npm` no funcionará, deberías usar `yarn` en su lugar. Yarn ahora se envía con Node.js, por lo que no necesitas instalarlo por separado. +Solo tienes que ejecutar `corepack enable` para habilitar Yarn si aún no lo has hecho. + - - 1. Instalar WSL - Abre PowerShell como Administrador y ejecuta: + - ```powershell - wsl --install - ``` + - Ahora deberías ver un aviso para reiniciar tu computadora. Si no, reiníciala manualmente. +1. Instalar WSL + Abre PowerShell como Administrador y ejecuta: +```powershell +wsl --install +``` +Ahora deberías ver un aviso para reiniciar tu computadora. Si no, reiníciala manualmente. - Al reiniciar, se abrirá una ventana de PowerShell e instalará Ubuntu. Esto puede tomar algo de tiempo. - Verás un aviso para crear un nombre de usuario y contraseña para tu instalación de Ubuntu. +Al reiniciar, se abrirá una ventana de PowerShell e instalará Ubuntu. Esto puede tomar algo de tiempo. +Verás un aviso para crear un nombre de usuario y contraseña para tu instalación de Ubuntu. - 2. Instalar y configurar git +2. Instalar y configurar git - ```bash - sudo apt-get install git +```bash +sudo apt-get install git - git config --global user.name "Your Name" +git config --global user.name "Your Name" - git config --global user.email "youremail@domain.com" - ``` +git config --global user.email "youremail@domain.com" +``` - 3. Instalar nvm, node.js y yarn +3. Instalar nvm, node.js y yarn - - Usa `nvm` para instalar la versión correcta de `node`. El `.nvmrc` asegura que todos los colaboradores usen la misma versión. - + +Usa `nvm` para instalar la versión correcta de `node`. El `.nvmrc` asegura que todos los colaboradores usen la misma versión. + - ```bash - sudo apt-get install curl +```bash +sudo apt-get install curl - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash - ``` +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash +``` +Cierra y vuelve a abrir tu terminal para usar nvm. Luego ejecuta los siguientes comandos. - Cierra y vuelve a abrir tu terminal para usar nvm. Luego ejecuta los siguientes comandos. +```bash - ```bash +nvm install # installs recommended node version - nvm install # installs recommended node version +nvm use # use recommended node version - nvm use # use recommended node version +corepack enable +``` - corepack enable - ``` - + --- @@ -75,19 +76,19 @@ description: La guía para los colaboradores (o desarrolladores curiosos) que qu En tu terminal, ejecuta el siguiente comando. - - Si aún no has configurado claves SSH, puedes aprender cómo hacerlo [aquí](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh). + +Si aún no has configurado claves SSH, puedes aprender cómo hacerlo [aquí](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh). +```bash +git clone git@github.com:twentyhq/twenty.git +``` + + - ```bash - git clone git@github.com:twentyhq/twenty.git - ``` - +```bash +git clone https://github.com/twentyhq/twenty.git +``` - - ```bash - git clone https://github.com/twentyhq/twenty.git - ``` - + ## Paso 2: Ubícate en la raíz @@ -102,22 +103,18 @@ Debes ejecutar todos los comandos de los siguientes pasos desde la raíz del pro - **Opción 1 (preferido):** Para aprovisionar tu base de datos localmente: + **Opción 1 (preferida):** Para aprovisionar tu base de datos localmente: Usa el siguiente enlace para instalar PostgreSQL en tu máquina Linux: [Instalación de PostgreSQL](https://www.postgresql.org/download/linux/) - ```bash psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;" ``` - Nota: Puede que necesites agregar `sudo -u postgres` antes del comando `psql` para evitar errores de permisos. **Opción 2:** Si tienes Docker instalado: - ```bash make -C packages/twenty-docker postgres-on-docker ``` - **Opción 1 (preferido):** Para aprovisionar tu base de datos localmente con `brew`: @@ -129,7 +126,6 @@ Debes ejecutar todos los comandos de los siguientes pasos desde la raíz del pro ``` Puedes verificar si el servidor PostgreSQL está corriendo ejecutando: - ```bash brew services list ``` @@ -138,7 +134,6 @@ Debes ejecutar todos los comandos de los siguientes pasos desde la raíz del pro vía Homebrew en macOS. En cambio, crea un rol de PostgreSQL que coincide con tu nombre de usuario de macOS (por ejemplo, "john"). Para comprobar y crear el usuario `postgres` si es necesario, sigue estos pasos: - ```bash # Connect to PostgreSQL psql postgres @@ -147,70 +142,61 @@ Debes ejecutar todos los comandos de los siguientes pasos desde la raíz del pro ``` Una vez en el comando psql (postgres=#), ejecuta: - - ```bash - # List existing PostgreSQL roles - \du - ``` - + ```bash + # List existing PostgreSQL roles + \du + ``` Verás una salida similar a: - - ```bash - Role name | Attributes | Member of - -----------+-------------+----------- - john | Superuser | {} - ``` + ```bash + Role name | Attributes | Member of + -----------+-------------+----------- + john | Superuser | {} + ``` Si no ves un rol `postgres` listado, procede al siguiente paso. Crea el rol `postgres` manualmente: - - ```bash - CREATE ROLE postgres WITH SUPERUSER LOGIN; - ``` - + ```bash + CREATE ROLE postgres WITH SUPERUSER LOGIN; + ``` Esto crea un rol de superusuario llamado `postgres` con acceso de inicio de sesión. - - ```bash - Nombre del rol | Atributos | Miembro de - -----------+-------------+----------- - postgres | Superuser | {} - john | Superuser | {} - ``` + ```bash + Nombre del rol | Atributos | Miembro de + -----------+-------------+----------- + postgres | Superuser | {} + john | Superuser | {} + ``` **Opción 2:** Si tienes Docker instalado: - ```bash make -C packages/twenty-docker postgres-on-docker ``` - Todos los siguientes pasos deben ejecutarse en la terminal de WSL (dentro de tu máquina virtual) **Opción 1:** Para aprovisionar tu PostgreSQL localmente: Usa el siguiente enlace para instalar PostgreSQL en tu máquina virtual Linux: [Instalación de PostgreSQL](https://www.postgresql.org/download/linux/) - ```bash psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;" ``` - Nota: Puede que necesites agregar `sudo -u postgres` antes del comando `psql` para evitar errores de permisos. **Opción 2:** Si tienes Docker instalado: Ejecutar Docker en WSL agrega una capa extra de complejidad. Solo usa esta opción si estás cómodo con los pasos extras involucrados, incluyendo activar [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl). - ```bash make -C packages/twenty-docker postgres-on-docker ``` -Ahora puedes acceder a la base de datos en [localhost:5432](localhost:5432), con usuario `postgres` y contraseña `postgres`. +Ahora puedes acceder a la base de datos en `localhost:5432`. + +Si utilizaste la opción de Docker anterior, las credenciales predeterminadas son usuario `postgres` y contraseña `postgres`. Para instalaciones nativas de PostgreSQL, usa las credenciales y los roles configurados en tu máquina. ## Paso 4: Configurar una Base de Datos Redis (cache) -Twenty requiere un caché de redis para proporcionar el mejor rendimiento +Twenty requiere un caché de Redis para proporcionar el mejor rendimiento. @@ -218,46 +204,41 @@ Twenty requiere un caché de redis para proporcionar el mejor rendimiento Usa el siguiente enlace para instalar Redis en tu máquina Linux: [Instalación de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/) **Opción 2:** Si tienes Docker instalado: - ```bash make -C packages/twenty-docker redis-on-docker ``` - **Opción 1 (preferido):** Para aprovisionar tu Redis localmente con `brew`: - ```bash brew install redis ``` - - Inicia tu servidor redis: - `brew services start redis` + Inicia tu servidor de Redis: + ```bash + brew services start redis + ``` **Opción 2:** Si tienes Docker instalado: - ```bash make -C packages/twenty-docker redis-on-docker ``` - **Opción 1:** Para aprovisionar tu Redis localmente: Usa el siguiente enlace para instalar Redis en tu máquina virtual Linux: [Instalación de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/) **Opción 2:** Si tienes Docker instalado: - ```bash make -C packages/twenty-docker redis-on-docker ``` -Si necesitas una interfaz gráfica de cliente, recomendamos [Redis Insight](https://redis.io/insight/) (versión gratuita disponible) +Si necesitas una interfaz gráfica de cliente, recomendamos [Redis Insight](https://redis.io/insight/) (versión gratuita disponible). ## Paso 5: Configurar las variables de entorno -Usa variables de entorno o archivos `.env` para configurar tu proyecto. Más información [aquí](/l/es/developers/self-host/capabilities/setup) +Usa variables de entorno o archivos `.env` para configurar tu proyecto. Más información [aquí](/l/es/developers/self-host/capabilities/setup). Copia los archivos `.env.example` en `/front` y `/server`: @@ -267,7 +248,7 @@ cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env ``` - **Modo de múltiples espacios de trabajo:** De forma predeterminada, Twenty se ejecuta en modo de un solo espacio de trabajo, donde solo se puede crear un espacio de trabajo. Para habilitar la compatibilidad con múltiples espacios de trabajo (útil para probar funciones basadas en subdominios), establece `IS_MULTIWORKSPACE_ENABLED=true` en el archivo `.env` de tu servidor. Consulta [Modo de múltiples espacios de trabajo](/l/es/developers/self-host/capabilities/setup#multi-workspace-mode) para más detalles. +**Modo de múltiples espacios de trabajo:** De forma predeterminada, Twenty se ejecuta en modo de un solo espacio de trabajo, donde solo se puede crear un espacio de trabajo. Para habilitar la compatibilidad con múltiples espacios de trabajo (útil para probar funciones basadas en subdominios), establece `IS_MULTIWORKSPACE_ENABLED=true` en el archivo `.env` de tu servidor. Consulta [Modo de múltiples espacios de trabajo](/l/es/developers/self-host/capabilities/setup#multi-workspace-mode) para más detalles. ## Paso 6: Instalación de dependencias @@ -287,15 +268,12 @@ Ten en cuenta que `npm` o `pnpm` no funcionarán Dependiendo de tu distribución de Linux, el servidor Redis podría iniciarse automáticamente. Si no, revisa la [guía de instalación de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) para tu distribución. - - Redis ya debería estar funcionando. Si no, ejecuta: - + Redis ya debería estar funcionando. Si no, ejecuta: ```bash brew services start redis ``` - Dependiendo de tu distribución de Linux, el servidor Redis podría iniciarse automáticamente. Si no es así, consulte la [guía de instalación de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) para su distribución. diff --git a/packages/twenty-docs/l/es/developers/contribute/commands.mdx b/packages/twenty-docs/l/es/developers/contribute/commands.mdx new file mode 100644 index 0000000000..54a95460ea --- /dev/null +++ b/packages/twenty-docs/l/es/developers/contribute/commands.mdx @@ -0,0 +1,77 @@ +--- +title: Comandos +icon: terminal +description: Comandos útiles para desarrollar Twenty. +--- + +Los comandos se pueden ejecutar desde la raíz del repositorio usando `npx nx`. Usa `npx nx run {project}:{command}` para especificar explícitamente el destino. + +## Iniciando la aplicación + +```bash +npx nx start twenty-front # Frontend dev server (http://localhost:3001) +npx nx start twenty-server # Backend server (http://localhost:3000) +npx nx run twenty-server:worker # Background worker +``` + +## Base de datos + +```bash +npx nx database:reset twenty-server # Reset and seed database +npx nx run twenty-server:database:migrate:prod # Run migrations +npx nx run twenty-server:database:migrate:generate --name --type # Generate a migration +``` + +## Análisis de código + +```bash +npx nx lint:diff-with-main twenty-front # Lint changed files (fastest) +npx nx lint:diff-with-main twenty-server +npx nx lint twenty-front --configuration=fix # Auto-fix +``` + +## Comprobación de tipos + +```bash +npx nx typecheck twenty-front +npx nx typecheck twenty-server +``` + +## Pruebas + +```bash +# Frontend +npx nx test twenty-front # Jest unit tests +npx nx storybook:build twenty-front # Build Storybook +npx nx storybook:test twenty-front # Storybook tests + +# Backend +npx nx run twenty-server:test:unit # Unit tests +npx nx run twenty-server:test:integration # Integration tests +npx nx run twenty-server:test:integration:with-db-reset # Integration with DB reset + +# Single file (fastest) +npx jest path/to/test.test.ts --config=packages/{project}/jest.config.mjs +``` + +## GraphQL + +```bash +npx nx run twenty-front:graphql:generate # Regenerate types +npx nx run twenty-front:graphql:generate --configuration=metadata # Metadata schema +``` + +## Traducciones + +```bash +npx nx run twenty-front:lingui:extract # Extract strings +npx nx run twenty-front:lingui:compile # Compile translations +``` + +## Compilar + +```bash +npx nx build twenty-shared # Must be built first +npx nx build twenty-front +npx nx build twenty-server +``` diff --git a/packages/twenty-docs/l/es/developers/contribute/contribute.mdx b/packages/twenty-docs/l/es/developers/contribute/contribute.mdx index f5b6ce47b6..8b31e741ff 100644 --- a/packages/twenty-docs/l/es/developers/contribute/contribute.mdx +++ b/packages/twenty-docs/l/es/developers/contribute/contribute.mdx @@ -25,7 +25,6 @@ Twenty es de código abierto y da la bienvenida a las contribuciones de la comun Reporta problemas o solicita funcionalidades - Contribuye a la interfaz de usuario diff --git a/packages/twenty-docs/l/es/developers/contribute/style-guide.mdx b/packages/twenty-docs/l/es/developers/contribute/style-guide.mdx new file mode 100644 index 0000000000..97a8c7b265 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/contribute/style-guide.mdx @@ -0,0 +1,176 @@ +--- +title: Guía de Estilo +icon: pincel +description: Convenciones de código y buenas prácticas para contribuir a Twenty. +--- + +## React + +### Solo componentes funcionales + +Siempre usa componentes funcionales TSX con exportaciones con nombre. + +```tsx +// ❌ Bad +const MyComponent = () => { + return
Hello World
; +}; +export default MyComponent; + +// ✅ Good +export function MyComponent() { + return
Hello World
; +}; +``` + +### "Props" + +Crea un tipo llamado `{ComponentName}Props`. Usa la desestructuración. No uses `React.FC`. + +```tsx +type MyComponentProps = { + name: string; +}; + +export const MyComponent = ({ name }: MyComponentProps) =>
Hello {name}
; +``` + +### Sin propagación de props de una sola variable + +```tsx +// ❌ Bad +const MyComponent = (props: MyComponentProps) => ; + +// ✅ Good +const MyComponent = ({ prop1, prop2 }: MyComponentProps) => ; +``` + +## Gestión del Estado + +### Átomos de Jotai para estado global + +```tsx +import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; +import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; + +export const myAtomState = createAtomState({ + key: 'myAtomState', + defaultValue: 'default value', +}); +``` + +* Prefiere átomos en lugar de prop drilling +* No uses `useRef` para estado — usa `useState` o átomos +* Usa familias de átomos y selectores para listas + +### Evita renderizados innecesarios + +* Extrae `useEffect` y la obtención de datos en componentes sidecar hermanos +* Prefiere los manejadores de eventos (`handleClick`, `handleChange`) en lugar de `useEffect` +* No uses `React.memo()` — en su lugar, corrige la causa raíz +* Limita el uso de `useCallback` / `useMemo` + +```tsx +// ❌ Bad — useEffect in the same component causes re-renders +export const Page = () => { + const [data, setData] = useAtomState(dataState); + const [dep] = useAtomState(depState); + useEffect(() => { setData(dep); }, [dep]); + return
{data}
; +}; + +// ✅ Good — extract into sibling +export const PageData = () => { + const [data, setData] = useAtomState(dataState); + const [dep] = useAtomState(depState); + useEffect(() => { setData(dep); }, [dep]); + return <>; +}; +export const Page = () => { + const [data] = useAtomState(dataState); + return
{data}
; +}; +``` + +## TypeScript + +* **`type` mejor que `interface`** — más flexible, más fácil de componer +* **Literales de cadena mejor que enums** — excepto para los enums de codegen de GraphQL y las APIs internas de la biblioteca +* **Sin `any`** — TypeScript estricto obligatorio +* **Sin importaciones de tipos** — usa importaciones normales (aplicado por Oxlint `typescript/consistent-type-imports`) +* **Usa [Zod](https://github.com/colinhacks/zod)** para la validación en tiempo de ejecución de objetos no tipados + +## JavaScript + +```tsx +// Use nullish-coalescing (??) instead of || +const value = process.env.MY_VALUE ?? 'default'; + +// Use optional chaining +onClick?.(); +``` + +## Nomenclatura + +* **Variables**: camelCase, descriptivas (`email` no `value`, `fieldMetadata` no `fm`) +* **Constantes**: SCREAMING_SNAKE_CASE +* **Tipos/Clases**: PascalCase +* **Archivos/directorios**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`) +* **Manejadores de eventos**: `handleClick` (no `onClick` para la función manejadora) +* **Props de componentes**: anteponer el nombre del componente (`ButtonProps`) +* **Componentes con estilo**: anteponer `Styled` (`StyledTitle`) + +## Estilo + +Usa componentes con estilo de [Linaria](https://github.com/callstack/linaria). Usa valores del tema — evita `px`, `rem` o colores fijos. + +```tsx +// ❌ Bad +const StyledButton = styled.button` + color: #333333; + font-size: 1rem; + margin-left: 4px; +`; + +// ✅ Good +const StyledButton = styled.button` + color: ${({ theme }) => theme.font.color.primary}; + font-size: ${({ theme }) => theme.font.size.md}; + margin-left: ${({ theme }) => theme.spacing(1)}; +`; +``` + +## Importar + +Usa alias en lugar de rutas relativas: + +```tsx +// ❌ Bad +import { Foo } from '../../../../../testing/decorators/Foo'; + +// ✅ Good +import { Foo } from '~/testing/decorators/Foo'; +import { Bar } from '@/modules/bar/components/Bar'; +``` + +## Estructura de carpetas + +``` +front +└── modules/ # Feature modules +│ └── module1/ +│ ├── components/ +│ ├── constants/ +│ ├── contexts/ +│ ├── graphql/ (fragments, queries, mutations) +│ ├── hooks/ +│ ├── states/ (atoms, selectors) +│ ├── types/ +│ └── utils/ +└── pages/ # Route-level components +└── ui/ # Reusable UI components (display, input, feedback, ...) +``` + +* Los módulos pueden importar desde otros módulos, pero `ui/` debe permanecer sin dependencias +* Usa subcarpetas `internal/` para código privado del módulo +* Componentes de menos de 300 líneas, servicios de menos de 500 líneas diff --git a/packages/twenty-docs/l/es/developers/extend/api.mdx b/packages/twenty-docs/l/es/developers/extend/api.mdx new file mode 100644 index 0000000000..0319557543 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/api.mdx @@ -0,0 +1,55 @@ +--- +title: APIs +icon: plug +description: APIs REST y GraphQL generadas a partir del esquema de tu espacio de trabajo. +--- + +import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; + +## APIs con esquema por tenant + +No existe una referencia estática de la API para Twenty. Cada espacio de trabajo tiene su propio esquema — cuando agregas un objeto personalizado (por ejemplo, `Invoice`), inmediatamente obtiene endpoints REST y GraphQL idénticos a los objetos integrados como `Company` o `Person`. La API se genera a partir del esquema, por lo que los endpoints usan directamente los nombres de tus objetos y campos — sin identificadores opacos. + +La documentación de la API específica de tu espacio de trabajo está disponible en **Configuración → APIs y Webhooks** después de crear una clave de API. Incluye un entorno de pruebas interactivo donde puedes ejecutar llamadas reales contra tus datos. + +## Dos APIs + +**API principal** — `/rest/` y `/graphql/` + +CRUD sobre registros: Personas, Empresas, Oportunidades y tus objetos personalizados. Consultar, filtrar, recorrer relaciones. + +**API de metadatos** — `/rest/metadata/` y `/metadata/` + +Gestión del esquema: crear/modificar/eliminar objetos, campos y relaciones. Así es como cambias tu modelo de datos de forma programática. + +Ambas están disponibles como REST y GraphQL. GraphQL añade operaciones upsert por lotes y la capacidad de recorrer relaciones en una única consulta. Los mismos datos subyacentes en ambos casos. + +## URLs base + +| Entorno | URL base | +| --------------- | ------------------------- | +| Nube | `https://api.twenty.com/` | +| Autoalojamiento | `https://{your-domain}/` | + +## Autenticación + +``` +Authorization: Bearer YOUR_API_KEY +``` + +Crea una clave de API en **Configuración → APIs y Webhooks → + Create key**. Cópiala de inmediato — se muestra una sola vez. Las claves pueden acotarse a un rol específico en **Configuración → Members → Roles → Assignment tab** para limitar a qué pueden acceder. + + + +Para el acceso basado en OAuth (aplicaciones externas que actúan en nombre de los usuarios), consulta [OAuth](/l/es/developers/extend/oauth). + +## Operaciones por lotes + +Tanto REST como GraphQL admiten el procesamiento por lotes de hasta 60 registros por solicitud — crear, actualizar o eliminar. GraphQL también admite upsert por lotes (crear o actualizar en una sola llamada) usando nombres en plural como `CreateCompanies`. + +## Límites de tasa + +| Límite | Valor | +| --------------- | -------------------------- | +| Solicitudes | 100 solicitudes por minuto | +| Tamaño del lote | 60 registros por llamada | diff --git a/packages/twenty-docs/l/es/developers/extend/apps/config/application.mdx b/packages/twenty-docs/l/es/developers/extend/apps/config/application.mdx new file mode 100644 index 0000000000..b67c15ca5f --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/config/application.mdx @@ -0,0 +1,64 @@ +--- +title: Configuración de la aplicación +description: Declara la identidad de tu aplicación, el rol predeterminado, las variables y los metadatos del marketplace con defineApplication. +icon: rocket +--- + +Cada aplicación debe tener exactamente una llamada a `defineApplication`. Declara: + +* **Identidad** — identificador universal, nombre para mostrar, descripción. +* **Permisos** — bajo qué rol se ejecutan sus funciones de lógica y componentes de frontend. +* **Variables** *(opcionales)* — pares clave–valor expuestos a tu código como variables de entorno. +* **Hooks de preinstalación / postinstalación** *(opcionales)* — consulta [Funciones de lógica](/l/es/developers/extend/apps/logic/logic-functions). + +```ts src/application-config.ts +import { defineApplication } from 'twenty-sdk/define'; + +export default defineApplication({ + universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d', + displayName: 'My Twenty App', + description: 'My first Twenty app', + applicationVariables: { + DEFAULT_RECIPIENT_NAME: { + universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de', + description: 'Default recipient name for postcards', + value: 'Jane Doe', + isSecret: false, + }, + }, +}); +``` + +Notas: + +* Los campos `universalIdentifier` son identificadores deterministas que te pertenecen. Genéralos una vez y mantenlos estables entre sincronizaciones. +* `applicationVariables` se convierten en variables de entorno para tus funciones y componentes de frontend. En las funciones lógicas (del lado del servidor), están disponibles como `process.env.VARIABLE_NAME`. En los componentes de frontend, usa `getApplicationVariable('VARIABLE_NAME')` de `twenty-sdk/front-component`. Las variables marcadas con `isSecret: true` solo se inyectan en las funciones lógicas. Los componentes de frontend solo reciben variables no secretas. +* El rol predeterminado se detecta automáticamente a partir del archivo de rol marcado con [`defineApplicationRole()`](/l/es/developers/extend/apps/config/roles); no necesitas hacer referencia a él desde `defineApplication()`. +* Las funciones de preinstalación y posinstalación se detectan automáticamente durante la compilación del manifiesto; no necesitas referenciarlas en `defineApplication()`. +* Pasar `defaultRoleUniversalIdentifier` explícitamente sigue siendo compatible por motivos de retrocompatibilidad, pero está en desuso en favor de `defineApplicationRole()`. + +## Rol de función predeterminado + +El rol declarado con [`defineApplicationRole()`](/l/es/developers/extend/apps/config/roles) controla a qué pueden acceder las funciones de lógica y los componentes de interfaz de la aplicación: + +* El token en tiempo de ejecución inyectado como `TWENTY_APP_ACCESS_TOKEN` se deriva de este rol. +* El cliente de API tipado está restringido a los permisos otorgados a ese rol. +* Sigue el principio de mínimo privilegio: declara solo los permisos que necesitan tus funciones. + +Cuando generas una nueva aplicación, la CLI crea un archivo de rol inicial en `src/roles/default-role.ts`. Consulta [Roles y permisos](/l/es/developers/extend/apps/config/roles) para obtener la referencia completa. + +## Metadatos del Marketplace + +Si planeas [publicar tu aplicación](/l/es/developers/extend/apps/operations/publishing), estos campos opcionales controlan cómo aparece en el marketplace: + +| Campo | Descripción | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `author` | Nombre del autor o de la empresa | +| `category` | Categoría de la aplicación para el filtrado en el marketplace | +| `logoUrl` | Ruta al logotipo de tu aplicación (p. ej., `public/logo.png`) | +| `screenshots` | Arreglo de rutas de capturas de pantalla (p. ej., `public/screenshot-1.png`) | +| `aboutDescription` | Descripción en Markdown más extensa para la pestaña "Acerca de". Si se omite, el marketplace utiliza el `README.md` del paquete en npm | +| `websiteUrl` | Enlace a tu sitio web | +| `termsUrl` | Enlace a los términos del servicio | +| `emailSupport` | Dirección de correo electrónico de soporte | +| `issueReportUrl` | Enlace al rastreador de incidencias | diff --git a/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx b/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx new file mode 100644 index 0000000000..3b1ba0f235 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/config/install-hooks.mdx @@ -0,0 +1,206 @@ +--- +title: Hooks de instalación +description: "Ejecuta lógica antes o después de la instalación: introduce datos iniciales, haz copias de seguridad de los registros, valida la actualización." +icon: llave inglesa +--- + +Los hooks de instalación son funciones de lógica especiales que se ejecutan durante el ciclo de vida de la instalación o actualización. Comparten el mismo tiempo de ejecución del controlador que las [logic functions](/l/es/developers/extend/apps/logic/logic-functions) normales y reciben un `InstallPayload`, pero se declaran con sus propias funciones de definición — `definePostInstallLogicFunction()` y `definePreInstallLogicFunction()` — y están fuera del modelo de desencadenadores normal (HTTP, cron, eventos de base de datos). + +Cada aplicación puede definir **como máximo una función de preinstalación** y **como máximo una función de posinstalación**. La compilación del manifiesto generará un error si se detecta más de una de cualquiera de las dos. + +``` +┌─────────────────────────────────────────────────────────────┐ +│ install flow │ +│ │ +│ upload package → [pre-install] → metadata migration → │ +│ generate SDK → [post-install] │ +│ │ +│ old schema visible new schema visible │ +└─────────────────────────────────────────────────────────────┘ +``` + + + + +Una función de posinstalación se ejecuta automáticamente una vez que tu aplicación ha terminado de instalarse en un espacio de trabajo. El servidor la ejecuta **después** de que se hayan sincronizado los metadatos de la aplicación y se haya generado el cliente del SDK, de modo que el espacio de trabajo esté completamente listo para usarse y el nuevo esquema esté disponible. Los casos de uso típicos incluyen poblar datos predeterminados, crear registros iniciales, configurar los ajustes del espacio de trabajo o aprovisionar recursos en servicios de terceros. + +```ts src/logic-functions/post-install.ts +import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; + +const handler = async (payload: InstallPayload): Promise => { + console.log('Post install logic function executed successfully!', payload.previousVersion); +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210', + name: 'post-install', + description: 'Runs after installation to set up the application.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + shouldRunSynchronously: false, + handler, +}); +``` + +También puedes ejecutar manualmente la función de posinstalación en cualquier momento usando la CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec --postInstall +``` + +Puntos clave: +* Las funciones de posinstalación usan `definePostInstallLogicFunction()` — una variante especializada que omite la configuración de desencadenadores (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `toolTriggerSettings`, `workflowActionTriggerSettings`). +* El controlador recibe un `InstallPayload` con `{ previousVersion?: string; newVersion: string }` — `newVersion` es la versión que se está instalando, y `previousVersion` es la versión que se instaló previamente (o `undefined` en una instalación nueva). Use estos valores para distinguir instalaciones nuevas de actualizaciones y para ejecutar lógica de migración específica de la versión. +* **Cuándo se ejecuta el hook**: solo en instalaciones nuevas, de forma predeterminada. Pase `shouldRunOnVersionUpgrade: true` si también quiere que se ejecute cuando la app se actualice desde una versión anterior. Si se omite, el indicador es `false` por defecto y las actualizaciones omiten el hook. +* **Modelo de ejecución — asíncrono por defecto, sincronía opcional**: el indicador `shouldRunSynchronously` controla *cómo* se ejecuta la post-instalación. + * `shouldRunSynchronously: false` *(predeterminado)* — el hook se **encola en la cola de mensajes** con `retryLimit: 3` y se ejecuta de forma asíncrona en un worker. La respuesta de instalación se devuelve tan pronto como el trabajo se encola, por lo que un controlador lento o con fallos no bloquea al solicitante. El worker reintentará hasta tres veces. **Úselo para trabajos de larga duración** — sembrar conjuntos de datos grandes, llamar a APIs de terceros lentas, aprovisionar recursos externos, cualquier cosa que pueda exceder una ventana de respuesta HTTP razonable. + * `shouldRunSynchronously: true` — el hook se ejecuta **en línea durante el flujo de instalación** (el mismo ejecutor que la pre-instalación). La solicitud de instalación se bloquea hasta que el controlador finaliza y, si arroja una excepción, quien realiza la instalación recibe un `POST_INSTALL_ERROR`. Sin reintentos automáticos. **Úselo para trabajo rápido que debe completarse antes de la respuesta** — por ejemplo, emitir un error de validación al usuario, o una configuración rápida de la que el cliente dependerá inmediatamente después de que regrese la llamada de instalación. Tenga en cuenta que la migración de metadatos ya se ha aplicado cuando se ejecuta la post-instalación, por lo que un fallo en modo síncrono **no** revierte los cambios de esquema — solo expone el error. +* Asegúrese de que su controlador sea idempotente. En modo asíncrono, la cola puede reintentar hasta tres veces; en cualquier modo, el hook puede ejecutarse de nuevo en las actualizaciones cuando `shouldRunOnVersionUpgrade: true`. +* Las variables de entorno `APPLICATION_ID`, `APP_ACCESS_TOKEN` y `API_URL` están disponibles dentro del controlador (igual que en cualquier otra función de lógica), por lo que puede llamar a la API de Twenty con un token de acceso de aplicación con alcance a su app. +* Solo se permite una función de posinstalación por aplicación. La compilación del manifiesto generará un error si se detecta más de una. +* Los `universalIdentifier`, `shouldRunOnVersionUpgrade` y `shouldRunSynchronously` de la función se adjuntan automáticamente al manifiesto de la aplicación en el campo `postInstallLogicFunction` durante la compilación; no es necesario que los referencies en [`defineApplication()`](/l/es/developers/extend/apps/config/application). +* El tiempo de espera predeterminado se establece en 300 segundos (5 minutos) para permitir tareas de configuración más largas como la carga inicial de datos. +* **No se ejecuta en modo de desarrollo**: cuando una app se registra localmente (mediante `yarn twenty dev`), el servidor omite por completo el flujo de instalación y sincroniza archivos directamente a través del observador de la CLI — por lo tanto, la post-instalación nunca se ejecuta en modo de desarrollo, independientemente de `shouldRunSynchronously`. Use `yarn twenty dev:function:exec --postInstall` para activarlo manualmente en un espacio de trabajo en ejecución. + + + + +Una función de preinstalación se ejecuta automáticamente durante la instalación, **antes de que se aplique la migración de metadatos del espacio de trabajo**. Comparte la misma forma de payload que la post-instalación (`InstallPayload`), pero está situada antes en el flujo de instalación para poder preparar el estado del que depende la próxima migración — usos típicos incluyen hacer copias de seguridad de datos, validar la compatibilidad con el nuevo esquema o archivar registros que están a punto de ser reestructurados o eliminados. + +```ts src/logic-functions/pre-install.ts +import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; + +const handler = async (payload: InstallPayload): Promise => { + console.log('Pre install logic function executed successfully!', payload.previousVersion); +}; + +export default definePreInstallLogicFunction({ + universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab', + name: 'pre-install', + description: 'Runs before installation to prepare the application.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: true, + handler, +}); +``` + +También puedes ejecutar manualmente la función de preinstalación en cualquier momento usando la CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec --preInstall +``` + +Puntos clave: +* Las funciones de pre-instalación usan `definePreInstallLogicFunction()` — la misma configuración especializada que la post-instalación, solo que adjunta a un punto diferente del ciclo de vida. +* Tanto los controladores de pre- como de post-instalación reciben el mismo tipo `InstallPayload`: `{ previousVersion?: string; newVersion: string }`. Impórtelo una vez y reutilícelo para ambos hooks. +* **Cuándo se ejecuta el hook**: se ubica justo antes de la migración de metadatos del espacio de trabajo (`synchronizeFromManifest`). Antes de ejecutarse, el servidor realiza una "sincronización simplificada" puramente aditiva que registra la función de pre-instalación de la versión **nueva** en los metadatos del espacio de trabajo — no se toca nada más — y luego la ejecuta. Debido a que esta sincronización es solo aditiva, los objetos, campos y datos de la versión anterior siguen intactos cuando se ejecuta su controlador: puede leer y respaldar de forma segura el estado premigración. +* **Modelo de ejecución**: la pre-instalación se ejecuta **de forma síncrona** y **bloquea la instalación**. Si el controlador lanza una excepción, la instalación se aborta antes de que se apliquen cambios de esquema — el espacio de trabajo permanece en la versión anterior en un estado consistente. Esto es intencional: la pre-instalación es su última oportunidad para rechazar una actualización arriesgada. +* Al igual que con la post-instalación, solo se permite una función de preinstalación por aplicación. Se adjunta automáticamente al manifiesto de la aplicación bajo `preInstallLogicFunction` durante la compilación. +* **No se ejecuta en modo de desarrollo**: igual que la post-instalación — el flujo de instalación se omite por completo para las apps registradas localmente, por lo que la pre-instalación nunca se ejecuta con `yarn twenty dev`. Use `yarn twenty dev:function:exec --preInstall` para activarlo manualmente. + + + + +Ambos hooks forman parte del mismo flujo de instalación y reciben el mismo `InstallPayload`. La diferencia es **cuándo** se ejecutan con respecto a la migración de metadatos del espacio de trabajo, y eso cambia qué datos pueden tocar de forma segura. + +La pre-instalación siempre es **síncrona** (bloquea la instalación y puede abortarla). La post-instalación es **asíncrona por defecto** — se pone en cola en un worker con reintentos automáticos — pero puede optar por ejecución síncrona con `shouldRunSynchronously: true`. Consulte el acordeón `definePostInstallLogicFunction` de arriba para saber cuándo usar cada modo. + +**Use `post-install` para cualquier cosa que necesite que exista el nuevo esquema.** Este es el caso más común: + +* Sembrar datos predeterminados (crear registros iniciales, vistas predeterminadas, contenido de demostración) sobre objetos y campos recién añadidos. +* Registrar webhooks con servicios de terceros ahora que la app ya tiene sus credenciales. +* Llamar a su propia API para finalizar una configuración que depende de los metadatos sincronizados. +* Lógica idempotente de "asegurar que esto exista" que debe reconciliar el estado en cada actualización — combínela con `shouldRunOnVersionUpgrade: true`. + +Ejemplo — sembrar un registro `PostCard` predeterminado después de la instalación: + +```ts src/logic-functions/post-install.ts +import { definePostInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +import { createClient } from './generated/client'; + +const handler = async ({ previousVersion }: InstallPayload): Promise => { + if (previousVersion) return; // fresh installs only + + const client = createClient(); + await client.postCard.create({ + data: { title: 'Welcome to Postcard', content: 'Your first card!' }, + }); +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f7a2b9c1-3d4e-5678-abcd-ef9876543210', + name: 'post-install', + description: 'Seeds a welcome post card after install.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + handler, +}); +``` + +**Use `pre-install` cuando una migración, de otro modo, destruiría o corrompería datos existentes.** Como la pre-instalación se ejecuta contra el esquema *anterior* y su fallo revierte la actualización, es el lugar adecuado para cualquier cosa arriesgada: + +* **Hacer copia de seguridad de datos que están a punto de eliminarse o reestructurarse** — p. ej., está quitando un campo en la v2 y necesita copiar sus valores a otro campo o exportarlos a almacenamiento antes de que se ejecute la migración. +* **Archivar registros que una nueva restricción invalidaría** — p. ej., un campo pasará a ser `NOT NULL` y primero necesita eliminar o corregir filas con valores nulos. +* **Validar la compatibilidad y rechazar la actualización si los datos actuales no pueden migrarse limpiamente** — lance desde el controlador y la instalación se abortará sin aplicar cambios. Esto es más seguro que descubrir la incompatibilidad a mitad de la migración. +* **Renombrar o reasignar claves de datos** antes de un cambio de esquema que perdería la asociación. + +Ejemplo — archivar registros antes de una migración destructiva: + +```ts src/logic-functions/pre-install.ts +import { definePreInstallLogicFunction, type InstallPayload } from 'twenty-sdk/define'; +import { createClient } from './generated/client'; + +const handler = async ({ previousVersion, newVersion }: InstallPayload): Promise => { + // Only the 1.x → 2.x upgrade drops the legacy `notes` field. + if (!previousVersion?.startsWith('1.') || !newVersion.startsWith('2.')) { + return; + } + + const client = createClient(); + const legacyRecords = await client.postCard.findMany({ + where: { notes: { isNotNull: true } }, + }); + + if (legacyRecords.length === 0) return; + + // Copy legacy `notes` into the new `description` field before the migration + // drops the `notes` column. If this fails, the upgrade is aborted and the + // workspace stays on v1 with all data intact. + await Promise.all( + legacyRecords.map((record) => + client.postCard.update({ + where: { id: record.id }, + data: { description: record.notes }, + }), + ), + ); +}; + +export default definePreInstallLogicFunction({ + universalIdentifier: 'a1b2c3d4-5678-90ab-cdef-1234567890ab', + name: 'pre-install', + description: 'Backs up legacy notes into description before the v2 migration.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: true, + handler, +}); +``` + +**Regla general:** + +| Quiere... | Usar | +| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| Sembrar datos predeterminados, configurar el espacio de trabajo, registrar recursos externos | `post-install` | +| Ejecutar siembras de larga duración o llamadas a terceros que no deberían bloquear la respuesta de instalación | `post-install` (predeterminado — `shouldRunSynchronously: false`, con reintentos del worker) | +| Ejecutar una configuración rápida de la que el cliente dependerá inmediatamente después de que regrese la llamada de instalación | `post-install` con `shouldRunSynchronously: true` | +| Leer o hacer copia de seguridad de datos que la próxima migración perdería | `pre-install` | +| Rechazar una actualización que corrompería datos existentes | `pre-install` (lanzar desde el controlador) | +| Ejecutar reconciliación en cada actualización | `post-install` con `shouldRunOnVersionUpgrade: true` | +| Realizar una configuración única solo en la primera instalación | `post-install` con `shouldRunOnVersionUpgrade: false` (predeterminado) | + + +En caso de duda, elija **post-install** como predeterminado. Recurra a la pre-instalación solo cuando la propia migración sea destructiva y necesite interceptar el estado anterior antes de que desaparezca. + + + + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/config/overview.mdx b/packages/twenty-docs/l/es/developers/extend/apps/config/overview.mdx new file mode 100644 index 0000000000..6d8fa766db --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/config/overview.mdx @@ -0,0 +1,51 @@ +--- +title: Resumen +description: "Configura la propia app: su identidad, los permisos predeterminados y lo que se ejecuta en el momento de la instalación." +icon: screwdriver-wrench +--- + +La **capa de configuración** de una app de Twenty es lo que describe la app *a la plataforma*: su identidad, los permisos que posee y el código que se ejecuta durante la instalación o la actualización. Estas declaraciones no añaden nuevas estructuras de datos ni comportamiento en tiempo de ejecución; le indican a Twenty *quién es la app* y *cómo configurarla*. + +```text +┌────────────────────────────────────────────────────────┐ +│ Application — identity, default role, variables, │ +│ marketplace metadata │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Role — what the app's logic functions can read │ │ +│ │ and write (referenced by Application) │ │ +│ └──────────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────┘ + │ + ▼ (at install / upgrade time) + ┌──────────────────────────────────┐ + │ Pre-install hook │ before metadata migration + └──────────────────────────────────┘ + ┌──────────────────────────────────┐ + │ Post-install hook │ after metadata migration + └──────────────────────────────────┘ +``` + +## En esta sección + + + + `defineApplication`: identidad, rol predeterminado, variables y metadatos del marketplace. + + + `defineRole`: declara qué pueden leer y escribir las funciones lógicas de tu app. + + + `definePreInstallLogicFunction` y `definePostInstallLogicFunction`: hacen copias de seguridad de los datos, cargan valores predeterminados y validan actualizaciones. + + + +## Cómo se relacionan las piezas + +* **Application** es el punto de entrada. Cada app tiene exactamente una llamada a `defineApplication()`, y apunta a un **Role** como su valor predeterminado. +* El **Role** controla qué pueden leer y escribir las funciones lógicas y los componentes de interfaz de la app. Sigue el principio de privilegios mínimos: concede solo los permisos que tu código realmente necesita. +* Los **hooks de instalación** se ejecutan durante la instalación o la actualización: el hook de preinstalación antes de la migración de metadatos (para poder rechazar una actualización arriesgada) y el hook de postinstalación después de la migración (para poder cargar datos predeterminados con el nuevo esquema). + + +Los hooks de instalación comparten el entorno de ejecución de la [función lógica](/l/es/developers/extend/apps/logic/logic-functions): misma firma del handler, mismas variables de entorno, mismo cliente de API tipado, pero se declaran con sus propias funciones de definición y viven fuera del modelo de triggers habitual (HTTP, cron, eventos de base de datos). + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/config/public-assets.mdx b/packages/twenty-docs/l/es/developers/extend/apps/config/public-assets.mdx new file mode 100644 index 0000000000..b9f81d11ec --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/config/public-assets.mdx @@ -0,0 +1,67 @@ +--- +title: Recursos públicos +description: Distribuye archivos estáticos — imágenes, íconos, fuentes — junto con tu aplicación mediante la carpeta `public/`. +icon: folder-open +--- + +La carpeta `public/` en la raíz de tu aplicación contiene archivos estáticos: imágenes, íconos, fuentes o cualquier otro recurso que tu aplicación necesite en tiempo de ejecución. Estos archivos se incluyen automáticamente en las compilaciones, se sincronizan durante el modo de desarrollo y se suben al servidor. + +Los archivos ubicados en `public/` son: + +* **De acceso público** — una vez sincronizados con el servidor, los recursos se sirven en una URL pública. No se necesita autenticación para acceder a ellos. +* **Disponibles en componentes de frontend** — usa las URLs de los recursos para mostrar imágenes, íconos o cualquier medio dentro de tus componentes de React. +* **Disponibles en funciones de lógica** — referencia las URLs de los recursos en correos electrónicos, respuestas de API o cualquier lógica del lado del servidor. +* **Usados para metadatos del marketplace** — los campos `logoUrl` y `screenshots` en `defineApplication()` referencian archivos de esta carpeta (p. ej., `public/logo.png`). Estos se muestran en el marketplace cuando se publica tu aplicación. +* **Sincronizados automáticamente en modo de desarrollo** — cuando agregas, actualizas o eliminas un archivo en `public/`, se sincroniza automáticamente con el servidor. No se necesita reiniciar. +* **Incluidos en las compilaciones** — `yarn twenty dev:build` agrupa todos los recursos públicos en la salida de distribución. + +## Acceder a recursos públicos con `getPublicAssetUrl` + +Usa el helper `getPublicAssetUrl` de `twenty-sdk` para obtener la URL completa de un archivo en tu directorio `public/`. Funciona tanto en **funciones de lógica** como en **componentes de frontend**. + +**En una función de lógica:** + +```ts src/logic-functions/send-invoice.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const handler = async (): Promise => { + const logoUrl = getPublicAssetUrl('logo.png'); + const invoiceUrl = getPublicAssetUrl('templates/invoice.png'); + + // Fetch the file content (no auth required — public endpoint) + const response = await fetch(invoiceUrl); + const buffer = await response.arrayBuffer(); + + return { logoUrl, size: buffer.byteLength }; +}; + +export default defineLogicFunction({ + universalIdentifier: 'a1b2c3d4-...', + name: 'send-invoice', + description: 'Sends an invoice with the app logo', + timeoutSeconds: 10, + handler, +}); +``` + +**En un componente de frontend:** + +```tsx src/front-components/company-card.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const CompanyCard = () => { + const logoUrl = getPublicAssetUrl('logo.png'); + + return App logo; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'company-card', + component: CompanyCard, +}); +``` + +El argumento `path` es relativo a la carpeta `public/` de tu aplicación. Tanto `getPublicAssetUrl('logo.png')` como `getPublicAssetUrl('public/logo.png')` resuelven a la misma URL — el prefijo `public/` se elimina automáticamente si está presente. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/config/roles.mdx b/packages/twenty-docs/l/es/developers/extend/apps/config/roles.mdx new file mode 100644 index 0000000000..eda6e97a72 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/config/roles.mdx @@ -0,0 +1,94 @@ +--- +title: Roles y permisos +description: Declara qué objetos y campos pueden leer y escribir las funciones de lógica y los componentes de interfaz de tu aplicación. +icon: shield-halved +--- + +Un **rol** es un conjunto de permisos: qué objetos puede leer o escribir una aplicación, qué campos puede ver y qué capacidades a nivel de plataforma puede usar. Las funciones lógicas y los componentes de interfaz de cada aplicación heredan los permisos del rol marcado con `defineApplicationRole()` (consulta [El rol de función predeterminado](#the-default-function-role) más abajo). + +```ts src/roles/restricted-company-role.ts +import { + defineRole, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + SystemPermissionFlag, +} from 'twenty-sdk/define'; + +export default defineRole({ + universalIdentifier: '2c80f640-2083-4803-bb49-003e38279de6', + label: 'My new role', + description: 'A role that can be used in your workspace', + canReadAllObjectRecords: false, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canUpdateAllSettings: false, + canBeAssignedToAgents: false, + canBeAssignedToUsers: false, + canBeAssignedToApiKeys: false, + objectPermissions: [ + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + ], + fieldPermissions: [ + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + fieldUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name + .universalIdentifier, + canReadFieldValue: false, + canUpdateFieldValue: false, + }, + ], + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS], +}); +``` + +## El rol de función predeterminado + +Cuando generas una nueva aplicación, la CLI crea un archivo de rol predeterminado declarado con `defineApplicationRole()`: + +```ts src/roles/default-role.ts +import { defineApplicationRole } from 'twenty-sdk/define'; + +export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = + 'b648f87b-1d26-4961-b974-0908fd991061'; + +export default defineApplicationRole({ + universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, + label: 'Default function role', + description: 'Default role for function Twenty client', + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canUpdateAllSettings: false, + canBeAssignedToAgents: false, + canBeAssignedToUsers: false, + canBeAssignedToApiKeys: false, + objectPermissions: [], + fieldPermissions: [], + permissionFlagUniversalIdentifiers: [], +}); +``` + +`defineApplicationRole()` es una envoltura ligera alrededor de `defineRole()` que marca **el** rol utilizado como predeterminado de tu aplicación en el momento de la instalación. La validación es idéntica a `defineRole`, pero la canalización de compilación conecta automáticamente su `universalIdentifier` con `defaultRoleUniversalIdentifier` del manifiesto de la aplicación, por lo que no necesitas hacer referencia a él desde [`defineApplication`](/l/es/developers/extend/apps/config/application). + +Notas: + +* Se permite exactamente **una** llamada a `defineApplicationRole(...)` por aplicación; la compilación del manifiesto fallará si encuentra más de una. +* Usa `defineRole()` (no `defineApplicationRole()`) para cualquier rol **adicional** que distribuya tu aplicación. +* Configurar `defaultRoleUniversalIdentifier` explícitamente en `defineApplication()` sigue siendo compatible por motivos de retrocompatibilidad, pero está en desuso en favor de `defineApplicationRole()`. + +## Mejores prácticas + +* Parte del rol generado automáticamente y luego restríngeelo progresivamente; el valor predeterminado concede un acceso amplio de lectura, lo cual rara vez es lo que quieres en producción. +* Reemplaza `objectPermissions` y `fieldPermissions` con los objetos y campos que realmente necesitan tus funciones. +* `permissionFlagUniversalIdentifiers` controla el acceso a capacidades a nivel de plataforma. Manténlos al mínimo. +* Consulta un ejemplo funcional: [`hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts). diff --git a/packages/twenty-docs/l/es/developers/extend/apps/data/extending-objects.mdx b/packages/twenty-docs/l/es/developers/extend/apps/data/extending-objects.mdx new file mode 100644 index 0000000000..c3efb83629 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/data/extending-objects.mdx @@ -0,0 +1,50 @@ +--- +title: Extender objetos +description: Añade campos a los objetos estándar de Twenty (Person, Company, …) o a objetos de otras aplicaciones usando defineField. +icon: wand-magic-sparkles +--- + +Usa `defineField()` para añadir un campo a un objeto que no te pertenece — un objeto estándar de Twenty como Person o Company, u otro objeto proporcionado por otra aplicación instalada. A diferencia de los campos en línea declarados dentro de [`defineObject`](/l/es/developers/extend/apps/data/objects), los campos independientes requieren un `objectUniversalIdentifier` para especificar qué objeto extienden. + +```ts src/fields/company-loyalty-tier.field.ts +import { defineField, FieldType } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890', + objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object + name: 'loyaltyTier', + type: FieldType.SELECT, + label: 'Loyalty Tier', + icon: 'IconStar', + options: [ + { value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' }, + { value: 'SILVER', label: 'Silver', position: 1, color: 'gray' }, + { value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' }, + ], +}); +``` + +## Puntos clave + +* `objectUniversalIdentifier` identifica el objeto de destino. Para los objetos estándar de Twenty, importa la constante desde `twenty-sdk`: + + ```ts + import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define'; + + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier + // STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier + // … + ``` + +* Al definir campos **en línea dentro de `defineObject()`**, **no** necesitas `objectUniversalIdentifier` — se hereda del objeto padre. + +* `defineField()` es la única forma de añadir campos a objetos que no creaste con `defineObject()`. + +* La ubicación del archivo depende de ti. La convención es `src/fields/\.field.ts`, pero el SDK detecta campos en cualquier lugar de `src/`. + +* Para agregar una pestaña a un diseño de página estándar (por ejemplo, la página de detalles de la Tarea o de la Empresa), usa [`definePageLayoutTab`](/l/es/developers/extend/apps/layout/page-layouts#definepagelayouttab) con `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS` de `twenty-sdk/define`. + +## Añadir una relación a un objeto existente + +Para añadir un campo de relación (por ejemplo, vinculando tu objeto personalizado a un `Person` estándar), usa `defineField()` con `FieldType.RELATION`. El patrón es el mismo que para las relaciones en línea, pero con `objectUniversalIdentifier` establecido explícitamente. Consulta [Relaciones](/l/es/developers/extend/apps/data/relations) para conocer el patrón bidireccional. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx b/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx new file mode 100644 index 0000000000..d9edf1f2f0 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/data/objects.mdx @@ -0,0 +1,104 @@ +--- +title: Objetos +description: Declara nuevos tipos de registro (tablas personalizadas con sus propios campos) usando defineObject. +icon: tabla +--- + +Los **objetos** personalizados son nuevos tipos de registro que tu aplicación añade a un espacio de trabajo — Tarjeta postal, Factura, Suscripción, cualquier cosa específica de tu dominio. Cada objeto declara su esquema (campos, relaciones, valores predeterminados) y un identificador universal estable que se mantiene a través de sincronizaciones e implementaciones. + +```ts src/objects/post-card.object.ts +import { defineObject, FieldType } from 'twenty-sdk/define'; + +enum PostCardStatus { + DRAFT = 'DRAFT', + SENT = 'SENT', + DELIVERED = 'DELIVERED', + RETURNED = 'RETURNED', +} + +export default defineObject({ + universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05', + nameSingular: 'postCard', + namePlural: 'postCards', + labelSingular: 'Post Card', + labelPlural: 'Post Cards', + description: 'A post card object', + icon: 'IconMail', + fields: [ + { + universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b', + name: 'content', + type: FieldType.TEXT, + label: 'Content', + description: "Postcard's content", + icon: 'IconAbc', + }, + { + universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac', + name: 'recipientName', + type: FieldType.FULL_NAME, + label: 'Recipient name', + icon: 'IconUser', + }, + { + universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266', + name: 'recipientAddress', + type: FieldType.ADDRESS, + label: 'Recipient address', + icon: 'IconHome', + }, + { + universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e', + name: 'status', + type: FieldType.SELECT, + label: 'Status', + icon: 'IconSend', + defaultValue: `'${PostCardStatus.DRAFT}'`, + options: [ + { value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' }, + { value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' }, + { value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' }, + { value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' }, + ], + }, + { + universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433', + name: 'deliveredAt', + type: FieldType.DATE_TIME, + label: 'Delivered at', + icon: 'IconCheck', + isNullable: true, + defaultValue: null, + }, + ], +}); +``` + +## Puntos clave + +* El `universalIdentifier` debe ser único y estable entre implementaciones. +* Cada campo requiere `name`, `type`, `label` y su propio `universalIdentifier` estable. +* La matriz `fields` es opcional: puedes definir objetos sin campos personalizados. +* Los campos en línea definidos aquí **no** necesitan un `objectUniversalIdentifier`, ya que se hereda del objeto padre. Usa [`defineField()`](/l/es/developers/extend/apps/data/extending-objects) para añadir campos a objetos que no te pertenecen. +* Puedes generar nuevos objetos con `yarn twenty dev:add object`, que te guía en la asignación de nombres, los campos y las relaciones. Consulta [Arquitectura → Generación de entidades](/l/es/developers/extend/apps/getting-started/scaffolding). + + +**Los campos base se añaden automáticamente.** Cuando defines un objeto personalizado, Twenty crea campos estándar como `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` y `deletedAt` por ti. No necesitas declararlos en tu matriz `fields`, solo tus campos personalizados. Puedes sobrescribir un campo predeterminado declarando uno con el mismo nombre, pero esto rara vez es una buena idea. + + +## Valores predeterminados + +Los valores predeterminados de cadenas literales deben ir entre comillas simples **dentro** de la cadena — `defaultValue: "'Draft'"`, no `defaultValue: "Draft"`. Por eso el campo `status` anterior utiliza `` `'${PostCardStatus.DRAFT}'` ``. + +Las cadenas sin comillas se reservan para valores predeterminados calculados, evaluados cuando se crea un registro: + +* `'uuid'` — genera un UUID (para campos `UUID`) +* `'now'` — la marca de tiempo actual (para campos `DATE_TIME`) + +La misma convención se aplica a los subcampos de tipo cadena de los valores predeterminados compuestos (por ejemplo, `{ source: "'MANUAL'" }` en un campo `ACTOR`) y a los valores de `SELECT`/`MULTI_SELECT`. Un valor predeterminado de tipo cadena literal dejado sin comillas genera una advertencia cuando se compila tu aplicación. + +## ¿Qué sigue? + +* **Conecta este objeto con otros**: consulta [Relaciones](/l/es/developers/extend/apps/data/relations) para el patrón de relación bidireccional. +* **Añade campos a objetos de otras aplicaciones**: consulta [Extender objetos](/l/es/developers/extend/apps/data/extending-objects) para `defineField()`. +* **Muestra este objeto en la interfaz de usuario**: consulta [Vistas](/l/es/developers/extend/apps/layout/views) y [Elementos del menú de navegación](/l/es/developers/extend/apps/layout/navigation-menu-items) para colocarlo en la barra lateral. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/data/overview.mdx b/packages/twenty-docs/l/es/developers/extend/apps/data/overview.mdx new file mode 100644 index 0000000000..db4d13f69f --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/data/overview.mdx @@ -0,0 +1,97 @@ +--- +title: Resumen +description: "Da forma a los datos que tu aplicación agrega a un espacio de trabajo: objetos, campos y relaciones." +icon: database +--- + +La **capa de datos** de una aplicación de Twenty es el conjunto de datos que tu aplicación *agrega* a un espacio de trabajo — los nuevos tipos de registros que declara, las columnas que agrega a los objetos existentes y cómo esos registros se conectan entre sí. + +```text +┌──────────────────────────────────────────────────┐ +│ Object — a record type, e.g. PostCard │ +│ ├─ Field (name, type, label) │ +│ ├─ Field │ +│ └─ Relation (link to another object) │ +└──────────────────────────────────────────────────┘ + │ + ├── lives in your app, OR + │ + ▼ +┌──────────────────────────────────────────────────┐ +│ Standard / other apps' objects │ +│ └─ Field added by your app via defineField │ +└──────────────────────────────────────────────────┘ +``` + +## En esta sección + + + + `defineObject` — declara nuevos tipos de registros con sus propios campos. + + + `defineField` — agrega campos a objetos estándar o de otras aplicaciones. + + + Conexiones bidireccionales `MANY_TO_ONE` / `ONE_TO_MANY` entre objetos. + + + +## Entidades de un vistazo + +| Entidad | Propósito | Definido con | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| **Objeto** | Un nuevo tipo de registro personalizado (por ejemplo, PostCard, Invoice) con sus propios campos | `defineObject()` | +| **Campo** | Una columna en un objeto. Los campos independientes pueden ampliar objetos que no creaste (por ejemplo, agregar `loyaltyTier` a Company) | `defineField()` | +| **Relación** | Un vínculo bidireccional entre dos objetos: ambos lados se declaran como campos | `defineField()` con `FieldType.RELATION` | +| **Índice** | Un índice de base de datos para acelerar una consulta recurrente sobre uno de tus objetos | `defineIndex()` | + +El SDK detecta estos mediante análisis AST en tiempo de compilación, por lo que la organización de archivos depende de ti; la convención es `src/objects/`, `src/fields/` y `src/indexes/`. Los UUID `universalIdentifier` estables vinculan todo a través de los despliegues. + +## Índices (opcional) + +Las aplicaciones pueden incluir índices junto con sus objetos para mantener rápidas las consultas recurrentes. El caso más habitual es una columna de estado o de clave externa que lees con frecuencia. + +```ts src/indexes/post-card-status.index.ts +import { defineIndex } from 'twenty-sdk/define'; + +import { + POST_CARD_UNIVERSAL_IDENTIFIER, + STATUS_FIELD_UNIVERSAL_IDENTIFIER, +} from '../objects/post-card.object'; + +export default defineIndex({ + universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff0', + objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + fields: [ + { + universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff1', + fieldUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + }, + ], +}); +``` + +### Índices únicos + +`defineIndex` acepta `isUnique: true` tanto para unicidad de una sola columna como de varias columnas. Este es el elemento primitivo recomendado — `defineField({ isUnique: true })` está obsoleto y se eliminará en una versión futura. + +```ts +defineIndex({ + universalIdentifier: '…', + objectUniversalIdentifier: PERSON_UNIVERSAL_IDENTIFIER, + isUnique: true, + fields: [{ universalIdentifier: '…', fieldUniversalIdentifier: EMAIL_FIELD_UNIVERSAL_IDENTIFIER }], +}); +``` + +### Otras restricciones + +* Las cláusulas `WHERE` parciales permanecen bajo el control del administrador: las aplicaciones no pueden declararlas. +* Cada objeto está limitado a 10 índices personalizados (los índices propios del framework no cuentan). + +Ordena el arreglo `fields` de la forma en que Postgres debería usarlo: la columna más a la izquierda primero, como en una guía telefónica. Los índices no son gratuitos: cada escritura en la tabla los actualiza. Añade uno solo cuando tengas una consulta que lo necesite. + + +¿Buscas **Application Config** o **Roles & Permissions**? Esos describen la propia aplicación en lugar de los datos que agrega; se encuentran en [Config](/l/es/developers/extend/apps/config/overview). ¿Buscas **Connections** (Linear, GitHub, Slack OAuth)? Estas existen para ser llamadas *desde* las funciones lógicas y se encuentran en [Logic](/l/es/developers/extend/apps/logic/connections). + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/data/relations.mdx b/packages/twenty-docs/l/es/developers/extend/apps/data/relations.mdx new file mode 100644 index 0000000000..c9da4c2e9c --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/data/relations.mdx @@ -0,0 +1,160 @@ +--- +title: Relaciones +description: Conecta objetos entre sí con relaciones bidireccionales MANY_TO_ONE / ONE_TO_MANY. +icon: diagram-project +--- + +Las relaciones conectan dos objetos entre sí. En Twenty, las relaciones siempre son **bidireccionales**: cada relación tiene dos lados, y cada lado se declara como un campo que hace referencia al otro. + +| Tipo de relación | Descripción | ¿Tiene clave foránea? | +| ---------------- | --------------------------------------------------------------------------- | --------------------- | +| `MANY_TO_ONE` | Muchos registros de este objeto apuntan a un registro del objeto de destino | Sí (`joinColumnName`) | +| `ONE_TO_MANY` | Un registro de este objeto tiene muchos registros del objeto de destino | No (lado inverso) | + +## Cómo funcionan las relaciones + +Cada relación requiere **dos campos** que se referencian entre sí: + +1. El lado **MANY_TO_ONE** — vive en el objeto que contiene la clave foránea. +2. El lado **ONE_TO_MANY** — vive en el objeto que es propietario de la colección. + +Ambos campos usan `FieldType.RELATION` y se hacen referencia cruzada mediante `relationTargetFieldMetadataUniversalIdentifier`. + +## Ejemplo: la tarjeta postal tiene muchos destinatarios + +Un `PostCard` puede enviarse a muchos registros `PostCardRecipient`. Cada destinatario pertenece exactamente a una tarjeta postal. + +**Paso 1: Define el lado ONE_TO_MANY en PostCard** (el lado "uno"): + +```ts src/fields/post-card-recipients-on-post-card.field.ts +import { defineField, FieldType, RelationType } from 'twenty-sdk/define'; +import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; +import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; + +// Export so the other side can reference it +export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111'; +// Import from the other side +import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field'; + +export default defineField({ + universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'postCardRecipients', + label: 'Post Card Recipients', + icon: 'IconUsers', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); +``` + +**Paso 2: Define el lado MANY_TO_ONE en PostCardRecipient** (el lado "muchos" — contiene la clave foránea): + +```ts src/fields/post-card-on-post-card-recipient.field.ts +import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define'; +import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object'; +import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object'; + +// Export so the other side can reference it +export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222'; +// Import from the other side +import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field'; + +export default defineField({ + universalIdentifier: POST_CARD_FIELD_ID, + objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'postCard', + label: 'Post Card', + icon: 'IconMail', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.CASCADE, + joinColumnName: 'postCardId', + }, +}); +``` + + +**Importaciones circulares:** ambos campos de relación hacen referencia al `universalIdentifier` del otro. Para evitar problemas de importaciones circulares, exporta los ID de tus campos como constantes con nombre desde cada archivo e impórtalos en el otro. El sistema de compilación los resuelve en tiempo de compilación. + + +## Relacionar con objetos estándar + +Para crear una relación con un objeto integrado de Twenty (Person, Company, etc.), usa `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`: + +```ts src/fields/person-on-self-hosting-user.field.ts +import { + defineField, + FieldType, + RelationType, + OnDeleteAction, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; +import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object'; + +export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333'; +export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444'; + +export default defineField({ + universalIdentifier: PERSON_FIELD_ID, + objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'person', + label: 'Person', + description: 'Person matching with the self hosting user', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'personId', + }, +}); +``` + +## Propiedades del campo de relación + +| Propiedad | Obligatorio | Descripción | +| ------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------ | +| `type` | Sí | Debe ser `FieldType.RELATION` | +| `relationTargetObjectMetadataUniversalIdentifier` | Sí | El `universalIdentifier` del objeto de destino | +| `relationTargetFieldMetadataUniversalIdentifier` | Sí | El `universalIdentifier` del campo correspondiente en el objeto de destino | +| `universalSettings.relationType` | Sí | `RelationType.MANY_TO_ONE` o `RelationType.ONE_TO_MANY` | +| `universalSettings.onDelete` | Solo para MANY_TO_ONE | Qué sucede cuando se elimina el registro referenciado: `CASCADE`, `SET_NULL`, `RESTRICT` o `NO_ACTION` | +| `universalSettings.joinColumnName` | Solo para MANY_TO_ONE | Nombre de la columna de base de datos para la clave foránea (p. ej., `postCardId`) | + +## Campos de relación en línea + +También puedes declarar una relación directamente dentro de [`defineObject`](/l/es/developers/extend/apps/data/objects). Cuando es en línea, omite `objectUniversalIdentifier` — se hereda del objeto padre: + +```ts +export default defineObject({ + universalIdentifier: '...', + nameSingular: 'postCardRecipient', + // ... + fields: [ + { + universalIdentifier: POST_CARD_FIELD_ID, + type: FieldType.RELATION, + name: 'postCard', + label: 'Post Card', + relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.CASCADE, + joinColumnName: 'postCardId', + }, + }, + // … other fields + ], +}); +``` diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/concepts.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/concepts.mdx new file mode 100644 index 0000000000..862c825a20 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/concepts.mdx @@ -0,0 +1,101 @@ +--- +title: Conceptos +description: "Cómo funcionan las aplicaciones de Twenty: modelo de entidad, sandboxing y ciclo de vida de la instalación." +icon: sitemap +--- + +Las aplicaciones de Twenty son paquetes de TypeScript que amplían tu espacio de trabajo con objetos personalizados, lógica, componentes de UI y capacidades de IA. Se ejecutan en la plataforma Twenty con sandboxing completo y controles de permisos. + +## Cómo funcionan las aplicaciones + +Una aplicación es un conjunto de **entidades** declaradas usando funciones `defineEntity()` del paquete `twenty-sdk`. El SDK detecta estas declaraciones mediante análisis de AST en tiempo de compilación y produce un **manifiesto** — una descripción completa de lo que tu aplicación agrega a un espacio de trabajo. Estas funciones validan tu configuración en tiempo de compilación y proporcionan autocompletado en el IDE y seguridad de tipos. + +``` +your-app/ +├── src/ +│ ├── application-config.ts ← defineApplication (required, one per app) +│ ├── roles/ ← defineRole +│ ├── objects/ ← defineObject +│ ├── fields/ ← defineField +│ ├── logic-functions/ ← defineLogicFunction +│ ├── front-components/ ← defineFrontComponent +│ ├── skills/ ← defineSkill +│ ├── agents/ ← defineAgent +│ ├── views/ ← defineView +│ ├── navigation-menu-items/ ← defineNavigationMenuItem +│ └── page-layouts/ ← definePageLayout +├── public/ ← Static assets (images, icons) +└── package.json +``` + + + **La organización de archivos depende de ti.** La detección de entidades se basa en el AST — el SDK encuentra llamadas a `export default defineEntity(...)` sin importar dónde se encuentre el archivo. La estructura de carpetas anterior es una convención, no un requisito. + + +## Tipos de entidades + +| Entidad | Propósito | Documentación | +| ----------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| **Aplicación** | Identidad de la aplicación, rol predeterminado, variables | [Configuración de la aplicación](/l/es/developers/extend/apps/config/application) | +| **Rol** | Conjuntos de permisos para objetos y campos | [Roles y permisos](/l/es/developers/extend/apps/config/roles) | +| **Objeto** | Tipos de registros personalizados con campos | [Objetos](/l/es/developers/extend/apps/data/objects) | +| **Campo** | Agregar campos a objetos de otras aplicaciones | [Ampliar objetos](/l/es/developers/extend/apps/data/extending-objects) | +| **Relación** | Vínculos bidireccionales entre objetos | [Relaciones](/l/es/developers/extend/apps/data/relations) | +| **Función de lógica** | TypeScript del lado del servidor con activadores | [Funciones de lógica](/l/es/developers/extend/apps/logic/logic-functions) | +| **Habilidad** | Instrucciones reutilizables para agentes de IA | [Habilidades y agentes](/l/es/developers/extend/apps/logic/skills-and-agents) | +| **Agente** | Asistentes de IA con prompts personalizados | [Habilidades y agentes](/l/es/developers/extend/apps/logic/skills-and-agents) | +| **Proveedor de conexión** | Credenciales OAuth para APIs de terceros | [Conexiones](/l/es/developers/extend/apps/logic/connections) | +| **Vista** | Vistas de listas de registros preconfiguradas | [Vistas](/l/es/developers/extend/apps/layout/views) | +| **Elemento del menú de navegación** | Entradas personalizadas de la barra lateral | [Elementos del menú de navegación](/l/es/developers/extend/apps/layout/navigation-menu-items) | +| **Diseño de página** | Pestañas y widgets en la página de detalles de un registro | [Diseños de página](/l/es/developers/extend/apps/layout/page-layouts) | +| **Componente de frontend** | Interfaz de usuario de React en entorno aislado dentro de Twenty | [Componentes de frontend](/l/es/developers/extend/apps/layout/front-components) | +| **Elemento del menú de comandos** | Acciones rápidas y entradas Cmd+K | [Elementos del menú de comandos](/l/es/developers/extend/apps/layout/command-menu-items) | + +## Sandboxing + +* **Las funciones de lógica** se ejecutan en procesos de Node.js aislados en el servidor. Solo acceden a los datos a través del cliente de API tipado, limitado a los permisos del rol de la aplicación. +* **Los componentes de frontend** se ejecutan en Web Workers usando Remote DOM — aislados de la página principal pero renderizando elementos DOM nativos (no iframes). Se comunican con Twenty a través de una API de host de paso de mensajes. +* **Los permisos** se aplican a nivel de API. El token de tiempo de ejecución (`TWENTY_APP_ACCESS_TOKEN`) se deriva del rol definido en `defineApplication()`. + +## Ciclo de vida de la aplicación + +``` +┌─────────────────────────────────────────────────────────┐ +│ Development │ +│ npx create-twenty-app → yarn twenty dev (live sync) │ +├─────────────────────────────────────────────────────────┤ +│ Build & Deploy │ +│ yarn twenty dev:build → yarn twenty app:publish │ +├─────────────────────────────────────────────────────────┤ +│ Install flow │ +│ upload → [pre-install] → metadata migration → │ +│ generate SDK → [post-install] │ +├─────────────────────────────────────────────────────────┤ +│ Publish │ +│ npm publish → appears in Twenty marketplace │ +└─────────────────────────────────────────────────────────┘ +``` + +* **`yarn twenty dev`** — observa tus archivos fuente y sincroniza en tiempo real los cambios con un servidor de Twenty conectado. El cliente de API tipado se regenera automáticamente cuando cambia el esquema. +* **`yarn twenty dev:build`** — compila TypeScript, agrupa las funciones de lógica y los componentes de frontend con esbuild, y produce un manifiesto. +* **Hooks de pre/post-instalación** — funciones opcionales que se ejecutan durante la instalación. Consulta [Hooks de instalación](/l/es/developers/extend/apps/config/install-hooks) para más detalles. + +## Próximos pasos + + + + Identidad de la aplicación, rol predeterminado y hooks de instalación. + + + Objetos, campos y relaciones bidireccionales. + + + Funciones de lógica, habilidades, agentes y conexiones OAuth. + + + Vistas, navegación, diseños de página y componentes de frontend. + + + CLI, pruebas, remotos, CI y publicación de tu aplicación. + + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/local-server.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/local-server.mdx new file mode 100644 index 0000000000..08eded2f25 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/local-server.mdx @@ -0,0 +1,87 @@ +--- +title: Servidor local +description: "Administra el servidor Docker local de Twenty: inícialo, deténlo, actualízalo, crea una instancia de prueba en paralelo y realiza la configuración manual del SDK." +icon: server +--- + +## Administrar el servidor local + +Usa `yarn twenty docker:*` para controlar el contenedor local de Twenty: + +| Comando | Qué hace | +| -------------------------------------- | ----------------------------------------------------------------- | +| `yarn twenty docker:start` | Inicia el servidor local (descarga la imagen si es necesario) | +| `yarn twenty docker:start 2.2.0` | Iniciar una versión específica del servidor | +| `yarn twenty docker:start --port 3030` | Inicia en un puerto personalizado | +| `yarn twenty docker:stop` | Detiene el servidor (conserva los datos) | +| `yarn twenty docker:status` | Muestra la URL, la versión y las credenciales de inicio de sesión | +| `yarn twenty docker:logs` | Transmite los registros del servidor | +| `yarn twenty docker:reset` | Elimina los datos y comienza desde cero | +| `yarn twenty docker:upgrade` | Descarga la última imagen `twenty-app-dev` | +| `yarn twenty docker:upgrade 2.2.0` | Actualiza a una versión específica | + +Los datos se conservan entre reinicios en dos volúmenes de Docker (`twenty-app-dev-data` para PostgreSQL, `twenty-app-dev-storage` para archivos). Usa `reset` para borrar todo. + +## Fijar la versión del servidor + +Cuando no se pasa ninguna versión, `docker:start` resuelve la versión a partir del rango `engines.twenty` de tu aplicación en `package.json`, el mismo rango con el que el servidor valida cuando tu aplicación se instala. Inicia la imagen publicada más reciente de `twenty-app-dev` que satisface el rango, recurriendo a `latest` cuando el campo está ausente o ninguna versión publicada coincide: + +```json filename="package.json" +{ + "engines": { + "twenty": ">=2.2.0" + } +} +``` + +Pasa una versión explícitamente para anular el rango en una sola ejecución: `yarn twenty docker:start 2.3.0`. Si ya existe un contenedor en una versión diferente, `docker:start` lo actualiza in situ (recreando el contenedor mientras conserva tus volúmenes de datos). + +## Actualización de la imagen del servidor + +`yarn twenty docker:upgrade` descarga la última imagen, compara los digests y solo recrea el contenedor si realmente cambió algo. Los volúmenes se conservan — solo se reemplaza el contenedor. Si se descargó una nueva imagen y el contenedor estaba en ejecución, la actualización inicia automáticamente un contenedor nuevo; ejecuta `yarn twenty docker:start` después para esperar a que esté en buen estado. + +```bash filename="Terminal" +yarn twenty docker:upgrade # Latest +yarn twenty docker:upgrade 2.2.0 # Specific version +``` + +Puedes verificar la versión en ejecución con `yarn twenty docker:status` (muestra el `APP_VERSION` incluido en el contenedor). + +## Ejecutar una instancia de prueba en paralelo + +Pasa `--test` a cualquier comando de `docker:*` para administrar una segunda instancia completamente aislada — útil para ejecutar pruebas de integración o experimentar sin tocar los datos principales de desarrollo: + +| Comando | Qué hace | +| ----------------------------------- | -------------------------------------------------------------- | +| `yarn twenty docker:start --test` | Inicia la instancia de prueba (usa el puerto 2021 por defecto) | +| `yarn twenty docker:stop --test` | Deténla | +| `yarn twenty docker:status --test` | Muestra su estado | +| `yarn twenty docker:logs --test` | Transmite sus registros | +| `yarn twenty docker:reset --test` | Borra sus datos | +| `yarn twenty docker:upgrade --test` | Actualiza su imagen | + +La instancia de prueba se ejecuta en su propio contenedor de Docker (`twenty-app-dev-test`) con volúmenes dedicados (`twenty-app-dev-test-data`, `twenty-app-dev-test-storage`) y configuración dedicada, por lo que puede ejecutarse en paralelo con la instancia principal sin conflictos. Combina `--test` con `--port` para anular el puerto predeterminado (2021). + +## Configuración manual (sin el generador) + +Omite el generador si vas a agregar el SDK a un proyecto existente: + +```bash filename="Terminal" +yarn add twenty-sdk twenty-client-sdk +``` + +Agrega el script a `package.json`: + +```json filename="package.json" +{ + "scripts": { + "twenty": "twenty" + } +} +``` + +Ahora puedes ejecutar `yarn twenty dev`, `yarn twenty docker:start` y todos los demás comandos. + + +No instales `twenty-sdk` globalmente — fíjalo por proyecto para que cada app use su propia versión. + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx new file mode 100644 index 0000000000..11ed884f9b --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/project-structure.mdx @@ -0,0 +1,61 @@ +--- +title: Estructura del proyecto +description: Qué hay dentro de una app de Twenty generada mediante scaffolding — archivos, carpetas y lo que hace cada uno. +icon: folder-tree +--- + +Una nueva app generada por `npx create-twenty-app` se ve así: + +```text filename="my-twenty-app/" +my-twenty-app/ + package.json + src/ + application-config.ts # Required — your app's entry point + default-role.ts # Permissions for logic functions + constants/ + universal-identifiers.ts # Auto-generated UUIDs and metadata + __tests__/ + setup-test.ts + app-install.integration-test.ts + .github/workflows/ci.yml # GitHub Actions + public/ # Static assets + vitest.config.ts # Test runner config + tsconfig.json, tsconfig.spec.json + .nvmrc, .yarnrc.yml, .oxlintrc.json + README.md, LLMS.md +``` + +## Archivos clave + +| Archivo / Carpeta | Propósito | +| ---------------------------------------- | ----------------------------------------------------------------------------------------- | +| `src/application-config.ts` | **Obligatorio.** El archivo de configuración principal de tu app. | +| `src/default-role.ts` | Rol predeterminado que controla a qué pueden acceder tus funciones lógicas. | +| `src/constants/universal-identifiers.ts` | UUIDs generados automáticamente y metadatos de la app (nombre para mostrar, descripción). | +| `src/__tests__/` | Pruebas de integración (configuración + prueba de ejemplo). | +| `public/` | Recursos estáticos (imágenes, fuentes) servidos con tu app. | + + +**La organización de archivos depende de ti.** Las carpetas anteriores son convenciones: el SDK detecta entidades mediante análisis AST en llamadas a `export default defineEntity(...)`, sin importar dónde se encuentre el archivo. + + +## Dependencias + +Ambos paquetes del SDK de Twenty pertenecen a `devDependencies`, no a `dependencies`: + +```json filename="package.json" +{ + "dependencies": {}, + "devDependencies": { + "twenty-client-sdk": "^2.13.0", + "twenty-sdk": "^2.13.0" + } +} +``` + +* **`twenty-sdk`** incluye el CLI `twenty` y las herramientas de build/scaffolding. Solo se ejecuta en el desarrollo y durante el build, y nunca lo importa el runtime de la app que publicas. +* **`twenty-client-sdk`** *sí* es importado por el código de tu app (`CoreApiClient`, `MetadataApiClient`, `RestApiClient`), pero Twenty lo proporciona en tiempo de ejecución: las funciones lógicas lo obtienen de una capa SDK generada y los componentes de front lo resuelven desde módulos servidos por el servidor. Tu copia instalada solo se utiliza para la comprobación de tipos y el build en tiempo de despliegue, por lo que nunca necesita incluirse en el bundle desplegado. + +Mantener cualquiera de los paquetes bajo `dependencies` lo introduce en el bundle de runtime de la app instalada, donde es peso muerto. `twenty build` emite una advertencia cuando cualquiera de ellos sigue listado bajo `dependencies`. + +Añade las dependencias de runtime propias de tu app (las bibliotecas que tus funciones lógicas realmente importan en tiempo de ejecución) bajo `dependencies` como de costumbre. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx new file mode 100644 index 0000000000..5c11a87152 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/quick-start.mdx @@ -0,0 +1,176 @@ +--- +title: Inicio rápido +icon: rocket +description: Crea tu primera aplicación de Twenty en minutos. +--- + +## Prerrequisitos + +* **Node.js 24+** — [Descargar](https://nodejs.org/) +* **Yarn 4** — incluido con Node.js a través de Corepack. Actívalo: `corepack enable` +* **Docker** — [Descargar](https://www.docker.com/products/docker-desktop/). Necesario para ejecutar un servidor local de Twenty. Omítelo si ya tienes Twenty ejecutándose en otro lugar. + +La creación de una app de Twenty tiene tres fases. El generador las combina en un único comando de ruta ideal, pero cada fase es un concepto independiente — cuando algo falla, saber en qué fase estás te indica qué debes corregir. + +| Fase | Qué haces | Herramienta | Resultado | +| --------------------------- | --------------------------------------------------- | ----------------------------- | ------------------------------------ | +| **1. Generar estructura** | Genera el código fuente de la app | `npx create-twenty-app` | Un proyecto de TypeScript en disco | +| **2. Ejecutar un servidor** | Inicia un servidor de Twenty con el que sincronizar | Docker + `yarn twenty server` | Una instancia de Twenty en ejecución | +| **3. Sincronizar** | Sincroniza en vivo tu código con el servidor | `yarn twenty dev` | Tus cambios aparecen en la UI | + +--- + +## Fase 1 — Genera la estructura de tu proyecto + +Crea una app nueva a partir de la plantilla: + +```bash filename="Terminal" +npx create-twenty-app@latest my-twenty-app +``` + +Se te pedirá un nombre y una descripción — pulsa **Enter** para usar los valores predeterminados. Esto genera un proyecto de TypeScript en `my-twenty-app/` con un `application-config.ts` inicial, un rol predeterminado, un flujo de trabajo de CI y una prueba de integración. + +**Después de esta fase:** tienes el código fuente de tu app en tu máquina. Aún no se está ejecutando — esa es la Fase 2. + +--- + +## Fase 2 — Ejecuta un servidor local de Twenty + +Tu app necesita un servidor de Twenty con el que sincronizar. El servidor es una instancia completa de Twenty — UI, API GraphQL, PostgreSQL — ejecutándose localmente en Docker. Tu código local sube sus definiciones a ese servidor, lo que hace que aparezcan en la UI. + +El generador ofrece iniciar uno por ti: + +> **¿Te gustaría configurar una instancia local de Twenty?** + +* **Sí (recomendado)** — descarga la imagen de Docker `twentycrm/twenty-app-dev` y la inicia en el puerto `2020`. Asegúrate de que Docker esté en ejecución antes. +* **No** — elige esto si ya tienes un servidor de Twenty al que te quieres conectar. Puedes conectarlo más tarde con `yarn twenty remote:add`. + +
+ ¿Debería iniciar una instancia local? +
+ +Una vez que el servidor esté en marcha, se abrirá un navegador para iniciar sesión. Inicia sesión con la cuenta de demostración precargada: + +* **Correo electrónico:** `tim@apple.dev` +* **Contraseña:** `tim@apple.dev` + +
+ Pantalla de inicio de sesión de Twenty +
+ +Haz clic en **Authorize** en la siguiente pantalla — esto le da a la CLI acceso a tu espacio de trabajo. + +
+ Pantalla de autorización de la CLI de Twenty +
+ +Tu terminal confirmará que todo está configurado. + +
+ Aplicación generada correctamente +
+ +**Después de esta fase:** tienes un servidor de Twenty en ejecución en [http://localhost:2020](http://localhost:2020) con tu CLI autorizada para sincronizar con él. + + +Si Docker no está instalado o en ejecución, el generador te indicará el comando de inicio correcto para tu sistema operativo. Una vez que Docker esté en marcha, puedes reanudar con `yarn twenty docker:start` — no es necesario volver a generar la estructura. + + +--- + +## Fase 3 — Sincroniza tus cambios + +Este es el ciclo interno en el que pasarás la mayor parte del tiempo. + +```bash filename="Terminal" +cd my-twenty-app +yarn twenty dev +``` + +Esto observa `src/`, recompila en cada cambio y sincroniza el resultado con el servidor. Edita un archivo, guarda y, en cuestión de unos segundos, el servidor reflejará el cambio. Verás un panel de estado en vivo en tu terminal. + +Para una salida más detallada (registros de compilación, solicitudes de sincronización, trazas de errores), añade `--verbose`. + +
+ Salida del modo de desarrollo en la terminal +
+ +Abre [http://localhost:2020/settings/applications#developer](http://localhost:2020/settings/applications#developer) en tu navegador. Deberías ver tu app listada en **Your Apps**. + +
+ Lista de Your Apps que muestra My twenty app +
+ +Haz clic en **My twenty app** para ver su **registro de la aplicación** — un registro a nivel de servidor que describe tu app (nombre, identificador, credenciales de OAuth, origen). Un único registro puede instalarse en varios espacios de trabajo del mismo servidor. + +
+ Detalles del registro de la aplicación +
+ +Haz clic en **View installed app** para ver la instalación en el espacio de trabajo. La pestaña **About** muestra la versión actual y las opciones de gestión. + +
+ Aplicación instalada +
+ +**Después de esta fase:** tienes un ciclo de desarrollo en vivo. Edita cualquier archivo en `src/` y aparecerá en la UI. + +### Sincronización de una sola vez para CI y scripts + +Pasa `--once` para ejecutar una sola compilación + sincronización y salir — mismo pipeline, sin watcher: + +```bash filename="Terminal" +yarn twenty dev --once +``` + +| Comando | Comportamiento | Cuándo usarlo | +| ---------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `yarn twenty dev` | Supervisa tus archivos fuente y vuelve a sincronizar en cada cambio. Se ejecuta hasta que lo detengas. | Desarrollo local interactivo. | +| `yarn twenty dev --once` | Realiza una sola compilación + sincronización y luego sale con el código `0` si tiene éxito o `1` si falla. | CI, hooks de pre-commit, agentes de IA, flujos de trabajo con scripts. | +| `yarn twenty dev --once --dry-run` | Genera y muestra los cambios de metadatos **sin aplicarlos**. | Inspeccionar qué cambiaría una sincronización antes de confirmarla. | + +Ambos modos necesitan un remoto autenticado. Consulta [Sincronización y recuperación](/l/es/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) para obtener más información sobre `--dry-run`. + +### Opciones del modo de desarrollo + +| Opción | Descripción | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `--once` | Compila y sincroniza una vez y luego finaliza. | +| `--dry-run` | Con `--once`, obtén una vista previa de los cambios de metadatos sin aplicarlos. No escribe nada. | +| `--debounceMs \` | Establece el tiempo de antirrebote para los cambios de archivo en milisegundos (valor predeterminado: `2000`). | +| `--verbose` / `--debug` | Muestra registros de compilación detallados, solicitudes de sincronización y seguimientos de errores. | + +## Lo que puedes crear + +Las apps se componen de **entidades** — cada una definida como un archivo de TypeScript con un único `export default`: + +| Entidad | Qué hace | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| **Objetos y campos** | Modelos de datos personalizados (Post Card, Invoice, etc.). con campos tipados | +| **Funciones de lógica** | Funciones de TypeScript del lado del servidor activadas por rutas HTTP, programaciones de cron o eventos de base de datos | +| **Componentes de frontend** | Componentes de React que se renderizan dentro de la interfaz de Twenty (panel lateral, widgets, menú de comandos) | +| **Habilidades y agentes** | Capacidades de IA — instrucciones reutilizables y asistentes autónomos | +| **Vistas y navegación** | Vistas de lista preconfiguradas y elementos del menú lateral | +| **Diseños de página** | Páginas de detalle de registros personalizadas con pestañas y widgets | + +Referencia completa: [Conceptos](/l/es/developers/extend/apps/getting-started/concepts). + +## Próximos pasos + + + + Identidad de la aplicación, rol predeterminado, hooks de instalación y recursos públicos. + + + Objetos, campos y relaciones bidireccionales. + + + Funciones de lógica, habilidades, agentes y conexiones OAuth. + + + Vistas, navegación, diseños de página y componentes de frontend. + + + CLI, pruebas, remotos, CI y publicación de tu aplicación. + + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx new file mode 100644 index 0000000000..38a5fd57bb --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/scaffolding.mdx @@ -0,0 +1,58 @@ +--- +title: Andamiaje +description: Genera archivos de entidad de forma interactiva con yarn twenty dev:add — objetos, campos, vistas, funciones de lógica y más. +icon: wand-magic-sparkles +--- + +En lugar de crear archivos de entidad a mano, usa el generador interactivo: + +```bash filename="Terminal" +yarn twenty dev:add +``` + +Te pide que elijas un tipo de entidad y te guía por los campos requeridos; luego escribe un archivo listo para usar con un `universalIdentifier` estable y la llamada correcta a `defineEntity()`. + +También puedes pasar el tipo de entidad directamente para omitir la primera pregunta: + +```bash filename="Terminal" +yarn twenty dev:add object +yarn twenty dev:add logicFunction +yarn twenty dev:add frontComponent +``` + +## Tipos de entidad disponibles + +| Tipo de entidad | Comando | Archivo generado | +| ------------------------------- | ---------------------------------------- | ------------------------------------------------------- | +| Objeto | `yarn twenty dev:add object` | `src/objects/\.ts` | +| Campo | `yarn twenty dev:add field` | `src/fields/\.ts` | +| Función de lógica | `yarn twenty dev:add logicFunction` | `src/logic-functions/\.ts` | +| Componente de frontend | `yarn twenty dev:add frontComponent` | `src/front-components/\.tsx` | +| Rol | `yarn twenty dev:add role` | `src/roles/\.ts` | +| Habilidad | `yarn twenty dev:add skill` | `src/skills/\.ts` | +| Agente | `yarn twenty dev:add agent` | `src/agents/\.ts` | +| Vista | `yarn twenty dev:add view` | `src/views/\.ts` | +| Elemento del menú de navegación | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/\.ts` | +| Diseño de página | `yarn twenty dev:add pageLayout` | `src/page-layouts/\.ts` | + +## Qué genera el generador + +Cada tipo de entidad tiene su propia plantilla. Por ejemplo, `yarn twenty dev:add object` solicita: + +1. **Nombre (singular)** — p. ej., `invoice` +2. **Nombre (plural)** — p. ej., `invoices` +3. **Etiqueta (singular)** — se completa automáticamente a partir del nombre (p. ej., `Invoice`) +4. **Etiqueta (plural)** — se completa automáticamente (p. ej., `Invoices`) +5. **¿Crear una vista y un elemento de navegación?** — si respondes que sí, el generador también crea una vista correspondiente y un enlace en la barra lateral para el nuevo objeto. + +Otros tipos de entidad tienen indicaciones más simples: la mayoría solo piden un nombre. + +El tipo de entidad `field` es más detallado: pide el nombre del campo, etiqueta, tipo (de una lista de todos los tipos de campo disponibles como `TEXT`, `NUMBER`, `SELECT`, `RELATION`, etc.) y el `universalIdentifier` del objeto de destino. + +## Ruta de salida personalizada + +Usa la opción `--path` para colocar el archivo generado en una ubicación personalizada: + +```bash filename="Terminal" +yarn twenty dev:add logicFunction --path src/custom-folder +``` diff --git a/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx new file mode 100644 index 0000000000..0ccbcfd985 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/getting-started/troubleshooting.mdx @@ -0,0 +1,14 @@ +--- +title: Solución de problemas +description: "Problemas comunes en la primera ejecución: Docker, versión de Node, Yarn, dependencias." +icon: llave inglesa +--- + +* **Errores de Docker** — Asegúrate de que Docker Desktop (o el daemon) esté en ejecución antes de `yarn twenty docker:start`. El mensaje de error mostrará el comando de inicio correcto para tu sistema operativo. +* **Versión de Node incorrecta** — Se requiere 24+. Compruébalo con `node -v`. +* **Falta Yarn 4** — Ejecuta `corepack enable`. +* **Dependencias rotas** — `rm -rf node_modules && yarn install`. +* **Errores de `twenty-sdk` tras actualizar a la v2.8.0** — Pasó de `dependencies` a `devDependencies` en la v2.8.0. Consulta [Estructura del proyecto → Dependencias](/l/es/developers/extend/apps/getting-started/project-structure#dependencies). +* **`twenty build` muestra una advertencia sobre `twenty-client-sdk` en `dependencies`** — Twenty lo proporciona en tiempo de ejecución, por lo que debería trasladarse a `devDependencies` junto con `twenty-sdk`. Consulta [Estructura del proyecto → Dependencias](/l/es/developers/extend/apps/getting-started/project-structure#dependencies). + +¿Atascado? Pide ayuda en el [Discord de Twenty](https://discord.com/channels/1130383047699738754/1130386664812982322). diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx new file mode 100644 index 0000000000..c02fd35d6b --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/command-menu-items.mdx @@ -0,0 +1,148 @@ +--- +title: Elementos del menú de comandos +description: Expón componentes de frontend como acciones rápidas y entradas del menú de comandos (Cmd+K) con defineCommandMenuItem. +icon: terminal +--- + +Un **elemento del menú de comandos** es el puente entre el usuario y un [componente de frontend](/l/es/developers/extend/apps/layout/front-components). Registra el componente en el menú de comandos de Twenty (Cmd+K) y, opcionalmente, como un botón de acción rápida anclado en la esquina superior derecha de la página. + +```ts src/command-menu-items/open-dashboard.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + label: 'Open Dashboard', + shortLabel: 'Dashboard', + icon: 'IconLayoutDashboard', + isPinned: true, + availabilityType: 'GLOBAL', + frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', +}); +``` + +## Campos de configuración + +| Campo | Obligatorio | Descripción | +| --------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `universalIdentifier` | Sí | ID único estable para el comando | +| `label` | Sí | Etiqueta completa mostrada en el menú de comandos (Cmd+K) | +| `frontComponentUniversalIdentifier` | Sí | El `universalIdentifier` del componente de frontend que abre este comando | +| `shortLabel` | No | Etiqueta corta mostrada en el botón de acción rápida anclado | +| `icon` | No | Nombre del ícono mostrado junto a la etiqueta (p. ej., 'IconBolt', 'IconSend') | +| `isPinned` | No | Cuando es `true`, muestra el comando como un botón de acción rápida en la esquina superior derecha de la página | +| `availabilityType` | No | Controla dónde aparece el comando: 'GLOBAL' (siempre disponible), 'RECORD_SELECTION' (solo cuando hay registros seleccionados) o 'FALLBACK' (se muestra cuando ningún otro comando coincide) | +| `availabilityObjectUniversalIdentifier` | No | Restringe el comando a páginas de un tipo de objeto específico (p. ej., solo en registros de Company) | +| `conditionalAvailabilityExpression` | No | Una expresión booleana que controla dinámicamente la visibilidad (ver abajo) | + +## Comandos sin interfaz + +Un elemento del menú de comandos emparejado con un [headless front component](/l/es/developers/extend/apps/layout/front-components#headless-vs-non-headless) es la forma idónea de ofrecer una acción de un solo clic: ejecutar código, navegar o confirmar y ejecutar. La página Front Components abarca los [SDK Command components](/l/es/developers/extend/apps/layout/front-components#sdk-command-components) (`Command`, `CommandLink`, `CommandModal`, `CommandOpenSidePanelPage`) que gestionan el patrón de acción y desmontaje. + +Un flujo típico: + +```tsx src/front-components/run-action.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const RunAction = () => { + const execute = async () => { + const client = new CoreApiClient(); + await client.mutation({ + createTask: { + __args: { data: { title: 'Created by my app' } }, + id: true, + }, + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', + name: 'run-action', + description: 'Creates a task from the command menu', + component: RunAction, + isHeadless: true, +}); +``` + +```ts src/command-menu-items/run-action.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', + label: 'Run my action', + icon: 'IconPlayerPlay', + frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', +}); +``` + +## Expresiones de disponibilidad condicional + +El campo `conditionalAvailabilityExpression` te permite controlar cuándo es visible un comando en función del contexto de la página actual. Importa variables tipadas y operadores desde `twenty-sdk` para construir expresiones: + +```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, + ), +}); +``` + + + `RECORD_SELECTION` ya implica una selección no vacía; usa `numberOfSelectedRecords` solo para recuentos específicos (por ejemplo, `>= 2`). + + +### Variables de contexto + +Estas representan el estado actual de la página: + +| Variable | Tipo | Descripción | +| ------------------------------ | --------- | ------------------------------------------------------------------- | +| `pageType` | `string` | Tipo de página actual (p. ej., 'RecordIndexPage', 'RecordShowPage') | +| `isInSidePanel` | `boolean` | Si el componente se renderiza en un panel lateral | +| `numberOfSelectedRecords` | `number` | Número de registros seleccionados actualmente | +| `isSelectAll` | `boolean` | Si "seleccionar todo" está activo | +| `selectedRecords` | `array` | Los objetos de registro seleccionados | +| `favoriteRecordIds` | `array` | IDs de registros marcados como favoritos | +| `objectPermissions` | `object` | Permisos para el tipo de objeto actual | +| `targetObjectReadPermissions` | `object` | Permisos de lectura para el objeto de destino | +| `targetObjectWritePermissions` | `object` | Permisos de escritura para el objeto de destino | +| `featureFlags` | `object` | Indicadores de características activos | +| `objectMetadataItem` | `object` | Metadatos del tipo de objeto actual | +| `hasAnySoftDeleteFilterOnView` | `boolean` | Si la vista actual tiene un filtro de eliminación lógica | + +### Operadores + +Combina variables en expresiones booleanas: + +| Operador | Descripción | +| ----------------------------------- | --------------------------------------------------------------------- | +| `isDefined(value)` | `true` si el valor no es null/undefined | +| `isNonEmptyString(value)` | `true` si el valor es una cadena no vacía | +| `includes(array, value)` | `true` si el arreglo contiene el valor | +| `includesEvery(array, prop, value)` | `true` si la propiedad de cada elemento incluye el valor | +| `every(array, prop)` | `true` si la propiedad es truthy en cada elemento | +| `everyDefined(array, prop)` | `true` si la propiedad está definida en cada elemento | +| `everyEquals(array, prop, value)` | `true` si la propiedad es igual al valor en cada elemento | +| `some(array, prop)` | `true` si la propiedad es truthy en al menos un elemento | +| `someDefined(array, prop)` | `true` si la propiedad está definida en al menos un elemento | +| `someEquals(array, prop, value)` | `true` si la propiedad es igual al valor en al menos un elemento | +| `someNonEmptyString(array, prop)` | `true` si la propiedad es una cadena no vacía en al menos un elemento | +| `none(array, prop)` | `true` si la propiedad es falsy en cada elemento | +| `noneDefined(array, prop)` | `true` si la propiedad es undefined en cada elemento | +| `noneEquals(array, prop, value)` | `true` si la propiedad no es igual al valor en ningún elemento | diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx new file mode 100644 index 0000000000..c2bebfc864 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/front-components.mdx @@ -0,0 +1,545 @@ +--- +title: Componentes de frontend +description: Crea componentes de React que se renderizan dentro de la UI de Twenty con aislamiento en entorno sandbox. +icon: window-maximize +--- + +Los componentes de frontend son componentes de React que se renderizan directamente dentro de la UI de Twenty. Se ejecutan en un **Web Worker aislado** usando Remote DOM: tu código está aislado (sandboxed) pero se renderiza de forma nativa en la página, no en un iframe. + +## Dónde se pueden usar los componentes de front + +Los componentes de front pueden renderizarse en dos ubicaciones dentro de Twenty: + +* **Panel lateral** — Los componentes de front no headless se abren en el panel lateral derecho. Este es el comportamiento predeterminado cuando un componente de front se activa desde el menú de comandos. +* **Widgets (tableros y páginas de registros)** — Los componentes de front pueden incrustarse como widgets dentro de los [diseños de página](/l/es/developers/extend/apps/layout/page-layouts). Al configurar un tablero o el diseño de una página de registro, los usuarios pueden agregar un widget de componente de front. + +Un componente de front por sí solo no es accesible desde la interfaz de usuario; necesitas *exponerlo*. Las dos formas de hacerlo son: + +* **Emparejarlo con un [elemento del menú de comandos](/l/es/developers/extend/apps/layout/command-menu-items)**: lo registra en el menú de comandos (Cmd+K) y, de forma opcional, como una acción rápida fijada. +* **Incrustarlo como widget en un [diseño de página](/l/es/developers/extend/apps/layout/page-layouts)**: lo coloca en la página de detalles de un registro o en un tablero. + +## Ejemplo básico + +La forma más rápida de ver un componente de front en acción es emparejarlo con un [`defineCommandMenuItem`](/l/es/developers/extend/apps/layout/command-menu-items), de modo que aparezca como un botón de acción rápida en la esquina superior derecha de la página: + +```tsx src/front-components/hello-world.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; + +const HelloWorld = () => { + return ( +
+

Hello from my app!

+

This component renders inside Twenty.

+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', + name: 'hello-world', + description: 'A simple front component', + component: HelloWorld, +}); +``` + +```ts src/command-menu-items/hello-world.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-456789012345', + shortLabel: 'Hello', + label: 'Hello World', + icon: 'IconBolt', + isPinned: true, + availabilityType: 'GLOBAL', + frontComponentUniversalIdentifier: '74c526eb-cb68-4cf7-b05c-0dd8c288d948', +}); +``` + +Después de sincronizar con `yarn twenty dev` (o ejecutar una sola vez `yarn twenty dev --once`), la acción rápida aparece en la esquina superior derecha de la página: + +
+ Botón de acción rápida en la esquina superior derecha +
+ +Haz clic para renderizar el componente en línea. + +## Campos de configuración + +| Campo | Obligatorio | Descripción | +| --------------------- | ----------- | -------------------------------------------------------------------- | +| `universalIdentifier` | Sí | ID único estable para este componente | +| `component` | Sí | Una función de componente de React | +| `name` | No | Nombre para mostrar | +| `description` | No | Descripción de lo que hace el componente | +| `isHeadless` | No | Configura en `true` si el componente no tiene UI visible (ver abajo) | + +## Colocar un componente de frontend en una página + +Más allá de los comandos, puedes incrustar un componente de frontend directamente en una página de registro agregándolo como un widget en un **diseño de página**. Consulta [Diseños de página](/l/es/developers/extend/apps/layout/page-layouts) para más detalles. + +## Headless vs no headless + +Los componentes de front vienen en dos modos de renderizado controlados por la opción `isHeadless`: + +**No headless (predeterminado)** — El componente renderiza una UI visible. Cuando se activa desde el menú de comandos, se abre en el panel lateral. Este es el comportamiento predeterminado cuando `isHeadless` es `false` o se omite. + +**Headless (`isHeadless: true`)** — El componente se monta de forma invisible en segundo plano. No abre el panel lateral. Los componentes headless están diseñados para acciones que ejecutan lógica y luego se desmontan — por ejemplo, ejecutar una tarea asíncrona, navegar a una página o mostrar un modal de confirmación. Se combinan de forma natural con los componentes Command del SDK descritos a continuación. + +```tsx src/front-components/sync-tracker.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { useRecordId, enqueueSnackbar } from 'twenty-sdk/front-component'; +import { useEffect } from 'react'; + +const SyncTracker = () => { + const recordId = useRecordId(); + + useEffect(() => { + enqueueSnackbar({ message: `Tracking record ${recordId}`, variant: 'info' }); + }, [recordId]); + + return null; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'sync-tracker', + description: 'Tracks record views silently', + isHeadless: true, + component: SyncTracker, +}); +``` + +Como el componente devuelve `null`, Twenty omite renderizar un contenedor para él — no aparece espacio vacío en el diseño. El componente sigue teniendo acceso a todos los hooks y a la API de comunicación con el host. + +## Componentes Command del SDK + +El paquete `twenty-sdk` proporciona cuatro componentes auxiliares Command diseñados para componentes de front headless. Cada componente ejecuta una acción al montarse, gestiona los errores mostrando una notificación tipo snackbar y desmonta automáticamente el componente de front al finalizar. + +Impórtalos desde `twenty-sdk/command`: + +* **`Command`** — Ejecuta un callback asíncrono mediante la prop `execute`. +* **`CommandLink`** — Navega a una ruta de la aplicación. Props: `to`, `params`, `queryParams`, `options`. +* **`CommandModal`** — Abre un modal de confirmación. Si el usuario confirma, ejecuta el callback `execute`. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`. +* **`CommandOpenSidePanelPage`** — Abre una página específica del panel lateral. Props: `page`, `pageTitle`, `pageIcon`. + +Aquí tienes un ejemplo completo de un componente de front headless que usa `Command` para ejecutar una acción desde el menú de comandos: + +```tsx src/front-components/run-action.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const RunAction = () => { + const execute = async () => { + const client = new CoreApiClient(); + + await client.mutation({ + createTask: { + __args: { data: { title: 'Created by my app' } }, + id: true, + }, + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', + name: 'run-action', + description: 'Creates a task from the command menu', + component: RunAction, + isHeadless: true, +}); +``` + +```ts src/command-menu-items/run-action.command-menu-item.ts +import { defineCommandMenuItem } from 'twenty-sdk/define'; + +export default defineCommandMenuItem({ + universalIdentifier: 'f6a7b8c9-d0e1-2345-fabc-456789012345', + label: 'Run my action', + icon: 'IconPlayerPlay', + frontComponentUniversalIdentifier: 'e5f6a7b8-c9d0-1234-efab-345678901234', +}); +``` + +Y un ejemplo que usa `CommandModal` para pedir confirmación antes de ejecutar: + +```tsx src/front-components/delete-draft.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { CommandModal } from 'twenty-sdk/command'; + +const DeleteDraft = () => { + const execute = async () => { + // perform the deletion + }; + + return ( + + ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'a7b8c9d0-e1f2-3456-abcd-567890123456', + name: 'delete-draft', + description: 'Deletes a draft with confirmation', + component: DeleteDraft, + isHeadless: true, +}); +``` + +## Llamar a una función de lógica + +Los componentes de front se ejecutan en el navegador dentro de un Web Worker aislado (sandboxed), mientras que las [funciones de lógica](/l/es/developers/extend/apps/logic/logic-functions) se ejecutan en el servidor. No hay una llamada directa en el mismo proceso entre ambos; en su lugar, un componente de front accede a una función de lógica a través de HTTP. + +Una función de lógica declarada con `httpRouteTriggerSettings` se expone bajo el endpoint `/s/` en `${TWENTY_API_URL}/s\`. Tu componente de front llama a esa ruta con el `RestApiClient` de `twenty-client-sdk/rest`, que se autentica con el `TWENTY_APP_ACCESS_TOKEN` que Twenty inyecta en el worker. + +El `RestApiClient` está diseñado precisamente para esto. Lee `TWENTY_API_URL` y `TWENTY_APP_ACCESS_TOKEN` del entorno del worker, añade la cabecera `Authorization: Bearer`, serializa y analiza JSON, y lanza un `RestApiClientError` cuando faltan el token o la URL o la respuesta no es 2xx, para que no tengas que volver a implementar ese código repetitivo en cada componente. + +Un componente de front sin interfaz (headless) puede ejecutar la llamada al montar mediante el componente `Command` y luego desmontarse automáticamente: + +```tsx src/front-components/sync-prs.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Command } from 'twenty-sdk/command'; +import { RestApiClient } from 'twenty-client-sdk/rest'; + +const SyncPrs = () => { + const execute = async () => { + const client = new RestApiClient(); + + await client.post('/s/github/fetch-prs', { + owner: 'twentyhq', + repo: 'twenty', + }); + }; + + return ; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'sync-prs', + description: 'Triggers the fetch-prs logic function', + isHeadless: true, + component: SyncPrs, +}); +``` + +La ruta que se pasa al cliente es la ruta pública de la ruta: el `httpRouteTriggerSettings.path` de la función lógica con el prefijo `/s`. Mantén `isAuthRequired: true`; el cliente proporciona el token de acceso de la aplicación que Twenty emite para tu componente: + +```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, + }, +}); +``` + + +`TWENTY_API_URL` y `TWENTY_APP_ACCESS_TOKEN` se inyectan automáticamente; consulta [Variables de la aplicación](#application-variables). Dado que las variables de aplicación secretas nunca se exponen a los componentes de front, mantén las claves de API y otra lógica confidencial en la función de lógica, no en el componente de front. + + +### Referencia de `RestApiClient` + +Importa `RestApiClient` desde `twenty-client-sdk/rest`. Pertenece a la misma familia de clientes que `CoreApiClient` y `MetadataApiClient`, pero se dirige a las rutas HTTP de tu aplicación en lugar de a la API de GraphQL. + +| Método | Descripción | +| --------------------------------- | -------------------------------------------- | +| `get(path, options?)` | Envía una solicitud `GET` | +| `post(path, body?, options?)` | Envía una solicitud `POST` | +| `put(path, body?, options?)` | Envía una solicitud `PUT` | +| `patch(path, body?, options?)` | Envía una solicitud `PATCH` | +| `delete(path, options?)` | Envía una solicitud `DELETE` | +| `request(method, path, options?)` | Solicitud genérica con cualquier método HTTP | + +`options` acepta `headers`, `query` (un registro de parámetros de cadena de consulta; los valores nulos o indefinidos se omiten) y un `AbortSignal` mediante `signal`. Un objeto `body` que no sea de tipo `FormData` se serializa automáticamente como JSON. Ante un `401`, el cliente actualiza el token de acceso una vez a través del host y vuelve a intentar la solicitud. + +La URL base y el token se resuelven desde el entorno de forma predeterminada. Pasa opciones de sobrescritura al constructor cuando sea necesario — por ejemplo, en pruebas: + +```ts +const client = new RestApiClient({ + baseUrl: 'https://api.example.com', + token: 'my-token', +}); +``` + +Las solicitudes fallidas lanzan un `RestApiClientError` que expone `status`, `statusText`, `url` y el `body` analizado: + +```tsx +import { RestApiClient, RestApiClientError } from 'twenty-client-sdk/rest'; + +const client = new RestApiClient(); + +try { + const prs = await client.get('/s/github/fetch-prs', { + query: { state: 'open' }, + }); +} catch (error) { + if (error instanceof RestApiClientError) { + console.error(error.status, error.body); + } +} +``` + +## Acceder al contexto de ejecución + +Dentro de tu componente, usa hooks del SDK para acceder al usuario actual, el registro y la instancia del componente: + +```tsx src/front-components/record-info.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { + useUserId, + useRecordId, + useFrontComponentId, +} from 'twenty-sdk/front-component'; + +const RecordInfo = () => { + const userId = useUserId(); + const recordId = useRecordId(); + const componentId = useFrontComponentId(); + + return ( +
+

User: {userId}

+

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

+

Component: {componentId}

+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f23456789012', + name: 'record-info', + component: RecordInfo, +}); +``` + +Hooks disponibles: + +| Hook | Devuelve | Descripción | +| --------------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------- | +| `useUserId()` | `string` o `null` | El ID del usuario actual | +| `useSelectedRecordIds()` | `string[]` | Todos los ID de los registros seleccionados (array vacío si no hay ninguno seleccionado) | +| `useRecordId()` | `string` o `null` | **Obsoleto.** Usa `useSelectedRecordIds()` en su lugar | +| `useFrontComponentId()` | `string` | El ID de esta instancia del componente | +| `useColorScheme()` | `'light'` o `'dark'` | La combinación de colores activa de la interfaz de usuario del host (`System` ya está resuelto) | +| `useFrontComponentExecutionContext(selector)` | varía | Accede al contexto de ejecución completo con una función selectora | + +## Variables de aplicación + +Las variables de aplicación definidas en [`defineApplication()`](/l/es/developers/extend/apps/config/application) con `isSecret: false` están disponibles dentro de los componentes de front mediante la utilidad `getApplicationVariable`: + +```tsx src/front-components/greeting.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getApplicationVariable } from 'twenty-sdk/front-component'; + +const Greeting = () => { + const recipientName = getApplicationVariable('DEFAULT_RECIPIENT_NAME') ?? 'World'; + + return

Hello, {recipientName}!

; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'greeting', + component: Greeting, +}); +``` + + +Las variables secretas (`isSecret: true`) **no** se exponen a los componentes de front. Solo están disponibles en las [funciones de lógica](/l/es/developers/extend/apps/logic/logic-functions), que se ejecutan del lado del servidor. Esto evita que valores confidenciales como las claves de API se envíen al navegador. + + +Las siguientes variables de sistema siempre están disponibles a través de `process.env`: + +| Variable | Descripción | +| ------------------------- | -------------------------------------------------------- | +| `TWENTY_API_URL` | URL base de la API de Twenty | +| `TWENTY_APP_ACCESS_TOKEN` | Token de corta duración limitado al rol de tu aplicación | + +## API de comunicación con el host + +Los componentes de frontend pueden activar navegación, modales y notificaciones usando funciones de `twenty-sdk`: + +| Función | Descripción | +| ----------------------------------------------- | -------------------------------------------- | +| `navigate(to, params?, queryParams?, options?)` | Navegar a una página en la aplicación | +| `openSidePanelPage(params)` | Abrir un panel lateral | +| `closeSidePanel()` | Cerrar el panel lateral | +| `openCommandConfirmationModal(params)` | Mostrar un cuadro de diálogo de confirmación | +| `enqueueSnackbar(params)` | Mostrar una notificación tipo toast | +| `unmountFrontComponent()` | Desmontar el componente | +| `updateProgress(progress)` | Actualizar un indicador de progreso | + +Aquí tienes un ejemplo que usa la API del host para mostrar un snackbar y cerrar el panel lateral después de que una acción finaliza: + +```tsx src/front-components/archive-record.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { useRecordId } from 'twenty-sdk/front-component'; +import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const ArchiveRecord = () => { + const recordId = useRecordId(); + + const handleArchive = async () => { + const client = new CoreApiClient(); + + await client.mutation({ + updateTask: { + __args: { id: recordId, data: { status: 'ARCHIVED' } }, + id: true, + }, + }); + + await enqueueSnackbar({ + message: 'Record archived', + variant: 'success', + }); + + await closeSidePanel(); + }; + + return ( +
+

Archive this record?

+ +
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'c9d0e1f2-a3b4-5678-cdef-789012345678', + name: 'archive-record', + description: 'Archives the current record', + component: ArchiveRecord, +}); +``` + +### Trabajar con varios registros + +Usa `useSelectedRecordIds()` para manejar varios registros seleccionados. Esto es útil para operaciones por lotes: + +```tsx src/front-components/bulk-export.tsx +import { defineFrontComponent, numberOfSelectedRecords } from 'twenty-sdk/define'; +import { useSelectedRecordIds } from 'twenty-sdk/front-component'; +import { enqueueSnackbar, closeSidePanel } from 'twenty-sdk/front-component'; +import { CoreApiClient } from 'twenty-sdk/clients'; + +const BulkExport = () => { + const selectedRecordIds = useSelectedRecordIds(); + + const handleExport = async () => { + const client = new CoreApiClient(); + + for (const recordId of selectedRecordIds) { + await client.mutation({ + updateTask: { + __args: { id: recordId, data: { exported: true } }, + id: true, + }, + }); + } + + await enqueueSnackbar({ + message: `Exported ${selectedRecordIds.length} records`, + variant: 'success', + }); + + await closeSidePanel(); + }; + + return ( +
+

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

+ +
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678901', + name: 'bulk-export', + description: 'Export selected records', + component: BulkExport, + command: { + universalIdentifier: 'd0e1f2a3-b4c5-6789-defa-012345678902', + label: 'Bulk Export', + availabilityType: 'RECORD_SELECTION', + conditionalAvailabilityExpression: numberOfSelectedRecords > 0, + }, +}); +``` + +## Recursos públicos + +Los componentes de frontend pueden acceder a archivos del directorio `public/` de la aplicación usando `getPublicAssetUrl`: + +```tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { getPublicAssetUrl } from 'twenty-sdk/utils'; + +const Logo = () => Logo; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'logo', + component: Logo, +}); +``` + +Consulta la [sección de recursos públicos](/l/es/developers/extend/apps/config/public-assets) para más detalles. + +## Estilo + +Los componentes de frontend admiten varios enfoques de estilos. Puedes usar: + +* **Estilos en línea** — `style={{ color: 'red' }}` +* **Componentes de Twenty UI** — importa desde `twenty-sdk/ui` (Button, Tag, Status, Chip, Avatar y más) +* **Emotion** — CSS-in-JS con `@emotion/react` +* **Styled-components** — patrones de `styled.div` +* **Tailwind CSS** — clases utilitarias +* **Cualquier librería CSS-in-JS** compatible con React + +```tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { Button, Tag, Status } from 'twenty-sdk/ui'; + +const StyledWidget = () => { + return ( +
+
+ ); +}; + +export default defineFrontComponent({ + universalIdentifier: 'e5f6a7b8-c9d0-1234-efab-567890123456', + name: 'styled-widget', + component: StyledWidget, +}); +``` diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx new file mode 100644 index 0000000000..bd0f1b223e --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/navigation-menu-items.mdx @@ -0,0 +1,44 @@ +--- +title: Elementos del menú de navegación +description: Agrega entradas personalizadas a la barra lateral del espacio de trabajo — enlaces a vistas guardadas o URLs externas. +icon: bars +--- + +Un **elemento del menú de navegación** es una entrada en la barra lateral izquierda. Usa `defineNavigationMenuItem()` para distribuir enlaces personalizados en la barra lateral — normalmente uno por cada [vista](/l/es/developers/extend/apps/layout/views) que publiques — o para apuntar a URL externas. + +```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, +}); +``` + +## Puntos clave + +* `type` determina a qué enlaza el elemento del menú. Cada tipo se asocia con un campo identificador específico: + + | Tipo | Qué hace | Campo obligatorio | + | ------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | + | `NavigationMenuItemType.VIEW` | Abre una vista guardada | `viewUniversalIdentifier` | + | `NavigationMenuItemType.LINK` | Abre una URL externa | `link` | + | `NavigationMenuItemType.FOLDER` | Agrupa elementos anidados bajo una etiqueta | `name` (y los elementos secundarios hacen referencia a la carpeta mediante `folderUniversalIdentifier`) | + | `NavigationMenuItemType.OBJECT` | Abre la página de índice predeterminada de un objeto | `targetObjectUniversalIdentifier` | + | `NavigationMenuItemType.PAGE_LAYOUT` | Abre un diseño de página independiente | `pageLayoutUniversalIdentifier` | + +* `position` controla el orden en la barra lateral. + +* `icon` y `color` son opcionales y personalizan el aspecto de la entrada. + +* `folderUniversalIdentifier` también está disponible en cualquier elemento para anidarlo dentro de un elemento padre de tipo `FOLDER`. + + +**Error común:** crear un objeto sin una vista asociada y un elemento del menú de navegación hace que ese objeto sea invisible para los usuarios. A menos que sea un objeto técnico/interno, cada objeto personalizado debería tener una vista predeterminada *y* una entrada en la barra lateral que apunte a ella. + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/overview.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/overview.mdx new file mode 100644 index 0000000000..1297743710 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/overview.mdx @@ -0,0 +1,56 @@ +--- +title: Resumen +description: Coloca tu aplicación dentro de la interfaz de usuario de Twenty — entradas de la barra lateral, vistas guardadas, pestañas de la página de registro y componentes React aislados. +icon: table-columns +--- + +La **capa de diseño** de una aplicación de Twenty es todo lo que el usuario ve: dónde aparece la aplicación en la barra lateral, qué vistas de lista incluye, cómo se organizan sus páginas de detalles de registro y qué componentes React personalizados se renderizan dentro de esas páginas. + +```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 └─────────────────────┘ +``` + +## En esta sección + + + + `defineView` — configuraciones de listas guardadas: columnas visibles, filtros, grupos. + + + `defineNavigationMenuItem` — entradas de la barra lateral que apuntan a vistas o URL externas. + + + `definePageLayout` y `definePageLayoutTab` — pestañas y widgets en la página de detalles de un registro. + + + `defineFrontComponent` — componentes React aislados que se renderizan dentro de Twenty. + + + `defineCommandMenuItem` — registra componentes de frontend como entradas Cmd+K y acciones rápidas. + + + +## Dónde aparece la aplicación + +| Ubicación | Qué controla | Entidad | +| ------------------------------------------ | ---------------------------------------------------------------------------------------- | ----------------------------------------- | +| **Barra lateral** | Una entrada personalizada que enlaza a una vista guardada o a una URL externa | `defineNavigationMenuItem` | +| **Lista de registros** | Una configuración guardada para un objeto — columnas visibles, orden, filtros, grupos | `defineView` | +| **Página de detalles del registro** | Las pestañas y widgets en una página de registro (de tu propio objeto o de uno estándar) | `definePageLayout`, `definePageLayoutTab` | +| **Dentro de cualquiera de las anteriores** | Un widget React personalizado — botones, formularios, paneles, integraciones | `defineFrontComponent` | +| **Menú de comandos (Cmd+K)** | Una acción rápida fijada o un comando oculto | `defineCommandMenuItem` | + +Los componentes de frontend se ejecutan dentro de un Web Worker aislado usando Remote DOM — se renderizan de forma nativa en la página (no dentro de un iframe), pero no pueden acceder directamente a la página o al DOM del host. La comunicación con Twenty ocurre a través de una API de host de paso de mensajes. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/page-layouts.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/page-layouts.mdx new file mode 100644 index 0000000000..2bb753837f --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/page-layouts.mdx @@ -0,0 +1,132 @@ +--- +title: Diseños de Página +description: Personaliza las páginas de detalle de los registros — pestañas, widgets y dónde se renderizan los componentes de frontend — usando `definePageLayout` y `definePageLayoutTab`. +icon: table-columns +--- + +Un **page layout** controla cómo se organiza la página de detalle de un registro: qué pestañas aparecen y qué widgets contienen. Usa `definePageLayout()` para declarar un layout para un objeto que posees, o `definePageLayoutTab()` para agregar una sola pestaña a un layout que ya existe (tuyo o uno estándar de Twenty). + +| Caso de uso | Entidad | +| -------------------------------------------------------------------------- | --------------------- | +| Define todo el layout para una página de registro en un objeto que posees | `definePageLayout` | +| Agrega una pestaña a un layout existente (tu propio objeto o uno estándar) | `definePageLayoutTab` | + +## definePageLayout + +Usa esto cuando eres propietario de toda la página de detalle; normalmente para un objeto personalizado que definiste tú mismo. + +```ts src/page-layouts/example-record-page-layout.ts +import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define'; +import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; +import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; + +export default definePageLayout({ + universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134', + name: 'Example Record Page', + type: 'RECORD_PAGE', + objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, + tabs: [ + { + universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5', + title: 'Hello World', + position: 50, + icon: 'IconWorld', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + widgets: [ + { + universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d', + title: 'Hello World', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], + }, + ], +}); +``` + +### Puntos clave + +* `type` suele ser `'RECORD_PAGE'` para personalizar la vista de detalles de un objeto específico. +* `objectUniversalIdentifier` especifica a qué objeto se aplica este diseño. +* Cada `tab` define una sección de la página con un `title`, `position` y `layoutMode` (`CANVAS` para un diseño libre). +* Cada `widget` dentro de una pestaña puede renderizar un [componente de frontend](/l/es/developers/extend/apps/layout/front-components), una lista de relaciones u otros tipos de widget integrados. +* `position` en las pestañas controla su orden. Usa valores más altos (p. ej., 50) para colocar pestañas personalizadas después de las integradas. + +## definePageLayoutTab + +Usa esto cuando solo quieras **agregar** una pestaña a un layout existente; por ejemplo, una pestaña de analíticas en la página estándar de Company o una pestaña de resumen de IA añadida al layout de tu propio objeto. + +```ts src/page-layouts/example-extra-tab.ts +import { + definePageLayoutTab, + PageLayoutTabLayoutMode, + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; +import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world'; + +export default definePageLayoutTab({ + universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001', + pageLayoutUniversalIdentifier: + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage + .universalIdentifier, + title: 'Hello World', + position: 1000, + icon: 'IconWorld', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + widgets: [ + { + universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002', + title: 'Hello World', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], +}); +``` + +### Puntos clave + +* `pageLayoutUniversalIdentifier` es **obligatorio** y debe apuntar a un page layout que ya exista en el momento de la instalación, ya sea un layout estándar de Twenty o uno definido por tu propia app. Las referencias entre apps a layouts que pertenecen a otra app instalada no son compatibles hoy en día. Cuando falta el layout padre, la instalación falla con un error de validación claro. + +* Para los diseños estándar de Twenty, importa los identificadores desde `twenty-sdk/define`: + + ```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 + // … + ``` + + Cada entrada de diseño también expone sus `tabs` y sus `widgets`, para que puedas hacer referencia a cualquier nivel: + + ```ts + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.universalIdentifier + STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets.fields.universalIdentifier + ``` + + También hay disponible un alias corto `STANDARD_PAGE_LAYOUT`: + + ```ts + import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define'; + + STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier; + ``` + +* `widgets` están limitados solo a esta pestaña: hacen referencia a [componentes de frontend](/l/es/developers/extend/apps/layout/front-components), vistas, etc., exactamente igual que los widgets definidos en línea en `definePageLayout`. + +* `position` controla el orden con respecto a las pestañas existentes en el diseño de página de destino. Elige un valor que sitúe tu pestaña donde la quieras, en relación con las pestañas integradas. + +* Usa esto en lugar de `definePageLayout` cuando solo quieras agregar a un layout existente. Usa `definePageLayout` cuando eres propietario de todo el layout. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx new file mode 100644 index 0000000000..3c791bcf2e --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/layout/views.mdx @@ -0,0 +1,97 @@ +--- +title: Vistas +description: Incluye vistas guardadas preconfiguradas — orden de columnas, filtros y grupos — para los objetos de tu aplicación. +icon: lista +--- + +Una **vista** es una configuración guardada de cómo se muestran los registros de un objeto: qué campos aparecen, su orden, si son visibles y qué filtros o grupos se aplican. Usa `defineView()` para incluir vistas preconfiguradas con tu aplicación — normalmente una vista de índice predeterminada para cada objeto personalizado que crees. + +```ts src/views/example-view.ts +import { defineView, ViewKey } from 'twenty-sdk/define'; +import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; +import { NAME_FIELD_UNIVERSAL_IDENTIFIER } from '../objects/example-object'; + +export default defineView({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + name: 'All example items', + objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER, + icon: 'IconList', + key: ViewKey.INDEX, + position: 0, + fields: [ + { + universalIdentifier: 'f926bdb7-6af7-4683-9a09-adbca56c29f0', + fieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER, + position: 0, + isVisible: true, + size: 200, + }, + ], +}); +``` + +## Puntos clave + +* `objectUniversalIdentifier` especifica a qué objeto se aplica esta vista. Puede ser un objeto personalizado que hayas definido o un objeto estándar de Twenty. +* `key` determina el tipo de vista — `ViewKey.INDEX` es la vista de lista principal para el objeto. +* `fields` controla qué columnas aparecen y en qué orden. Cada campo referencia un `fieldMetadataUniversalIdentifier`. +* También puedes definir `filters`, `filterGroups`, `groups` y `fieldGroups` para configuraciones avanzadas. +* `position` controla el orden cuando existen múltiples vistas para el mismo objeto. + +## Filtros + +Una vista puede incluir filtros preaplicados. Cada filtro tiene tres coordenadas: el **campo** que se está filtrando, el **operando** (cómo comparar) y el **valor** (contra qué comparar). Las tres deben alinearse: usar un operando que no aplique a un tipo de campo será rechazado en el momento de la sincronización. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Operandos admitidos por tipo de campo + +| Tipo de campo | Operandos admitidos | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `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` | + +> Los tipos de campo con nombres similares pueden usar operandos completamente diferentes; `SELECT` y `MULTI_SELECT` son un caso común. + +### Forma del valor por operando + +El campo `value` siempre es un valor serializable en JSON, pero su forma esperada depende del operando: + +| Familia de operandos | Forma del valor | Ejemplo | +| ------------------------------------------------------ | ------------------------------------- | ------------------------ | +| `IS`, `IS_NOT` en `SELECT` | array de claves de opciones (cadenas) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` en `MULTI_SELECT` | array de claves de opciones (cadenas) | `['TAG_A']` | +| `IS`, `IS_NOT` en `RELATION` | array de IDs de registros (uuids) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` en campos de tipo texto | cadena | `'acme'` | +| `IS`, `IS_NOT` en `NUMBER` | cadena (el valor) | `'5'` | +| `IS` en `RATING` / `UUID` | cadena (el valor) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | cadena (el límite) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` en `DATE` / `DATE_TIME` | cadena ISO 8601 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | cadena vacía | `''` | +| `IS` en `BOOLEAN` | `'true'` o `'false'` | `'true'` | + +## Cómo aparecen las vistas en la interfaz de usuario + +Una vista por sí sola no es accesible desde la barra lateral. Para que aparezca allí, vincúlala con un [elemento del menú de navegación](/l/es/developers/extend/apps/layout/navigation-menu-items) de tipo `VIEW` que apunte al `universalIdentifier` de la vista. Ese es el patrón canónico: cada objeto personalizado suele incluir una vista predeterminada + una entrada en la barra lateral que la abre. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/logic/connections.mdx b/packages/twenty-docs/l/es/developers/extend/apps/logic/connections.mdx new file mode 100644 index 0000000000..2e7e02da7f --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/logic/connections.mdx @@ -0,0 +1,192 @@ +--- +title: Conexiones +description: Permite que tu aplicación actúe en nombre de un usuario en servicios de terceros mediante OAuth. +icon: plug +--- + +Las conexiones son credenciales que un usuario posee para un servicio externo (Linear, GitHub, Slack, ...). Tu aplicación declara **cómo** se obtienen esas credenciales — un **proveedor de conexión** — y las consume en tiempo de ejecución para realizar llamadas autenticadas a la API de terceros. + +Actualmente solo se admite OAuth 2.0. Los futuros tipos de credenciales (tokens de acceso personal, claves de API, autenticación básica) se integrarán en la misma interfaz — las aplicaciones que ya usan `defineConnectionProvider({ type: 'oauth', ... })` no necesitarán migrar. + + + + + +Un proveedor de conexión describe el flujo de OAuth que tu aplicación necesita. El usuario hace clic en "Agregar conexión" en la configuración de tu aplicación, completa la pantalla de consentimiento del proveedor y se crea una fila `ConnectedAccount` en su espacio de trabajo. + +Una configuración funcional necesita **dos archivos** — el proveedor de conexión y una declaración `serverVariables` correspondiente en `defineApplication` que contiene las credenciales del cliente OAuth. + +```ts src/connection-providers/linear-connection.ts +import { defineConnectionProvider } from 'twenty-sdk/define'; + +export default defineConnectionProvider({ + universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f', + name: 'linear', + displayName: 'Linear', + icon: 'IconBrandLinear', + type: 'oauth', + oauth: { + authorizationEndpoint: 'https://linear.app/oauth/authorize', + tokenEndpoint: 'https://api.linear.app/oauth/token', + scopes: ['read', 'write'], + // These must match keys in `defineApplication.serverVariables` below. + clientIdVariable: 'LINEAR_CLIENT_ID', + clientSecretVariable: 'LINEAR_CLIENT_SECRET', + // Optional: defaults to 'json'. Some providers (Linear, Slack) want + // 'form-urlencoded' for the token request. + tokenRequestContentType: 'form-urlencoded', + // Optional: defaults to true. Disable only if the provider rejects PKCE. + usePkce: false, + // Optional: extra query params on the authorize URL. + // authorizationParams: { prompt: 'consent' }, + // Optional: provider's RFC 7009 token revocation endpoint, called on disconnect. + // revokeEndpoint: 'https://example.com/oauth/revoke', + }, +}); +``` + +```ts src/application.config.ts +import { defineApplication } from 'twenty-sdk/define'; + +export default defineApplication({ + universalIdentifier: '...', + displayName: 'Linear', + description: 'Connect Linear to Twenty.', + // OAuth client credentials live on the app registration (one OAuth app per + // Twenty server, configured by the admin) — not per-workspace. Declare them + // as serverVariables so the admin can fill them in once for all installs. + serverVariables: { + LINEAR_CLIENT_ID: { + description: 'OAuth client ID from your Linear OAuth application.', + isSecret: false, + isRequired: true, + }, + LINEAR_CLIENT_SECRET: { + description: 'OAuth client secret from your Linear OAuth application.', + isSecret: true, + isRequired: true, + }, + }, +}); +``` + +Puntos clave: + +* `name` es la cadena de identificador única utilizada en `listConnections({ providerName })` (kebab-case, debe coincidir con `^[a-z][a-z0-9-]*$`). +* `displayName` se muestra en la pestaña de configuración por aplicación y en la lista de herramientas de IA. +* `clientIdVariable` / `clientSecretVariable` son **nombres**, no valores — deben coincidir con las claves declaradas en `defineApplication.serverVariables`. Los `client_id` y `client_secret` reales los introduce el administrador del servidor a través de la interfaz de registro de la aplicación; nunca se incluyen en tu repositorio. +* Usa `serverVariables` (no `applicationVariables`) — las credenciales de OAuth son a nivel de servidor y hay una aplicación OAuth por servidor de Twenty. +* Hasta que ambos `serverVariables` estén completos, la pestaña de configuración por aplicación muestra un aviso de "requiere administrador del servidor" y el botón "Agregar conexión" está deshabilitado. +* `type: 'oauth'` es el único valor admitido actualmente. El discriminador es compatible hacia adelante: tipos futuros (`'pat'`, `'api-key'`, ...) agregarán nuevos bloques de subconfiguración junto a `oauth`. + +La URL de callback de OAuth que tu proveedor debe autorizar es: + +``` +https:///auth/apps/callback +``` + + + + + +Dentro de un controlador de función de lógica, `listConnections({ providerName })` devuelve las filas `ConnectedAccount` de esta aplicación para el proveedor indicado, con tokens de acceso actualizados. + +```ts src/logic-functions/handlers/create-linear-issue-handler.ts +import { listConnections } from 'twenty-sdk/logic-function'; + +export const createLinearIssueHandler = async (input: { + teamId?: string; + title?: string; +}) => { + if (!input.teamId || !input.title) { + return { success: false, error: 'teamId and title are required' }; + } + + const connections = await listConnections({ providerName: 'linear' }); + + // Workspace-shared credentials win when present; fall back to the first + // user-visibility one. For HTTP-route triggers you typically pick the + // request user's connection via event.userWorkspaceId instead. + const connection = + connections.find((c) => c.visibility === 'workspace') ?? connections[0]; + + if (!connection) { + return { + success: false, + error: + 'Linear is not connected. Open the app settings and click "Add connection".', + }; + } + + // Use connection.accessToken to call the third-party API. + const response = await fetch('https://api.linear.app/graphql', { + method: 'POST', + headers: { + Authorization: `Bearer ${connection.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`, + }), + }); + + return { success: response.ok }; +}; +``` + +Cada conexión tiene: + +| Campo | Descripción | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `id` | ID de fila único; pásalo a `getConnection(id)` para volver a obtener una sola conexión | +| `visibility` | `'user'` (privada para un miembro del espacio de trabajo) o `'workspace'` (compartida con todos los miembros) | +| `scopes` | Permisos de OAuth concedidos por el proveedor de origen (distintos de `visibility` — no están relacionados) | +| `userWorkspaceId` | El id de userWorkspace del propietario — útil para elegir "la conexión del usuario de la solicitud" en activadores de rutas HTTP | +| `accessToken` | Token de acceso OAuth actualizado (se renueva automáticamente si ha expirado) | +| `name` / `handle` | El nombre para mostrar de la conexión (derivado automáticamente en el callback de OAuth, el usuario puede cambiarlo) | +| `authFailedAt` | Se establece cuando la actualización más reciente falló; el usuario debe reconectarse | + +Puntos clave: + +* Pasa `{ providerName }` para filtrar por proveedor; omítelo para obtener todas las conexiones que posee esta aplicación en todos los proveedores. +* El servidor actualiza de forma transparente el token de acceso antes de devolver la respuesta. Tu controlador siempre ve un token utilizable (o `authFailedAt` establecido). +* `getConnection(id)` es el equivalente de una sola fila. + + + + + +Cuando un usuario hace clic en "Agregar conexión", se le solicita que elija una visibilidad: + +* **Solo para mí** — la credencial es privada para el usuario que se conecta. Cualquier función de lógica llamada en su nombre (activador de ruta HTTP con `isAuthRequired: true`) la ve; los activadores de cron y los eventos de base de datos no. +* **Compartida en el espacio de trabajo** — cualquier miembro del espacio de trabajo puede usar la credencial. Los activadores de cron y de base de datos también la ven, ya que no tienen usuario de la solicitud. + +Usa la adecuada para cada controlador: + +```ts +// HTTP-route trigger — prefer the request user's own connection. +const conn = + connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ?? + connections.find((c) => c.visibility === 'workspace'); + +// Cron trigger — no request user; only shared credentials are sensible. +const conn = connections.find((c) => c.visibility === 'workspace'); +``` + +Se permiten múltiples conexiones por (usuario, proveedor), por lo que el mismo usuario puede tener "Linear personal" y "Linear de trabajo" a la vez. + + + + + +Para cada proveedor de conexión, el administrador del servidor debe registrar primero una aplicación OAuth en el servicio de terceros. + +1. Ve a la configuración de desarrollador del proveedor (p. ej., https://linear.app/settings/api/applications/new). +2. Configura el **URI de redirección** en `\/auth/apps/callback`. +3. Copia el **Client ID** y el **Client Secret** generados. +4. Abre la aplicación instalada en Twenty como administrador del servidor → establece los valores en los `serverVariables` correspondientes. +5. Luego, los miembros del espacio de trabajo pueden agregar conexiones desde la sección **Conexiones** por aplicación. + + + + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx b/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx new file mode 100644 index 0000000000..68769150ff --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/logic/logic-functions.mdx @@ -0,0 +1,515 @@ +--- +title: Funciones de lógica +description: Defina funciones de TypeScript del lado del servidor con activadores HTTP, de cron y de eventos de base de datos. +icon: bolt +--- + +Las funciones lógicas son funciones de TypeScript del lado del servidor que se ejecutan en la plataforma Twenty. Pueden activarse mediante solicitudes HTTP, programaciones de cron o eventos de base de datos — y también pueden exponerse como herramientas para agentes de IA. + + + + +Cada archivo de función usa `defineLogicFunction()` para exportar una configuración con un controlador y desencadenadores opcionales. + +```ts src/logic-functions/createPostCard.logic-function.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import type { RoutePayload } from 'twenty-sdk/logic-function'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (params: RoutePayload) => { + const client = new CoreApiClient(); + const body = (params.body ?? {}) as { name?: string }; + const name = body.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'; + + const result = await client.mutation({ + createPostCard: { + __args: { data: { name } }, + id: true, + name: true, + }, + }); + return result; +}; + +export default defineLogicFunction({ + universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', + name: 'create-new-post-card', + timeoutSeconds: 2, + handler, + httpRouteTriggerSettings: { + path: '/post-card/create', + httpMethod: 'POST', + isAuthRequired: true, + }, + /*databaseEventTriggerSettings: { + eventName: 'people.created', + },*/ + /*cronTriggerSettings: { + pattern: '0 0 1 1 *', + },*/ +}); +``` + +Tipos de desencadenadores disponibles: +* **httpRoute**: Expone tu función en una ruta y método HTTP **bajo el endpoint `/s/`**: +> p. ej., `path: '/post-card/create'` se puede invocar en `https://your-twenty-server.com/s/post-card/create` + + +Para invocar una función de lógica activada por una ruta desde un componente de frontend (headless), consulta [Llamar a una función de lógica](/l/es/developers/extend/apps/layout/front-components#calling-a-logic-function). + +* **cron**: Ejecuta tu función en un horario usando una expresión CRON. +* **databaseEvent**: Se ejecuta en eventos del ciclo de vida de objetos del espacio de trabajo. Cuando la operación del evento es `updated`, se pueden especificar campos específicos que se deben escuchar en la matriz `updatedFields`. Si se deja sin definir o vacío, cualquier actualización activará la función. +> p. ej. `person.updated`, `*.created`, `company.*` + + +También puedes ejecutar manualmente una función usando la CLI: + +```bash filename="Terminal" +yarn twenty dev:function:exec -n create-new-post-card -p '{"key": "value"}' +``` + +```bash filename="Terminal" +yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf +``` + +Puedes ver los registros con: + +```bash filename="Terminal" +yarn twenty dev:function:logs +``` + + +#### Carga útil del disparador de ruta + +Cuando un desencadenador de ruta invoca tu función de lógica, esta recibe un objeto `RoutePayload` que sigue el +[formato AWS HTTP API v2](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). +Importa el tipo `RoutePayload` desde `twenty-sdk/logic-function`: + +```ts +import type { RoutePayload } from 'twenty-sdk/logic-function'; + +const handler = async (event: RoutePayload) => { + const { headers, queryStringParameters, pathParameters, body } = event; + const { method, path } = event.requestContext.http; + + return { message: 'Success' }; +}; +``` + +El tipo `RoutePayload` tiene la siguiente estructura: + + | Propiedad | Tipo | Descripción | Ejemplo | + | ---------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | + | `headers` | `Record\` | Encabezados HTTP (solo aquellos listados en `forwardedRequestHeaders`) | consulta la sección de abajo | + | `queryStringParameters` | `Record\` | Parámetros de consulta (valores múltiples unidos con comas) | `/users?ids=1&ids=2&ids=3&name=Alice` -> `{ ids: '1,2,3', name: 'Alice' }` | + | `pathParameters` | `Record\` | Parámetros de ruta extraídos del patrón de la ruta | `/users/:id`, `/users/123` -> `{ id: '123' }` | + | `body` | `object \| null` | Cuerpo de la solicitud analizado (JSON) | `{ id: 1 }` -> `{ id: 1 }` | + | `rawBody` | `string \| undefined` | Cuerpo de la solicitud UTF-8 original, antes del análisis de JSON. Útil para verificar firmas de webhooks de estilo HMAC (p. ej., `X-Hub-Signature-256` de GitHub, Stripe). `undefined` cuando el entorno de ejecución no lo conservó. | | + | `isBase64Encoded` | `boolean` | Indica si el cuerpo está codificado en base64 | | + | `requestContext.http.method` | `string` | Método HTTP (GET, POST, PUT, PATCH, DELETE) | | + | `requestContext.http.path` | `string` | Ruta de la solicitud sin procesar | | + + +#### forwardedRequestHeaders + +De forma predeterminada, los encabezados HTTP de las solicitudes entrantes **no** se pasan a tu función de lógica por razones de seguridad. +Para acceder a encabezados específicos, enuméralos explícitamente en el arreglo `forwardedRequestHeaders`: + +```ts +export default defineLogicFunction({ + universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf', + name: 'webhook-handler', + handler, + httpRouteTriggerSettings: { + path: '/webhook', + httpMethod: 'POST', + isAuthRequired: false, + forwardedRequestHeaders: ['x-webhook-signature', 'content-type'], + }, +}); +``` + +En tu controlador, accede a los encabezados reenviados así: + +```ts +const handler = async (event: RoutePayload) => { + const signature = event.headers['x-webhook-signature']; + const contentType = event.headers['content-type']; + + // Validate webhook signature... + return { received: true }; +}; +``` + + +Los nombres de los encabezados se normalizan a minúsculas. Accede a ellos usando claves en minúsculas (p. ej., `event.headers['content-type']`). + + +#### Respuesta HTTP personalizada + +De forma predeterminada, devolver un valor sencillo desde tu controlador lo envía de vuelta como una respuesta `200` (JSON para objetos, `text/plain` para cadenas). Para controlar el código de estado y los encabezados de la respuesta, devuelve un `Response` desde `twenty-sdk/logic-function`: + +```ts +import { Response } from 'twenty-sdk/logic-function'; + +const handler = async (event: RoutePayload) => { + return new Response('

Hello

', { + status: 201, + headers: { 'content-type': 'text/html' }, + }); +}; +``` + +Por razones de seguridad, los encabezados de la respuesta están restringidos a una lista de permitidos. Cualquier encabezado que no esté en la lista (por ejemplo, `Set-Cookie`, encabezados CORS como `Access-Control-Allow-Origin`, o encabezados personalizados `X-*`) se descarta silenciosamente antes de que se envíe la respuesta. Los encabezados de respuesta permitidos son: + +* `content-type` +* `content-language` +* `content-disposition` +* `cache-control` +* `retry-after` + + +El código de estado debe ser un código de estado HTTP válido (entre 100 y 599). Los nombres de los encabezados de respuesta se comparan sin distinguir mayúsculas de minúsculas. + + +#### Payload del disparador de evento de base de datos + +Cuando un disparador de evento de base de datos invoca tu función de lógica, esta recibe un `DatabaseEventPayload` por cada registro modificado. El payload combina metadatos sobre el espacio de trabajo y el objeto de origen con el evento a nivel de registro. + +```ts +import type { + DatabaseEventPayload, + ObjectRecordCreateEvent, + ObjectRecordDestroyEvent, + ObjectRecordUpdateEvent, +} from 'twenty-sdk/logic-function'; + +type Person = { + id: string; + emails?: { primaryEmail?: string }; +}; +``` + +La carga útil incluye: + +| Propiedad | Descripción | +| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | +| `name` | Nombre del evento, como `person.updated`. | +| `workspaceId` | Espacio de trabajo donde ocurrió el evento. | +| `objectMetadata` | Metadatos del objeto que cambió. | +| `recordId` | Id del registro que cambió. | +| `userId`, `userWorkspaceId`, `workspaceMemberId` | Campos del actor cuando el evento fue causado por un usuario del espacio de trabajo. | +| `propiedades` | Datos del registro para el evento, con `before`, `after`, `diff` y `updatedFields` según la operación. | + +| Evento | Datos del registro | +| ------------------ | -------------------------------------------------------------------------------------------------------------- | +| `person.created` | `event.properties.after` | +| `person.updated` | `event.properties.before`, `event.properties.after`, `event.properties.diff`, `event.properties.updatedFields` | +| `person.destroyed` | `event.properties.before` | + +Para eliminaciones lógicas (soft deletes), `.deleted` sigue la estructura de estilo de actualización porque el campo `deletedAt` del registro cambia. +Para eliminaciones permanentes, usa `.destroyed`. + + +`databaseEventTriggerSettings.updatedFields` filtra qué eventos de actualización activan la función. +`event.properties.updatedFields` te indica qué campos realmente cambiaron en el evento actual. + + +Ejemplo de evento de creación: + +```ts +type PersonCreatedEvent = DatabaseEventPayload< + ObjectRecordCreateEvent +>; + +const handler = async (event: PersonCreatedEvent) => { + const person = event.properties.after; + + return { + personId: event.recordId, + email: person.emails?.primaryEmail, + }; +}; +``` + +Ejemplo de evento de actualización: + +```ts +type PersonUpdatedEvent = DatabaseEventPayload< + ObjectRecordUpdateEvent +>; + +const handler = async (event: PersonUpdatedEvent) => { + const { before, after, diff, updatedFields } = event.properties; + + return { + personId: event.recordId, + updatedFields, + previousEmail: before.emails?.primaryEmail, + currentEmail: after.emails?.primaryEmail, + emailDiff: diff.emails, + }; +}; +``` + +Ejecutar solo en actualizaciones de correo electrónico: + +```ts +export default defineLogicFunction({ + ..., + databaseEventTriggerSettings: { + eventName: 'person.updated', + updatedFields: ['emails'], + }, +}); +``` + +Ejemplo de evento de eliminación: + +```ts +type PersonDestroyedEvent = DatabaseEventPayload< + ObjectRecordDestroyEvent +>; + +const handler = async (event: PersonDestroyedEvent) => { + const personBeforeDestroy = event.properties.before; + + return { + personId: event.recordId, + email: personBeforeDestroy.emails?.primaryEmail, + }; +}; +``` + +#### Exponer una función como herramienta de IA o acción de flujo de trabajo + +Las funciones lógicas pueden exponerse en dos ámbitos, cada uno con su propio disparador: + +* **`toolTriggerSettings`** — hace que la función sea descubrible por las funciones de IA de Twenty (chat, MCP, llamadas a funciones). Usa el JSON Schema estándar, el formato que los LLM entienden de forma nativa. +* **`workflowActionTriggerSettings`** — hace que la función aparezca como un paso en el constructor visual de flujos de trabajo. Usa el `InputSchema` completo de Twenty para que el constructor pueda renderizar editores de campos adecuados, selectores de variables y etiquetas. + +Una función puede optar por una, por la otra o por ambas. Se ubican junto a `cronTriggerSettings`, `databaseEventTriggerSettings` y `httpRouteTriggerSettings` — mismo patrón, misma estructura. + +```ts src/logic-functions/enrich-company.logic-function.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (params: { companyName: string; domain?: string }) => { + const client = new CoreApiClient(); + + const result = await client.mutation({ + createTask: { + __args: { + data: { + title: `Enrich data for ${params.companyName}`, + body: `Domain: ${params.domain ?? 'unknown'}`, + }, + }, + id: true, + }, + }); + + return { taskId: result.createTask.id }; +}; + +export default defineLogicFunction({ + universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479', + name: 'enrich-company', + description: 'Enrich a company record with external data', + timeoutSeconds: 10, + handler, + toolTriggerSettings: {}, +}); +``` + +Puntos clave: + +* Una función puede mezclar superficies — declara tanto `toolTriggerSettings` como `workflowActionTriggerSettings` para exponerla en el chat Y en el constructor de flujos de trabajo. +* Ambos, `toolTriggerSettings.inputSchema` y `workflowActionTriggerSettings.inputSchema`, son opcionales. Cuando se omiten, el generador del manifiesto los infiere a partir del código fuente del controlador (JSON Schema para la herramienta de IA, `InputSchema` de Twenty para la acción de flujo de trabajo). Proporciona uno explícitamente cuando quieras un tipado más rico — por ejemplo, con campos compatibles con `FieldMetadataType` como `CURRENCY` o `RELATION` para el constructor de flujos de trabajo, o con campos `description` que el agente de IA pueda leer: + +```ts +export default defineLogicFunction({ + ..., + toolTriggerSettings: { + inputSchema: { + type: 'object', + properties: { + companyName: { + type: 'string', + description: 'The name of the company to enrich', + }, + domain: { + type: 'string', + description: 'The company website domain (optional)', + }, + }, + required: ['companyName'], + }, + }, +}); +``` + + +**Escribe una buena `description`.** Los agentes de IA dependen del campo `description` de la función para decidir cuándo usar la herramienta. Sé específico acerca de lo que hace la herramienta y cuándo debe invocarse. + + +
+
+ + +**Hooks de instalación** — los controladores de preinstalación y postinstalación — comparten este entorno de ejecución, pero se declaran con sus propias funciones 'define' y no aceptan configuraciones de disparador. Consulta [Hooks de instalación](/l/es/developers/extend/apps/config/install-hooks) para `definePreInstallLogicFunction` y `definePostInstallLogicFunction`. + + +## Clientes de API tipados (twenty-client-sdk) + +El paquete `twenty-client-sdk` proporciona dos clientes GraphQL tipados para interactuar con la API de Twenty desde tus funciones de lógica y componentes de frontend. + +| Cliente | Importar | Endpoint | ¿Generado? | +| ------------------- | ---------------------------- | ---------------------------------------------------------------------- | --------------------------------------- | +| `CoreApiClient` | `twenty-client-sdk/core` | `/graphql` — datos del espacio de trabajo (registros, objetos) | Sí, en tiempo de desarrollo/compilación | +| `MetadataApiClient` | `twenty-client-sdk/metadata` | `/metadata` — configuración del espacio de trabajo, cargas de archivos | No, viene preconstruido | + + + + +`CoreApiClient` es el cliente principal para consultar y mutar datos del espacio de trabajo. Se **genera a partir del esquema de tu espacio de trabajo** durante `yarn twenty dev` o `yarn twenty dev:build`, por lo que está completamente tipado para coincidir con tus objetos y campos. + +```ts +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const client = new CoreApiClient(); + +// Query records +const { companies } = await client.query({ + companies: { + edges: { + node: { + id: true, + name: true, + domainName: { + primaryLinkLabel: true, + primaryLinkUrl: true, + }, + }, + }, + }, +}); + +// Create a record +const { createCompany } = await client.mutation({ + createCompany: { + __args: { + data: { + name: 'Acme Corp', + }, + }, + id: true, + name: true, + }, +}); +``` + +El cliente usa una sintaxis de conjunto de selección: pasa `true` para incluir un campo, usa `__args` para los argumentos y anida objetos para las relaciones. Obtienes autocompletado completo y verificación de tipos basados en el esquema de tu espacio de trabajo. + + +**CoreApiClient se genera en tiempo de desarrollo/compilación.** Si intentas usarlo sin ejecutar primero `yarn twenty dev` o `yarn twenty dev:build`, lanzará un error. La generación ocurre automáticamente: la CLI inspecciona el esquema GraphQL de tu espacio de trabajo y genera un cliente tipado usando `@genql/cli`. + + +#### Uso de CoreSchema para anotaciones de tipos + +`CoreSchema` proporciona tipos de TypeScript que coinciden con los objetos de tu espacio de trabajo; útil para tipar el estado de componentes o parámetros de funciones: + +```ts +import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; +import { useState } from 'react'; + +const [company, setCompany] = useState< + Pick | undefined +>(undefined); + +const client = new CoreApiClient(); +const result = await client.query({ + company: { + __args: { filter: { position: { eq: 1 } } }, + id: true, + name: true, + }, +}); +setCompany(result.company); +``` + + + + +`MetadataApiClient` viene preconstruido con el SDK (no se requiere generación). Consulta el endpoint `/metadata` para la configuración del espacio de trabajo, las aplicaciones y las cargas de archivos. + +```ts +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; + +const metadataClient = new MetadataApiClient(); + +// List first 10 objects in the workspace +const { objects } = await metadataClient.query({ + objects: { + edges: { + node: { + id: true, + nameSingular: true, + namePlural: true, + labelSingular: true, + isCustom: true, + }, + }, + __args: { + filter: {}, + paging: { first: 10 }, + }, + }, +}); +``` + +#### Subir archivos + +El `MetadataApiClient` incluye un método `uploadFile` para adjuntar archivos a los campos de tipo archivo: + +```ts +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import * as fs from 'fs'; + +const metadataClient = new MetadataApiClient(); + +const fileBuffer = fs.readFileSync('./invoice.pdf'); + +const uploadedFile = await metadataClient.uploadFile( + fileBuffer, // file contents as a Buffer + 'invoice.pdf', // filename + 'application/pdf', // MIME type + '58a0a314-d7ea-4865-9850-7fb84e72f30b', // field universalIdentifier +); + +console.log(uploadedFile); +// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' } +``` + +| Parámetro | Tipo | Descripción | +| ---------------------------------- | -------- | ----------------------------------------------------------------------------- | +| `fileBuffer` | `Buffer` | El contenido sin procesar del archivo | +| `filename` | `string` | El nombre del archivo (se utiliza para el almacenamiento y la visualización) | +| `contentType` | `string` | Tipo MIME (de forma predeterminada es `application/octet-stream` si se omite) | +| `fieldMetadataUniversalIdentifier` | `string` | El `universalIdentifier` del campo de tipo de archivo de tu objeto | + +Puntos clave: +* Utiliza el `universalIdentifier` del campo (no su ID específico del espacio de trabajo), por lo que tu código de carga funciona en cualquier espacio de trabajo donde esté instalada tu aplicación. +* La `url` devuelta es una URL firmada que puedes usar para acceder al archivo cargado. + + + + + + Cuando tu código se ejecuta en Twenty (funciones de lógica o componentes de frontend), la plataforma inyecta credenciales como variables de entorno: + + * `TWENTY_API_URL` — URL base de la API de Twenty + * `TWENTY_APP_ACCESS_TOKEN` — Token de corta duración con alcance al rol de función predeterminado de tu aplicación + + No necesitas pasar estas credenciales a los clientes — leen de `process.env` automáticamente. Los permisos de la clave de API están determinados por el rol declarado con `defineApplicationRole()` (o referenciado mediante `defaultRoleUniversalIdentifier` en `application-config.ts`). + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx b/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx new file mode 100644 index 0000000000..a7475eedf6 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/logic/overview.mdx @@ -0,0 +1,55 @@ +--- +title: Resumen +description: TypeScript del lado del servidor que se ejecuta dentro de Twenty, activado por rutas HTTP, programaciones cron, eventos de base de datos, herramientas de IA o acciones de flujos de trabajo. +icon: bolt +--- + +La **capa de lógica** de una app de Twenty es el código que *se ejecuta*: controladores de TypeScript del lado del servidor que reaccionan a solicitudes HTTP, programaciones cron y cambios en registros; habilidades y agentes de IA que viven dentro del espacio de trabajo; y conexiones OAuth que permiten que tus funciones actúen en nombre de un usuario en servicios de terceros. + +```text + ┌─ HTTP route ──┐ + │ Cron schedule │ + │ Database event │ ┌────────────────────┐ + triggers ─┤ AI tool call ├─────▶│ Logic function │ + │ Workflow action │ │ (your handler) │ + │ Manual exec │ └────────────────────┘ + └────────────────────┘ │ + ▼ + ┌────────────────────────────┐ + │ Twenty API (records) │ + │ Third-party API │ + │ (via Connection token) │ + └────────────────────────────┘ +``` + +## En esta sección + + + + El bloque de construcción principal: tipos de disparadores, cargas útiles y el cliente de API tipado. + + + Instrucciones reutilizables para agentes de IA y asistentes con mensajes de sistema personalizados. + + + Credenciales OAuth que tu app mantiene para servicios de terceros — Linear, GitHub, Slack y más. + + + +## Tipos de disparadores de un vistazo + +Una función de lógica selecciona uno o más disparadores: cada entrada a continuación es un campo independiente en `defineLogicFunction()`: + +| Disparador | Cuándo se ejecuta | Configuración | +| ------------------------------- | --------------------------------------------------------------- | ------------------------------- | +| **Ruta HTTP** | Una solicitud llega a tu endpoint `/s/\` | `httpRouteTriggerSettings` | +| **Cron** | Coincide una expresión CRON | `cronTriggerSettings` | +| **Evento de base de datos** | Se crea, actualiza o elimina un registro del espacio de trabajo | `databaseEventTriggerSettings` | +| **Herramienta de IA** | Una funcionalidad de IA de Twenty decide llamar a tu función | `toolTriggerSettings` | +| **Acción del Flujo de Trabajo** | Un paso de flujo de trabajo invoca tu función | `workflowActionTriggerSettings` | + +Las funciones se ejecutan en un entorno aislado en procesos independientes de Node.js y acceden al espacio de trabajo a través de un cliente de API tipado con un ámbito limitado al rol declarado en [`defineApplication()`](/l/es/developers/extend/apps/config/application). + + +**Hooks de instalación**: el código que se ejecuta antes o después de la instalación comparte este entorno de ejecución pero usa sus propias funciones define y se encuentra en [Config → Install Hooks](/l/es/developers/extend/apps/config/install-hooks). + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/logic/skills-and-agents.mdx b/packages/twenty-docs/l/es/developers/extend/apps/logic/skills-and-agents.mdx new file mode 100644 index 0000000000..0f55371476 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/logic/skills-and-agents.mdx @@ -0,0 +1,138 @@ +--- +title: Habilidades y agentes +description: Define habilidades y agentes de IA para tu aplicación. +icon: robot +--- + + + Las habilidades y los agentes están actualmente en pruebas alfa. La funcionalidad es operativa, pero sigue evolucionando. + + +Las aplicaciones pueden definir capacidades de IA que residen dentro del espacio de trabajo — instrucciones de habilidades reutilizables y agentes con prompts de sistema personalizados. + + + + +Las habilidades definen instrucciones y capacidades reutilizables que los agentes de IA pueden usar dentro de tu espacio de trabajo. Usa `defineSkill()` para definir habilidades con validación incorporada: + +```ts src/skills/example-skill.ts +import { defineSkill } from 'twenty-sdk/define'; + +export default defineSkill({ + universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + name: 'sales-outreach', + label: 'Sales Outreach', + description: 'Guides the AI agent through a structured sales outreach process', + icon: 'IconBrain', + content: `You are a sales outreach assistant. When reaching out to a prospect: +1. Research the company and recent news +2. Identify the prospect's role and likely pain points +3. Draft a personalized message referencing specific details +4. Keep the tone professional but conversational`, +}); +``` + +Puntos clave: +* `name` es una cadena identificadora única de la habilidad (se recomienda kebab-case). +* `label` es el nombre para mostrar, legible para humanos, que aparece en la interfaz de usuario. +* `content` contiene las instrucciones de la habilidad — este es el texto que usa el agente de IA. +* `icon` (opcional) establece el icono mostrado en la interfaz de usuario. +* `description` (opcional) proporciona contexto adicional sobre el propósito de la habilidad. + + + + +Los agentes son asistentes de IA que viven dentro de tu espacio de trabajo. Usa `defineAgent()` para crear agentes con un prompt de sistema personalizado: + +```ts src/agents/example-agent.ts +import { defineAgent } from 'twenty-sdk/define'; + +export default defineAgent({ + universalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123', + name: 'sales-assistant', + label: 'Sales Assistant', + description: 'Helps the sales team draft outreach emails and research prospects', + icon: 'IconRobot', + prompt: 'You are a helpful sales assistant. Help users with their questions and tasks.', +}); +``` + +Puntos clave: +* `name` es una cadena identificadora única del agente (se recomienda kebab-case). +* `label` es el nombre para mostrar que aparece en la interfaz de usuario. +* `prompt` es el mensaje del sistema que define el comportamiento del agente. +* `description` (opcional) proporciona contexto sobre lo que hace el agente. +* `icon` (opcional) establece el icono mostrado en la interfaz de usuario. +* `modelId` (opcional) reemplaza el modelo de IA predeterminado usado por el agente. +* `responseFormat` (opcional) controla la forma de la salida del agente. De forma predeterminada es `{ type: 'text' }` para texto de formato libre. Usa `{ type: 'json', schema }` para forzar una salida JSON estructurada. + +De forma predeterminada, un agente devuelve texto de formato libre. Para obtener una salida estructurada, establece `responseFormat` en `{ type: 'json' }` y proporciona un `schema`: + +```ts src/agents/structured-agent.ts +import { defineAgent } from 'twenty-sdk/define'; + +export default defineAgent({ + universalIdentifier: 'c4d5e6f7-a8b9-0123-cdef-456789012345', + name: 'lead-scorer', + label: 'Lead Scorer', + prompt: 'Score the lead and explain your reasoning.', + responseFormat: { + type: 'json', + schema: { + type: 'object', + properties: { + score: { type: 'number', description: 'Lead score from 0 to 100' }, + summary: { type: 'string', description: 'Short reasoning for the score' }, + }, + required: ['score', 'summary'], + additionalProperties: false, + }, + }, +}); +``` + +Notas sobre el esquema: +* El esquema es un objeto plano: el `type` de cada propiedad debe ser un tipo primitivo (`string`, `number` o `boolean`). Los objetos anidados y los arrays no son compatibles. +* `description` (opcional) en cada propiedad guía al modelo sobre qué debe poner allí. +* `required` (opcional) enumera las propiedades que el modelo siempre debe devolver. +* `additionalProperties: false` (opcional) prohíbe cualquier propiedad que no esté declarada en `properties`. + + + + +`runAgent()` permite que una función de lógica ejecute uno de los agentes de tu app (con sus skills y tools). Identifica el agente mediante el `universalIdentifier` que pasaste a `defineAgent()`: + +```ts src/logic-functions/run-enricher.ts +import { runAgent } from 'twenty-sdk/logic-function'; + +const { result, error, success } = await runAgent({ + agentUniversalIdentifier: 'b3c4d5e6-f7a8-9012-bcde-f34567890123', + prompt: 'Enrich House Ad : fill empty fields from its listing URL.', +}); +``` + +Puntos clave: +* El agente se ejecuta **sincrónicamente** y puede leer/actualizar registros por sí mismo mediante sus propias tools; `runAgent()` se resuelve una vez que la ejecución finaliza. +* Una app solo puede ejecutar sus propios agentes. +* El [rol predeterminado](/l/es/developers/extend/apps/config/roles) de la app debe conceder el indicador de permiso `AI`; agrega `SystemPermissionFlag.AI` a sus `permissionFlagUniversalIdentifiers` (o establece `canAccessAllTools: true`). + Sin esto, `runAgent()` falla con un error de permisos. +* Establece un valor generoso de `timeoutSeconds` en la función de lógica: las ejecuciones de agentes pueden tardar varios segundos. +* `success` es `true` y `result` es no nulo cuando la ejecución finaliza; en caso de fallo `success` es `false`, `result` es `null`, y `error` contiene el motivo (por ejemplo, cuando el espacio de trabajo se queda sin créditos de AI en mitad de la ejecución). + +```ts src/roles/default-role.ts +import { defineApplicationRole, SystemPermissionFlag } from 'twenty-sdk/define'; + +export default defineApplicationRole({ + universalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061', + label: 'Default function role', + // runAgent() requires the AI permission flag on the app's default role. + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.AI], +}); +``` + + + **Evita los bucles:** si llamas a `runAgent()` desde un trigger de evento de base de datos `*.updated` y el agente actualiza el mismo registro, limita el alcance del trigger con `updatedFields` a un campo que el agente nunca escriba (por ejemplo, la URL de origen), o comprueba si algún campo de destino sigue vacío antes de llamar a `runAgent()`. + + + + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx new file mode 100644 index 0000000000..7a26b76116 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/cli.mdx @@ -0,0 +1,105 @@ +--- +title: CLI +description: Comandos de `yarn twenty` para ejecutar funciones, transmitir registros en tiempo real, gestionar instalaciones de aplicaciones y cambiar entre remotos. +icon: terminal +--- + +Más allá de `dev`, `dev:build`, `dev:add` y `dev:typecheck`, la CLI de `yarn twenty` proporciona comandos para ejecutar funciones, ver registros y gestionar instalaciones de aplicaciones. + +## Ejecutar funciones (`yarn twenty dev:function:exec`) + +Ejecuta manualmente una función de lógica sin activarla mediante HTTP, cron o evento de base de datos: + +```bash filename="Terminal" +# Execute by function name +yarn twenty dev:function:exec -n create-new-post-card + +# Execute by universalIdentifier +yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf + +# Pass a JSON payload +yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}' + +# Execute the post-install function +yarn twenty dev:function:exec --postInstall +``` + +## Ver registros de funciones (`yarn twenty dev:function:logs`) + +Transmitir en tiempo real los registros de ejecución de las funciones de lógica de tu aplicación: + +```bash filename="Terminal" +# Stream all function logs +yarn twenty dev:function:logs + +# Filter by function name +yarn twenty dev:function:logs -n create-new-post-card + +# Filter by universalIdentifier +yarn twenty dev:function:logs -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf +``` + + +Esto es diferente de `yarn twenty docker:logs`, que muestra los registros del contenedor de Docker. `yarn twenty dev:function:logs` muestra los registros de ejecución de funciones de tu aplicación desde el servidor de Twenty. + + +## Generando el cliente tipado (`yarn twenty dev:generate-client`) + +Regenera el cliente de API tipado (`twenty-client-sdk`) a partir del esquema del remoto activo, sin compilar ni sincronizar una aplicación. Úsalo para obtener un cliente tipado en cualquier proyecto — como un servicio backend que vive en un repositorio separado — que se comunica con tu instancia de Twenty: + +```bash filename="Terminal" +# In your project (no Twenty app definition required) +yarn add twenty-sdk twenty-client-sdk + +# Connect to the Twenty instance to generate the client from +yarn twenty remote:add + +# Generate the typed client into node_modules/twenty-client-sdk +yarn twenty dev:generate-client +``` + +Luego importa el cliente en tu código: + +```typescript +import { CoreApiClient } from 'twenty-client-sdk/core'; +``` + +Vuelve a ejecutar el comando cada vez que cambie tu modelo de datos para actualizar los tipos generados. + + +El cliente se genera dentro de `node_modules`, por lo que no se incluye en tus commits de código. Ejecuta `yarn twenty dev:generate-client` después de cada instalación (por ejemplo, en un script de `postinstall` o en CI). + + +## Desinstalar una aplicación (`yarn twenty app:uninstall`) + +Elimina tu aplicación del espacio de trabajo activo: + +```bash filename="Terminal" +yarn twenty app:uninstall + +# Skip the confirmation prompt +yarn twenty app:uninstall --yes +``` + +## Gestión de remotos + +Un **remoto** es un servidor de Twenty al que se conecta tu app. Durante la configuración, el generador crea uno automáticamente para ti. Puedes añadir más remotos o cambiar entre ellos en cualquier momento. + +```bash filename="Terminal" +# Add a new remote (opens a browser for OAuth login) +yarn twenty remote:add + +# Connect to a local Twenty server (auto-detects port 2020 or 3000) +yarn twenty remote:add --local + +# Add a remote non-interactively (useful for CI) +yarn twenty remote:add --url https://your-twenty-server.com --api-key $TWENTY_API_KEY --as my-remote + +# List all configured remotes +yarn twenty remote:list + +# Set the active remote +yarn twenty remote:use +``` + +Tus credenciales se almacenan en `~/.twenty/config.json`. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/overview.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/overview.mdx new file mode 100644 index 0000000000..45e497e16f --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/overview.mdx @@ -0,0 +1,32 @@ +--- +title: Resumen +description: "Compila, prueba y envía tu aplicación: comandos de CLI, pruebas de integración, CI y publicación en un servidor o en npm." +icon: rocket +--- + +La **capa de operaciones** es todo lo que haces *a* tu aplicación en lugar de *con* ella: invocar comandos de CLI, ejecutar pruebas de integración contra un servidor Twenty real, configurar CI y enviar versiones, ya sea como un tarball implementado en un único servidor o como un paquete de npm listado en el marketplace. + +```text + develop ─▶ test ─▶ build ─▶ deploy / publish + ─────── ──── ───── ───────────────── + yarn yarn yarn yarn twenty app:publish --private (tarball → one server) + twenty test twenty + dev dev:build yarn twenty app:publish (npm → marketplace) +``` + +## En esta sección + + + + Referencia de `yarn twenty` — exec, logs, uninstall, remotes. + + + Qué comando usar y cuándo, cómo leer el diff de sincronización y una guía escalonada de recuperación. + + + Configuración de Vitest, pruebas de integración, comprobación de tipos, flujo de trabajo de CI. + + + Compilar, desplegar un tarball, publicar en npm, instalar. + + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx new file mode 100644 index 0000000000..e651be4d32 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/publishing.mdx @@ -0,0 +1,294 @@ +--- +title: Publicación +icon: subir +description: Distribuye tu aplicación de Twenty en el marketplace o despliégala internamente. +--- + +## Resumen + +Una vez que tu aplicación esté [compilada y probada localmente](/l/es/developers/extend/apps/getting-started/concepts), tienes dos vías para distribuirla: + +* **Desplegar un paquete tar** — sube tu aplicación directamente a un servidor Twenty específico para uso interno o privado. +* **Publicar en npm** — incluye tu aplicación en el marketplace de Twenty para que cualquier espacio de trabajo la descubra e instale. + +Ambas rutas comienzan en el mismo paso de **build**. + +## Compilar tu aplicación + +Ejecuta el comando `build` para compilar tu aplicación y generar un `manifest.json` listo para distribución: + +```bash filename="Terminal" +yarn twenty dev:build +``` + +Esto compila el código fuente de TypeScript, transpila las funciones de lógica y los componentes de frontend, y escribe todo en `.twenty/output/`. Agrega `--tarball` para generar también un paquete `.tgz` para la distribución manual o para el comando `publish`. + +## Despliegue en un servidor (tarball) + +Para aplicaciones que no quieres que estén disponibles públicamente — herramientas propietarias, integraciones solo para empresas o compilaciones experimentales — puedes desplegar un tarball directamente en un servidor de Twenty. + +### Prerrequisitos + +Antes de desplegar, necesitas un remoto configurado que apunte al servidor de destino. Los remotos almacenan la URL del servidor y las credenciales de autenticación localmente en `~/.twenty/config.json`. + +Agrega un remoto: + +```bash filename="Terminal" +yarn twenty remote:add --url https://your-twenty-server.com --as production +``` + +### Despliegue + +Compila y sube tu aplicación al servidor en un solo paso: + +```bash filename="Terminal" +yarn twenty app:publish --private +# To deploy to a specific remote: +# yarn twenty app:publish --private --remote production +``` + +### Compartir una aplicación desplegada + + +Compartir aplicaciones privadas (tarball) entre espacios de trabajo es una función de **Enterprise**. La pestaña **Distribución** mostrará un aviso de actualización en lugar de los controles para compartir hasta que tu espacio de trabajo tenga una clave de Enterprise válida. Ve a [Configuración > Panel de administración > Enterprise](/settings/admin-panel#enterprise) para habilitarla. + + +Las aplicaciones en tarball no se listan en el marketplace público, por lo que otros espacios de trabajo en el mismo servidor no las descubrirán navegando. Una vez que tu espacio de trabajo esté en el plan Enterprise, puedes compartir una aplicación desplegada de esta manera: + +1. Ve a **Configuración > Aplicaciones > Registros** y abre tu aplicación +2. En la pestaña **Distribución**, haz clic en **Copiar enlace para compartir** +3. Comparte este enlace con usuarios de otros espacios de trabajo — los llevará directamente a la página de instalación de la aplicación + +El enlace para compartir usa la URL base del servidor (sin ningún subdominio de espacio de trabajo), por lo que funciona para cualquier espacio de trabajo en el servidor. + +### Gestión de versiones + +Al actualizar una aplicación tarball ya desplegada, el servidor requiere que la `version` en `package.json` sea **estrictamente mayor** (según el orden de [semver](https://semver.org)) que la versión actualmente desplegada. Volver a desplegar la misma versión, o subir una inferior, se rechaza antes de que se almacene el tarball — verás un error `VERSION_ALREADY_EXISTS` en la CLI. + +Para publicar una actualización: + +1. Incrementa el campo `version` en tu `package.json` (p. ej., `1.2.3` → `1.2.4`, `1.3.0` o `2.0.0`) +2. Ejecuta `yarn twenty app:publish --private` (o `yarn twenty app:publish --private --remote production`) +3. Los espacios de trabajo que tengan la aplicación instalada verán la actualización disponible en su configuración + + +Las etiquetas de prelanzamiento funcionan como se espera: incrementar `1.0.0-rc.1` → `1.0.0-rc.2` está permitido, y una versión final como `1.0.0` se reconoce correctamente como superior a `1.0.0-rc.5`. La versión en `package.json` debe ser en sí misma una cadena semver válida. + + +{/* TODO: add screenshot of the Upgrade button */} + +### Compatibilidad de la versión del servidor + +Si tu app usa una función introducida en una versión específica del servidor Twenty (por ejemplo, proveedores de OAuth agregados en v2.3.0), debes declarar la versión mínima del servidor que tu app requiere usando el campo `engines.twenty` en `package.json`: + +```json filename="package.json" +{ + "name": "twenty-my-app", + "version": "1.0.0", + "engines": { + "node": "^24.5.0", + "twenty": ">=2.3.0" + } +} +``` + +El valor es un [rango semver](https://github.com/npm/node-semver#ranges) estándar. Patrones comunes: + +| Rango | Significado | +| ---------------------------------- | -------------------------------------------------------------------- | +| `>=2.3.0` | Cualquier servidor desde 2.3.0 en adelante | +| `>=2.3.0 \<3.0.0` | 2.3.0 o posterior, pero por debajo de la siguiente versión principal | +| `^2.3.0` | Igual que `>=2.3.0 \<3.0.0` | + +**Qué sucede durante la implementación e instalación:** + +* Si `engines.twenty` está configurado y la versión del servidor de destino no cumple el rango, la implementación (carga del tarball) o la instalación se rechaza con un error `SERVER_VERSION_INCOMPATIBLE` y un mensaje que indica tanto el rango requerido como la versión real del servidor. +* Si `engines.twenty` **no está configurado**, la app se acepta en cualquier versión del servidor (retrocompatible con las apps existentes). +* Si el servidor no tiene `APP_VERSION` configurado, se omite la comprobación. + + +El servidor es la autoridad en la comprobación — valida `engines.twenty` tanto en la carga del tarball como en la instalación en el espacio de trabajo. Si implementas un tarball fuera de banda o instalas desde el marketplace, el servidor sigue garantizando la compatibilidad. + + +## CI/CD automatizado (flujos de trabajo preconfigurados) + +Las aplicaciones generadas con `create-twenty-app` incluyen de forma predeterminada dos flujos de trabajo de GitHub Actions, en `.github/workflows/`. Están listas para ejecutarse en cuanto hagas push del repositorio a GitHub — no se necesita configuración adicional para CI, y CD solo requiere un único secreto. + +### CI — `ci.yml` + +Ejecuta pruebas de integración en cada push a `main` y en cada pull request. + +**Qué hace:** + +1. Obtiene el código fuente de tu aplicación. +2. Inicia una instancia de prueba aislada de Twenty usando la acción compuesta `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main` (el equivalente en CI de `yarn twenty docker:start --test`). +3. Habilita Corepack, configura Node.js desde tu `.nvmrc` e instala las dependencias con `yarn install --immutable`. +4. Ejecuta `yarn test`, pasando `TWENTY_API_URL` y `TWENTY_API_KEY` de la instancia iniciada para que tus pruebas puedan comunicarse con un servidor real. + +**Ajustes de configuración:** + +* `TWENTY_VERSION` (variable de entorno; por defecto `latest`) — fija la versión del servidor de Twenty usada en CI editando esto en `ci.yml`. +* La concurrencia se agrupa por `github.ref` y cancela las ejecuciones en progreso cuando hay nuevos pushes. + +No se requieren secretos — la instancia de prueba es efímera y existe solo durante la ejecución del trabajo. + +### CD — `cd.yml` + +Despliega tu aplicación en un servidor de Twenty configurado en cada push a `main` y, opcionalmente, desde un pull request cuando se aplica la etiqueta `deploy`. + +**Qué hace:** + +1. Obtiene el head del PR (para PR etiquetados) o el commit enviado. +2. Ejecuta `twentyhq/twenty/.github/actions/deploy-twenty-app@main` — el equivalente en CI de `yarn twenty app:publish --private`. +3. Ejecuta `twentyhq/twenty/.github/actions/install-twenty-app@main` para que la versión recién desplegada se instale en el espacio de trabajo de destino. + +**Configuración necesaria:** + +| Configuración | Dónde | Propósito | +| ----------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `TWENTY_DEPLOY_URL` | `env` en `cd.yml` (por defecto `http://localhost:3000`) | El servidor de Twenty al que se va a desplegar. Cámbialo por la URL real de tu servidor antes del primer uso. | +| `TWENTY_DEPLOY_API_KEY` | Repositorio de GitHub **Settings → Secrets and variables → Actions** | Clave de API con permiso de despliegue en el servidor de destino. | + + +La `TWENTY_DEPLOY_URL` predeterminada de `http://localhost:3000` es un marcador de posición — no alcanzará nada desde un runner alojado por GitHub. Actualízala a la URL pública de tu servidor (o usa un runner autohospedado con acceso a la red) antes de habilitar CD. + + +**Activar un despliegue de vista previa desde un PR:** + +Añade la etiqueta `deploy` a un pull request. La condición `if:` en `cd.yml` ejecutará el trabajo para ese PR usando el commit head del PR, lo que te permitirá validar un cambio en el servidor de destino antes de hacer merge. + +### Fijar las acciones reutilizables + +Ambos flujos de trabajo hacen referencia a acciones reutilizables en `@main`, por lo que las actualizaciones de acciones en el repositorio `twentyhq/twenty` se aplican automáticamente. Si quieres compilaciones deterministas, reemplaza `@main` por un SHA de commit o una etiqueta de versión en cada línea `uses:`. + +## Publicación en npm + +Publicarla en npm hace que tu aplicación sea visible en el marketplace de Twenty. Cualquier espacio de trabajo de Twenty puede explorar, instalar y actualizar aplicaciones del marketplace directamente desde la interfaz de usuario. + +### Requisitos + +* Una cuenta de [npm](https://www.npmjs.com) +* La palabra clave `twenty-app` en la matriz `keywords` de tu `package.json` (agrégala manualmente — no se incluye de forma predeterminada en la plantilla `create-twenty-app`) + +```json filename="package.json" +{ + "name": "twenty-app-postcard-sender", + "version": "1.0.0", + "keywords": ["twenty-app"] +} +``` + +### Metadatos del Marketplace + +La configuración de `defineApplication()` admite campos opcionales que controlan cómo aparece tu aplicación en el marketplace. Usa `logoUrl` y `screenshots` para hacer referencia a imágenes de la carpeta `public/`: + +```ts src/application-config.ts +export default defineApplication({ + universalIdentifier: '...', + displayName: 'My App', + description: 'A great app', + logoUrl: 'public/logo.png', + screenshots: [ + 'public/screenshot-1.png', + 'public/screenshot-2.png', + ], +}); +``` + +Consulta el [acordeón de defineApplication](/l/es/developers/extend/apps/config/application#marketplace-metadata) en la página Building Apps para ver la lista completa de campos del marketplace (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.). + +#### Dimensiones recomendadas de las capturas de pantalla + +El marketplace muestra `screenshots` en un contenedor fijo de `8:5` (por ejemplo, `1600×1000 px`). + + +Las capturas de pantalla de cualquier relación de aspecto se muestran completas y nunca se recortan, pero las que sean significativamente más altas o más estrechas que `8:5` mostrarán franjas vacías a los lados. + + +### Publicar + +```bash filename="Terminal" +yarn twenty app:publish +``` + +Para publicar con una dist-tag específica (p. ej., `beta` o `next`): + +```bash filename="Terminal" +yarn twenty app:publish --tag beta +``` + +### Cómo funciona el descubrimiento en el marketplace + +El servidor de Twenty sincroniza su catálogo del marketplace desde el registro de npm **cada hora**. + +Puedes activar la sincronización de inmediato en lugar de esperar: + +```bash filename="Terminal" +yarn twenty dev:catalog-sync +# To target a specific remote: +# yarn twenty dev:catalog-sync --remote production +``` + +Los metadatos que se muestran en el marketplace provienen de tu configuración de `defineApplication()` — campos como `displayName`, `description`, `author`, `category`, `logoUrl`, `screenshots`, `aboutDescription`, `websiteUrl` y `termsUrl`. + + +Si tu aplicación no define un `aboutDescription` en `defineApplication()`, el marketplace usará automáticamente el `README.md` de tu paquete en npm como el contenido de la página Acerca de. Esto significa que puedes mantener un único README tanto para npm como para el marketplace de Twenty. Si quieres una descripción diferente en el marketplace, establece explícitamente `aboutDescription`. + + +### Publicación en CI + +Usa este flujo de trabajo de GitHub Actions para publicar automáticamente en cada versión (usa [OIDC](https://docs.npmjs.com/trusted-publishers)): + +```yaml filename=".github/workflows/publish.yml" +name: Publish +on: + release: + types: [published] + +permissions: + contents: read + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: https://registry.npmjs.org + - run: yarn install --immutable + - run: npx twenty dev:build + - run: npm publish --provenance --access public + working-directory: .twenty/output +``` + +Para otros sistemas de CI (GitLab CI, CircleCI, etc.), se aplican los mismos tres comandos: `yarn install`, `yarn twenty dev:build` y luego `npm publish` desde `.twenty/output`. + + +**npm provenance** es opcional pero recomendable. Publicar con `--provenance` añade una insignia de confianza a tu ficha de npm, permitiendo que los usuarios verifiquen que el paquete se compiló a partir de un commit específico en una canalización de CI pública. Consulta la [documentación de npm sobre provenance](https://docs.npmjs.com/generating-provenance-statements) para las instrucciones de configuración. + + +## Instalar aplicaciones + +Una vez que una aplicación esté publicada (npm) o desplegada (tarball), los espacios de trabajo pueden instalarla a través de la interfaz de usuario. + +Ve a la página **Configuración > Aplicaciones** en Twenty, donde se pueden explorar e instalar tanto las aplicaciones del marketplace como las desplegadas mediante tarball. + +{/* TODO: add screenshot of the UI when the app is registered */} + +También puedes instalar aplicaciones desde la línea de comandos: + +```bash filename="Terminal" +yarn twenty app:install +``` + + +El servidor aplica el versionado semver al instalar, reflejando las reglas del despliegue: + +* Instalar la misma versión que ya está instalada en tu espacio de trabajo se rechaza con un error `APP_ALREADY_INSTALLED`. +* Instalar una versión inferior a la que está instalada actualmente se rechaza con un error `CANNOT_DOWNGRADE_APPLICATION`. + +Para instalar una versión más reciente, primero despliégala o publícala y luego vuelve a ejecutar `yarn twenty app:install`. + diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx new file mode 100644 index 0000000000..db90f4fec1 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/sync-and-recovery.mdx @@ -0,0 +1,111 @@ +--- +title: Sincronización y recuperación +description: Qué comando usar y cuándo, cómo leer la salida de sincronización y una escalera de recuperación para cuando los metadatos locales se desvían, antes de llegar a un restablecimiento completo. +icon: brújula +--- + +El desarrollo de aplicaciones locales gira en torno a la **sincronización**: la CLI recompila tu manifiesto y el servidor aplica solo la diferencia entre este y los metadatos que ya se encuentran en tu espacio de trabajo. Esta página explica qué comando usar, cómo leer qué cambió una sincronización y qué hacer, en orden, cuando el estado local parece inconsistente. + +## Qué comando usar y cuándo + + +Para la iteración local del día a día casi siempre quieres `yarn twenty dev`. La implementación y la publicación son para enviar versiones, **no** para el ciclo local. + + +| Quieres… | Comando | Notas | +| --------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| Iterar localmente con sincronización en tiempo real | `yarn twenty dev` | Supervisa tus archivos y sincroniza en cada cambio. | +| Sincronizar una vez y salir (CI, scripts, hooks) | `yarn twenty dev --once` | Una compilación + sincronización, luego sale. | +| Previsualizar cambios **sin aplicarlos** | `yarn twenty dev --once --dry-run` | Calcula e imprime el diff; no escribe nada. | +| Eliminar la aplicación del espacio de trabajo | `yarn twenty app:uninstall` | Agrega `--yes` para omitir la confirmación. | +| Enviar un tarball a un servidor | `yarn twenty app:publish --private` | Requiere una versión de `package.json` **estrictamente superior**; consulta [Publicación](/l/es/developers/extend/apps/operations/publishing). | +| Publicar en el marketplace (npm) | `yarn twenty app:publish` | — | +| Instalar / actualizar una versión implementada | `yarn twenty app:install` | Instala la versión actualmente implementada. | +| Borrar el servidor local y empezar desde cero | `yarn twenty docker:reset` | Elimina **todos** los datos locales: último recurso. | + +### La sincronización local no necesita un aumento de versión + +La regla de `version` estrictamente creciente (`VERSION_ALREADY_EXISTS` al implementar, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` al instalar) se aplica a **`app:publish` / `app:install`**: la ruta de publicación. `yarn twenty dev` sincroniza tu manifiesto en su lugar y nunca requiere un cambio de versión, por lo que no necesitas tocar `package.json` para iterar. Si te encuentras aumentando la versión para probar un cambio local, estás usando la ruta de publicación cuando lo que quieres es el ciclo de desarrollo. + +## Leer la salida de la sincronización + +Cada sincronización muestra los cambios de metadatos que aplicó (o aplicaría, con `--dry-run`): + +```text filename="Terminal" +Metadata changes: 2 created, 1 updated, 1 deleted + created objectMetadata rocket + created fieldMetadata timelineActivities + updated fieldMetadata launchedAt + deleted pageLayout legacyTab +✓ Synced +``` + +Este es tu primer diagnóstico: te indica exactamente qué objetos, campos y diseños cambiaron, para que puedas confirmar que una sincronización hizo lo que esperabas antes de revisar la interfaz de usuario. + +Cuando una sincronización falla en una sola entidad, el error nombra la entidad implicada y su `universalIdentifier`, por ejemplo: + +```text +Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337) failed +``` + +Usa ese identificador para encontrar la entidad en tu manifiesto (y, si es necesario, en el espacio de trabajo) en lugar de adivinar cuál entra en conflicto. + +## Previsualizar cambios (simulación) + +`yarn twenty dev --once --dry-run` compila tu manifiesto, le pide al servidor el plan de migración y lo imprime, **sin aplicar nada**. Es la forma segura de responder "¿qué cambiaría esta sincronización?" antes de comprometerte a ella. + +```bash filename="Terminal" +yarn twenty dev --once --dry-run +``` + +```text filename="Terminal" +Building manifest... +Computing metadata diff (dry run, nothing will be applied)... +Metadata changes: 1 created, 1 updated + created fieldMetadata timelineActivities + updated objectMetadata rocket +✓ Dry run complete for My App — no changes were applied +``` + +Una simulación: + +* **No escribe nada**: sin migración de metadatos, sin actualización del registro de la aplicación, sin cambios de roles/pestañas predeterminados y sin generación del cliente de la API. +* Devuelve el **mismo diff** que aplicaría una sincronización real, para que puedas revisar por adelantado las entidades creadas/actualizadas/eliminadas. +* Es útil antes de un cambio arriesgado, al revisar un cambio generado por IA o en un script que deba fallar si está a punto de producirse un cambio inesperado. + + +Una simulación solo previsualiza cambios de **metadatos** y requiere que la aplicación se haya sincronizado al menos una vez (para que el espacio de trabajo la conozca). Si la ejecutas con una aplicación que nunca se sincronizó, el servidor indicará que la aplicación no está instalada; ejecuta `yarn twenty dev` una vez primero. + + +## Escalera de recuperación + +Cuando los metadatos locales parezcan incorrectos, ve escalando en este orden y detente en cuanto te hayas desbloqueado. Cada paso es más disruptivo que el anterior. + +1. **Volver a sincronizar.** Ejecuta `yarn twenty dev --once` de nuevo. Las sincronizaciones son idempotentes: volver a ejecutar un manifiesto limpio es seguro y suele resolver un problema transitorio. +2. **Previsualizar el plan.** Ejecuta `yarn twenty dev --once --dry-run` para ver exactamente qué pretende cambiar la siguiente sincronización, sin aplicarlo. +3. Lee el error identificado. Un conflicto suele señalar un identificador duplicado o reutilizado. +4. **Desinstalar y volver a instalar.** `yarn twenty app:uninstall`, luego vuelve a sincronizar (`yarn twenty dev`). Esto reconstruye los metadatos de la aplicación desde cero manteniendo intacto el resto de tu espacio de trabajo. +5. **Restablecimiento completo (último recurso).** `yarn twenty docker:reset`, luego vuelve a sembrar los datos y a sincronizar. + + +`yarn twenty docker:reset` elimina **todos** los datos de tu instancia local: todos los espacios de trabajo, registros y aplicaciones. Úsalo solo cuando los pasos anteriores hayan fallado. + + + +¿Te has encontrado con un error de metadatos? Por favor, [abre una incidencia](https://github.com/twentyhq/twenty/issues/new/choose) e incluye el mensaje de migración con error (con su tipo de metadatos y `universalIdentifier`), la salida de `Metadata changes` de la sincronización y los comandos que ejecutaste. + + +## Evita sincronizaciones concurrentes en un mismo espacio de trabajo + +La sincronización aplica migraciones de metadatos. Ejecutar varias operaciones de sincronización, implementación o instalación contra el **mismo espacio de trabajo al mismo tiempo** (por ejemplo, múltiples terminales o agentes de IA iterando en paralelo) puede entremezclar esas migraciones y dejar los metadatos en un estado parcialmente aplicado. + +El servidor serializa las sincronizaciones por espacio de trabajo para evitar esto, pero aun así deberías canalizar las operaciones de metadatos sensibles a través de un proceso **único** en lugar de lanzarlas de forma concurrente. Si orquestas el desarrollo con varios agentes, enruta sus llamadas de sincronización/implementación/instalación a través de una sola cola de modo que solo una se ejecute a la vez. + +## Distinguir los tipos de error + +Cuando algo sale mal, el diff de metadatos y los errores con nombre te permiten situar el fallo: + +* **Error de compilación del manifiesto**: la CLI falla antes de sincronizar (`MANIFEST_BUILD_FAILED`, `TYPECHECK_FAILED`); corrige el código fuente de tu aplicación. +* **Error de sincronización / migración**: la compilación tiene éxito, pero aplicar el diff falla, nombrando la entidad y el `universalIdentifier`; corrige los metadatos en conflicto. +* **Error de tiempo de ejecución del código de la aplicación**: la sincronización se completa correctamente, pero tus funciones lógicas o componentes se comportan de forma incorrecta en tiempo de ejecución; revisa los [registros de funciones](/l/es/developers/extend/apps/operations/cli). +* **Estado de instancia local**: nada de lo anterior aplica y el espacio de trabajo sigue viéndose mal; desciende por la escalera de recuperación. diff --git a/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx b/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx new file mode 100644 index 0000000000..c15ba57c69 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/apps/operations/testing.mdx @@ -0,0 +1,301 @@ +--- +title: Pruebas +description: Configuración de Vitest, pruebas de integración contra un servidor real de Twenty, comprobación de tipos e integración continua (CI) con GitHub Actions. +icon: flask +--- + +El SDK proporciona APIs programáticas que te permiten compilar, desplegar, instalar y desinstalar tu aplicación desde código de pruebas. Combinado con [Vitest](https://vitest.dev/) y los clientes de API tipados, puedes escribir pruebas de integración que verifiquen que tu aplicación funciona de extremo a extremo contra un servidor real de Twenty. + +## Uso de paquetes de npm + +Puedes instalar y usar cualquier paquete de npm en tu aplicación. Tanto las funciones de lógica como los componentes de frontend se empaquetan con [esbuild](https://esbuild.github.io/), que incorpora todas las dependencias en la salida — no se necesitan `node_modules` en tiempo de ejecución. + +### Instalar un paquete + +```bash filename="Terminal" +yarn add axios +``` + +Luego impórtalo en tu código: + +```ts src/logic-functions/fetch-data.ts +import { defineLogicFunction } from 'twenty-sdk/define'; +import axios from 'axios'; + +const handler = async (): Promise => { + const { data } = await axios.get('https://api.example.com/data'); + + return { data }; +}; + +export default defineLogicFunction({ + universalIdentifier: '...', + name: 'fetch-data', + description: 'Fetches data from an external API', + timeoutSeconds: 10, + handler, +}); +``` + +Lo mismo funciona para los componentes de frontend: + +```tsx src/front-components/chart.tsx +import { defineFrontComponent } from 'twenty-sdk/define'; +import { format } from 'date-fns'; + +const DateWidget = () => { + return

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

; +}; + +export default defineFrontComponent({ + universalIdentifier: '...', + name: 'date-widget', + component: DateWidget, +}); +``` + +### Cómo funciona el empaquetado + +El paso de compilación usa esbuild para producir un solo archivo autónomo por función de lógica y por componente de frontend. Todos los paquetes importados se insertan en el bundle. + +**Las funciones de lógica** se ejecutan en un entorno Node.js. Los módulos integrados de Node (`fs`, `path`, `crypto`, `http`, etc.) están disponibles y no necesitan instalarse. + +**Los componentes de frontend** se ejecutan en un Web Worker. Los módulos integrados de Node **no** están disponibles — solo las APIs del navegador y paquetes de npm que funcionen en un entorno de navegador. + +Ambos entornos tienen `twenty-client-sdk/core` y `twenty-client-sdk/metadata` disponibles como módulos preproporcionados — estos no se incluyen en el bundle sino que se resuelven en tiempo de ejecución por el servidor. + +## Configuración + +La aplicación generada ya incluye Vitest. Si lo configuras manualmente, instala las dependencias: + +```bash filename="Terminal" +yarn add -D vitest vite-tsconfig-paths +``` + +Crea un `vitest.config.ts` en la raíz de tu aplicación: + +```ts vitest.config.ts +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + tsconfigPaths({ + projects: ['tsconfig.spec.json'], + ignoreConfigErrors: true, + }), + ], + test: { + testTimeout: 120_000, + hookTimeout: 120_000, + include: ['src/**/*.integration-test.ts'], + setupFiles: ['src/__tests__/setup-test.ts'], + env: { + TWENTY_API_URL: 'http://localhost:2020', + TWENTY_API_KEY: 'your-api-key', + }, + }, +}); +``` + +Crea un archivo de configuración que verifique que el servidor es accesible antes de ejecutar las pruebas: + +```ts src/__tests__/setup-test.ts +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { beforeAll } from 'vitest'; + +const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020'; +const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test'); + +beforeAll(async () => { + // Verify the server is running + const response = await fetch(`${TWENTY_API_URL}/healthz`); + + if (!response.ok) { + throw new Error( + `Twenty server is not reachable at ${TWENTY_API_URL}. ` + + 'Start the server before running integration tests.', + ); + } + + // Write a temporary config for the SDK + fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true }); + + fs.writeFileSync( + path.join(TEST_CONFIG_DIR, 'config.json'), + JSON.stringify({ + remotes: { + local: { + apiUrl: process.env.TWENTY_API_URL, + apiKey: process.env.TWENTY_API_KEY, + }, + }, + defaultRemote: 'local', + }, null, 2), + ); +}); +``` + +## APIs programáticas del SDK + +La subruta `twenty-sdk/cli` exporta funciones que puedes invocar directamente desde el código de pruebas: + +| Función | Descripción | +| -------------- | ------------------------------------------------------------ | +| `appBuild` | Compilar la aplicación y opcionalmente empaquetar un tarball | +| `appDeploy` | Subir un tarball al servidor | +| `appInstall` | Instalar la aplicación en el espacio de trabajo activo | +| `appUninstall` | Desinstalar la aplicación del espacio de trabajo activo | + +Cada función devuelve un objeto de resultado con `success: boolean` y `data` o `error`. + +## Escribir una prueba de integración + +Aquí tienes un ejemplo completo que compila, despliega e instala la aplicación, y luego verifica que aparezca en el espacio de trabajo: + +```ts src/__tests__/app-install.integration-test.ts +import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config'; +import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli'; +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +const APP_PATH = process.cwd(); + +describe('App installation', () => { + beforeAll(async () => { + const buildResult = await appBuild({ + appPath: APP_PATH, + tarball: true, + onProgress: (message: string) => console.log(`[build] ${message}`), + }); + + if (!buildResult.success) { + throw new Error(`Build failed: ${buildResult.error?.message}`); + } + + const deployResult = await appDeploy({ + tarballPath: buildResult.data.tarballPath!, + onProgress: (message: string) => console.log(`[deploy] ${message}`), + }); + + if (!deployResult.success) { + throw new Error(`Deploy failed: ${deployResult.error?.message}`); + } + + const installResult = await appInstall({ appPath: APP_PATH }); + + if (!installResult.success) { + throw new Error(`Install failed: ${installResult.error?.message}`); + } + }); + + afterAll(async () => { + await appUninstall({ appPath: APP_PATH }); + }); + + it('should find the installed app in the workspace', async () => { + const metadataClient = new MetadataApiClient(); + + const result = await metadataClient.query({ + findManyApplications: { + id: true, + name: true, + universalIdentifier: true, + }, + }); + + const installedApp = result.findManyApplications.find( + (app: { universalIdentifier: string }) => + app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER, + ); + + expect(installedApp).toBeDefined(); + }); +}); +``` + +## Ejecutar pruebas + +Asegúrate de que tu servidor local de Twenty esté en ejecución y luego: + +```bash filename="Terminal" +yarn test +``` + +O en modo watch durante el desarrollo: + +```bash filename="Terminal" +yarn test:watch +``` + +## Comprobación de tipos + +También puedes ejecutar la comprobación de tipos en tu aplicación sin ejecutar pruebas: + +```bash filename="Terminal" +yarn twenty dev:typecheck +``` + +Esto ejecuta `tsc --noEmit` e informa cualquier error de tipo. + +## CI con GitHub Actions + +El generador crea un flujo de trabajo de GitHub Actions listo para usar en `.github/workflows/ci.yml`. Ejecuta tus pruebas de integración automáticamente en cada push a `main` y en los pull requests. + +El flujo de trabajo: + +1. Obtiene tu código +2. Inicia un servidor temporal de Twenty usando la acción `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` +3. Instala las dependencias con `yarn install --immutable` +4. Ejecuta `yarn test` con `TWENTY_API_URL` y `TWENTY_API_KEY` inyectados a partir de las salidas de la acción + +```yaml .github/workflows/ci.yml +name: CI + +on: + push: + branches: + - main + pull_request: {} + +env: + TWENTY_VERSION: latest + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Spawn Twenty instance + id: twenty + uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main + with: + twenty-version: ${{ env.TWENTY_VERSION }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable Corepack + run: corepack enable + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + + - name: Run integration tests + run: yarn test + env: + TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} + TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }} +``` + +No necesitas configurar secretos: la acción `spawn-twenty-docker-image` inicia un servidor efímero de Twenty directamente en el runner y devuelve los detalles de conexión. El secreto `GITHUB_TOKEN` lo proporciona GitHub automáticamente. + +Para fijar una versión específica de Twenty en lugar de `latest`, cambia la variable de entorno `TWENTY_VERSION` al inicio del flujo de trabajo. diff --git a/packages/twenty-docs/l/es/developers/extend/capabilities/apis.mdx b/packages/twenty-docs/l/es/developers/extend/capabilities/apis.mdx index 65f08139df..5f4f2a53dc 100644 --- a/packages/twenty-docs/l/es/developers/extend/capabilities/apis.mdx +++ b/packages/twenty-docs/l/es/developers/extend/capabilities/apis.mdx @@ -17,7 +17,7 @@ Twenty genera APIs específicamente para tu modelo de datos: * **Documentación personalizada**: Generada específicamente para el modelo de datos de tu espacio de trabajo. - Tu documentación personalizada de la API está disponible en **Configuración → API & Webhooks** después de crear una clave de API. Como Twenty genera APIs que coinciden con tu modelo de datos personalizado, la documentación es única para tu espacio de trabajo. +Tu documentación personalizada de la API está disponible en **Configuración → API & Webhooks** después de crear una clave de API. Como Twenty genera APIs que coinciden con tu modelo de datos personalizado, la documentación es única para tu espacio de trabajo. ## Los dos tipos de API @@ -81,14 +81,14 @@ Authorization: Bearer YOUR_API_KEY - Tu clave de API concede acceso a datos sensibles. No la compartas con servicios no confiables. Si se ve comprometida, desactívala de inmediato y genera una nueva. +Tu clave de API concede acceso a datos sensibles. No la compartas con servicios no confiables. Si se ve comprometida, desactívala de inmediato y genera una nueva. ### Asignar un rol a una clave de API Para mayor seguridad, asigna un rol específico para limitar el acceso: -1. Ve a **Configuración → Roles** +1. Ve a **Ajustes → Miembros → Roles** 2. Haz clic en el rol que deseas asignar 3. Abre la pestaña **Asignación** 4. En **Claves de API**, haz clic en **+ Asignar a clave de API** @@ -143,5 +143,5 @@ Las solicitudes a la API se limitan para garantizar la estabilidad de la platafo | **Tamaño del lote** | 60 registros por llamada | - Usa operaciones por lotes para maximizar el rendimiento — procesa hasta 60 registros en una sola llamada a la API en lugar de hacer solicitudes individuales. +Usa operaciones por lotes para maximizar el rendimiento — procesa hasta 60 registros en una sola llamada a la API en lugar de hacer solicitudes individuales. diff --git a/packages/twenty-docs/l/es/developers/extend/capabilities/webhooks.mdx b/packages/twenty-docs/l/es/developers/extend/capabilities/webhooks.mdx index aa938bf322..b4e66c4619 100644 --- a/packages/twenty-docs/l/es/developers/extend/capabilities/webhooks.mdx +++ b/packages/twenty-docs/l/es/developers/extend/capabilities/webhooks.mdx @@ -62,7 +62,7 @@ Cada webhook envía una solicitud HTTP POST con un cuerpo JSON: | `marca de tiempo` | Cuándo ocurrió el evento (UTC) | - Responde con un **estado HTTP 2xx** (200-299) para confirmar la recepción. Las respuestas que no sean 2xx se registran como errores de entrega. +Responde con un **estado HTTP 2xx** (200-299) para confirmar la recepción. Las respuestas que no sean 2xx se registran como errores de entrega. ## Validación de Webhook diff --git a/packages/twenty-docs/l/es/developers/extend/extend.mdx b/packages/twenty-docs/l/es/developers/extend/extend.mdx index f6445743ec..deed9b0989 100644 --- a/packages/twenty-docs/l/es/developers/extend/extend.mdx +++ b/packages/twenty-docs/l/es/developers/extend/extend.mdx @@ -1,7 +1,6 @@ --- title: Ampliar description: Amplía la funcionalidad de Twenty con APIs, webhooks y aplicaciones personalizadas. -redirect: /developers/introduction --- @@ -16,20 +15,18 @@ Twenty está diseñado para ser extensible. Usa nuestras APIs, webhooks y el fra * **APIs**: Consulta y modifica tus datos de CRM de forma programática usando REST o GraphQL * **Webhooks**: Recibe notificaciones en tiempo real cuando ocurran eventos en Twenty -* **Aplicaciones**: Crea aplicaciones personalizadas que amplíen las capacidades de Twenty - Próximamente. +* **Aplicaciones**: Crea aplicaciones personalizadas que amplíen las capacidades de Twenty ## Primeros pasos - + Conéctate a Twenty de forma programática - - + Recibe notificaciones de eventos en tiempo real - - - Crea personalizaciones como código (Alpha) + + Crea personalizaciones como código diff --git a/packages/twenty-docs/l/es/developers/extend/oauth.mdx b/packages/twenty-docs/l/es/developers/extend/oauth.mdx new file mode 100644 index 0000000000..94a8fd8d3b --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/oauth.mdx @@ -0,0 +1,189 @@ +--- +title: OAuth +icon: clave +description: Flujo de código de autorización con PKCE y credenciales de cliente para acceso de servidor a servidor. +--- + +Twenty implementa OAuth 2.0 con código de autorización + PKCE para aplicaciones orientadas al usuario y credenciales de cliente para acceso de servidor a servidor. Los clientes se registran dinámicamente mediante [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) — sin configuración manual en un panel. + +## Cuándo usar OAuth + +| Escenario | Método de autenticación | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| Scripts internos, automatización | [Clave de API](/l/es/developers/extend/api#authentication) | +| Aplicación externa que actúa en nombre de un usuario | **OAuth — Código de autorización** | +| De servidor a servidor, sin contexto de usuario | **OAuth — Credenciales de cliente** | +| Aplicación de Twenty con extensiones de UI | [Aplicaciones](/l/es/developers/extend/apps/getting-started) (OAuth se gestiona automáticamente) | + +## Registrar un cliente + +Twenty admite el **registro dinámico de clientes** según [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). No se necesita configuración manual — regístrelo de forma programática: + +```bash +POST /oauth/register +Content-Type: application/json + +{ + "client_name": "My Integration", + "redirect_uris": ["https://myapp.com/callback"], + "grant_types": ["authorization_code"], + "token_endpoint_auth_method": "client_secret_post" +} +``` + +**Respuesta:** + +```json +{ + "client_id": "abc123", + "client_secret": "secret456", + "client_name": "My Integration", + "redirect_uris": ["https://myapp.com/callback"] +} +``` + + +Almacene el `client_secret` de forma segura — no se puede recuperar más tarde. + + +## Ámbitos + +| Ámbito | Acceso | +| --------- | ----------------------------------------------------------------- | +| `api` | Acceso completo de lectura/escritura a las API de Core y Metadata | +| `profile` | Leer la información de perfil del usuario autenticado | + +Solicite los ámbitos como una cadena separada por espacios: `scope=api profile` + +## Flujo de código de autorización + +Use este flujo cuando su aplicación actúe en nombre de un usuario de Twenty. + +### 1. Redirija al usuario para autorizar + +``` +GET /oauth/authorize? + client_id=YOUR_CLIENT_ID& + response_type=code& + redirect_uri=https://myapp.com/callback& + scope=api& + state=random_state_value& + code_challenge=CHALLENGE& + code_challenge_method=S256 +``` + +| Parámetro | Obligatorio | Descripción | +| ----------------------- | ----------- | -------------------------------------------------------------------- | +| `client_id` | Sí | Su ID de cliente registrado | +| `response_type` | Sí | Debe ser `code` | +| `redirect_uri` | Sí | Debe coincidir con un URI de redirección registrado | +| `scope` | No | Ámbitos separados por espacios (valor predeterminado: `api`) | +| `state` | Recomendado | Cadena aleatoria para evitar ataques CSRF | +| `code_challenge` | Recomendado | Desafío PKCE (hash SHA-256 del verificador, codificado en base64url) | +| `code_challenge_method` | Recomendado | Debe ser `S256` al usar PKCE | + +El usuario ve una pantalla de consentimiento y aprueba o deniega el acceso. + +### 2. Gestione el callback + +Tras la autorización, Twenty redirige de vuelta a su `redirect_uri`: + +``` +https://myapp.com/callback?code=AUTH_CODE&state=random_state_value +``` + +Verifique que `state` coincida con lo que envió. + +### 3. Intercambie el código por tokens + +```bash +POST /oauth/token +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code& +code=AUTH_CODE& +redirect_uri=https://myapp.com/callback& +client_id=YOUR_CLIENT_ID& +client_secret=YOUR_CLIENT_SECRET& +code_verifier=YOUR_PKCE_VERIFIER +``` + +**Respuesta:** + +```json +{ + "access_token": "eyJhbG...", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "dGhpcyBpcyBh..." +} +``` + +### 4. Use el token de acceso + +```bash +GET /rest/companies +Authorization: Bearer ACCESS_TOKEN +``` + +### 5. Actualice cuando caduque + +```bash +POST /oauth/token +Content-Type: application/x-www-form-urlencoded + +grant_type=refresh_token& +refresh_token=YOUR_REFRESH_TOKEN& +client_id=YOUR_CLIENT_ID& +client_secret=YOUR_CLIENT_SECRET +``` + +## Flujo de credenciales de cliente + +Para integraciones de servidor a servidor sin interacción del usuario: + +```bash +POST /oauth/token +Content-Type: application/x-www-form-urlencoded + +grant_type=client_credentials& +client_id=YOUR_CLIENT_ID& +client_secret=YOUR_CLIENT_SECRET& +scope=api +``` + +El token devuelto tiene acceso a nivel de espacio de trabajo, no está vinculado a ningún usuario específico. + +## Descubrimiento del servidor + +Twenty publica su configuración de OAuth en un endpoint de descubrimiento estándar: + +``` +GET /.well-known/oauth-authorization-server +``` + +Esto devuelve todos los endpoints, los tipos de concesión compatibles, los ámbitos y las capacidades — útil para crear clientes OAuth genéricos. + +## Resumen de Puntos de Acceso de API + +| Endpoint | Propósito | +| ----------------------------------------- | ---------------------------------------- | +| `/.well-known/oauth-authorization-server` | Descubrimiento de metadatos del servidor | +| `/oauth/register` | Registro dinámico de clientes | +| `/oauth/authorize` | Autorización del usuario | +| `/oauth/token` | Intercambio y renovación de tokens | + +| Entorno | URL base | +| ------------------- | ------------------------ | +| **Nube** | `https://api.twenty.com` | +| **Autoalojamiento** | `https://{your-domain}` | + +## OAuth frente a claves de API + +| | Claves API | OAuth | +| ------------------------ | --------------------------------------- | ------------------------------------------------- | +| **Configuración** | Generar en Ajustes | Registrar un cliente, implementar el flujo | +| **Contexto del usuario** | Ninguno (a nivel de espacio de trabajo) | Permisos de un usuario específico | +| **Ideal para** | Scripts, herramientas internas | Aplicaciones externas, integraciones multiusuario | +| **Rotación de tokens** | Manual | Automática mediante tokens de actualización | +| **Acceso con ámbitos** | Acceso completo a la API | Granular mediante ámbitos | diff --git a/packages/twenty-docs/l/es/developers/extend/webhooks.mdx b/packages/twenty-docs/l/es/developers/extend/webhooks.mdx new file mode 100644 index 0000000000..7126f9a790 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/extend/webhooks.mdx @@ -0,0 +1,117 @@ +--- +title: Webhooks +icon: satellite-dish +description: Recibe notificaciones cuando los registros cambien — HTTP POST a tu endpoint en cada creación, actualización o eliminación. +--- + +import { VimeoEmbed } from '/snippets/vimeo-embed.mdx'; + +Twenty envía un HTTP POST a tu URL cada vez que se crea, actualiza o elimina un registro. Todos los tipos de objetos están cubiertos, incluidos los objetos personalizados. + +## Crear un webhook + +1. Ve a **Configuración → APIs y Webhooks → Webhooks** +2. Haz clic en **+ Crear webhook** +3. Introduce la URL de tu webhook (debe ser públicamente accesible) +4. Haz clic en **Guardar** + +El webhook se activa de inmediato y comienza a enviar notificaciones. + + + +### Gestionar webhooks + +**Editar**: Haz clic en el webhook → Actualizar la URL → **Guardar** + +**Eliminar**: Haz clic en el webhook → **Eliminar** → Confirmar + +## Eventos + +Twenty envía webhooks para estos tipos de eventos: + +| Evento | Ejemplo | +| ---------------------------- | ---------------------------------------------------------- | +| **Se crea un registro** | `person.created`, `company.created`, `note.created` | +| **Se actualiza un registro** | `person.updated`, `company.updated`, `opportunity.updated` | +| **Se elimina un registro** | `person.deleted`, `company.deleted` | + +Todos los tipos de eventos se envían a la URL de tu webhook. Es posible que se agregue el filtrado de eventos en versiones futuras. + +## Formato de la carga útil + +Cada webhook envía una solicitud HTTP POST con un cuerpo JSON: + +```json +{ + "event": "person.created", + "data": { + "id": "abc12345", + "firstName": "Alice", + "lastName": "Doe", + "email": "alice@example.com", + "createdAt": "2025-02-10T15:30:45Z", + "createdBy": "user_123" + }, + "timestamp": "2025-02-10T15:30:50Z" +} +``` + +| Campo | Descripción | +| ----------- | -------------------------------------------------- | +| `event` | Qué ocurrió (p. ej., `person.created`) | +| `data` | El registro completo que se creó/actualizó/eliminó | +| `timestamp` | Cuándo ocurrió el evento (UTC) | + + +Responde con un **estado HTTP 2xx** (200-299) para confirmar la recepción. Las respuestas que no sean 2xx se registran como errores de entrega. + + +## Validación de webhook + +Twenty firma cada solicitud de webhook por seguridad. Valida las firmas para garantizar que las solicitudes sean auténticas. + +### Encabezados + +| Encabezado | Descripción | +| ---------------------------- | ------------------------------- | +| `X-Twenty-Webhook-Signature` | Firma HMAC SHA256 | +| `X-Twenty-Webhook-Timestamp` | Marca de tiempo de la solicitud | + +### Pasos de validación + +1. Obtén la marca de tiempo de `X-Twenty-Webhook-Timestamp` +2. Crea la cadena: `{timestamp}:{JSON payload}` +3. Calcula HMAC SHA256 usando tu secreto de webhook +4. Compara con `X-Twenty-Webhook-Signature` + +### Ejemplo (Node.js) + +```javascript +const crypto = require("crypto"); + +const timestamp = req.headers["x-twenty-webhook-timestamp"]; +const payload = JSON.stringify(req.body); +const secret = "your-webhook-secret"; + +const stringToSign = `${timestamp}:${payload}`; +const expectedSignature = crypto + .createHmac("sha256", secret) + .update(stringToSign) + .digest("hex"); + +const receivedSignature = req.headers["x-twenty-webhook-signature"]; +const isValid = crypto.timingSafeEqual( + Buffer.from(expectedSignature, "hex"), + Buffer.from(receivedSignature, "hex") +); +``` + +## Webhooks vs flujos de trabajo + +| Método | Dirección | Caso de uso | +| --------------------------------------------- | --------- | --------------------------------------------------------------------------------- | +| **Webhooks** | SALIDA | Notificar automáticamente a los sistemas externos cualquier cambio en un registro | +| **Flujo de trabajo + solicitud HTTP** | SALIDA | Enviar datos con lógica personalizada (filtros, transformaciones) | +| **Disparador de webhook de flujo de trabajo** | ENTRADA | Recibir datos en Twenty desde sistemas externos | + +Para recibir datos externos, consulta [Configurar un disparador de webhook](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger). diff --git a/packages/twenty-docs/l/es/developers/introduction.mdx b/packages/twenty-docs/l/es/developers/introduction.mdx index 9cfad10e09..6374b1ef3a 100644 --- a/packages/twenty-docs/l/es/developers/introduction.mdx +++ b/packages/twenty-docs/l/es/developers/introduction.mdx @@ -1,33 +1,28 @@ --- -title: Primeros pasos -description: Bienvenido a la documentación para desarrolladores de Twenty, tus recursos para ampliar, autoalojar y contribuir a Twenty. +title: Desarrolladores +description: Crea aplicaciones, usa la API, aloja por tu cuenta o contribuye a la base de código. --- import { CardTitle } from "/snippets/card-title.mdx" - - + + + Aplicaciones + Amplía Twenty con objetos personalizados, lógica del lado del servidor, componentes de IU y agentes de IA — todo como paquetes de TypeScript. + + + API - Consulta y modifica los datos de tu CRM con REST o GraphQL. + APIs REST y GraphQL, ganchos web y OAuth. - - Webhooks - Recibe notificaciones en tiempo real cuando ocurran eventos. - - - - Apps - Crea aplicaciones personalizadas que amplíen las capacidades de Twenty. - - - + Autoalojar - Despliega y administra Twenty en tu propia infraestructura. + Ejecuta Twenty en tu propia infraestructura. - + Contribuir - Únete a nuestra comunidad de código abierto y contribuye a Twenty. + Configura el monorepo localmente y envía PRs. diff --git a/packages/twenty-docs/l/es/developers/self-host/capabilities/docker-compose.mdx b/packages/twenty-docs/l/es/developers/self-host/capabilities/docker-compose.mdx index 538160baad..204092cb16 100644 --- a/packages/twenty-docs/l/es/developers/self-host/capabilities/docker-compose.mdx +++ b/packages/twenty-docs/l/es/developers/self-host/capabilities/docker-compose.mdx @@ -1,9 +1,10 @@ --- -title: 1-Clic con Docker Compose +title: Docker Compose +icon: docker --- - Los contenedores de Docker son para alojamiento en producción o autoalojamiento, para la contribución por favor revise la [Configuración Local](/l/es/developers/contribute/capabilities/local-setup). +Los contenedores de Docker son para alojamiento en producción o autoalojamiento. Para contribuir, consulta la [Configuración local](/l/es/developers/contribute/capabilities/local-setup). ## Resumen @@ -12,7 +13,7 @@ Esta guía proporciona instrucciones paso a paso para instalar y configurar la a **Importante:** Solo modifica configuraciones explícitamente mencionadas en esta guía. Alterar otras configuraciones puede causar problemas. -Consulta los documentos [Configurar Variables de Entorno](/l/es/developers/self-host/capabilities/setup) para configuraciones avanzadas. Todas las variables de entorno deben ser declaradas en el archivo docker-compose.yml en el nivel del servidor y/o trabajador dependiendo de la variable. +Consulta [Configurar variables de entorno](/l/es/developers/self-host/capabilities/setup) para configuraciones avanzadas. Todas las variables de entorno deben declararse en el archivo `docker-compose.yml` a nivel de servidor y/o trabajador, dependiendo de la variable. ## Requisitos del sistema @@ -50,7 +51,7 @@ Sigue estos pasos para una configuración manual. curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example ``` -2. **Generar tokens secretos** +2. **Generar una clave de cifrado** Ejecuta el siguiente comando para generar una cadena única aleatoria: @@ -58,16 +59,18 @@ Sigue estos pasos para una configuración manual. openssl rand -base64 32 ``` - **Importante:** Mantén este valor en secreto / no lo compartas. + **Importante:** Mantén este valor en secreto / no lo compartas. Perder `ENCRYPTION_KEY` significa perder el acceso a todos los secretos almacenados en la base de datos (tokens OAuth, variables de la aplicación, secretos TOTP, etc.). 3. **Actualiza el `.env`** Reemplaza el valor de marcador de posición en tu archivo .env con el token generado: ```ini - APP_SECRET=first_random_string + ENCRYPTION_KEY=random_string ``` + Consulta la [guía de rotación de claves](/l/es/developers/self-host/capabilities/key-rotation) para obtener instrucciones sobre cómo rotarla sin tiempo de inactividad. + 4. **Establecer la contraseña de Postgres** Actualiza el valor de `PG_DATABASE_PASSWORD` en el archivo .env con una contraseña fuerte sin caracteres especiales. diff --git a/packages/twenty-docs/l/es/developers/self-host/capabilities/key-rotation.mdx b/packages/twenty-docs/l/es/developers/self-host/capabilities/key-rotation.mdx new file mode 100644 index 0000000000..4e0df52868 --- /dev/null +++ b/packages/twenty-docs/l/es/developers/self-host/capabilities/key-rotation.mdx @@ -0,0 +1,60 @@ +--- +title: Rotación de claves +icon: rotate +--- + +Twenty tiene dos familias de claves independientes: + +* **Claves de firma JWT** — pares de claves asimétricas ES256 (etiquetados con `kid`) almacenados en `core."signingKey"`, utilizados para firmar y verificar tokens de acceso/renovación. +* **Clave de cifrado en reposo** — `ENCRYPTION_KEY`, se usa para cifrar tokens OAuth, variables de la aplicación, claves privadas de las claves de firma, valores de configuración confidenciales y secretos TOTP dentro de un sobre `enc:v2:`. + +`APP_SECRET` es un secreto heredado mantenido para compatibilidad retroactiva: cuando `ENCRYPTION_KEY` no está definido actúa como el cifrado en reposo / alternativa para la cookie de sesión, y todavía verifica los tokens de acceso HS256 preexistentes. Quedará obsoleto. + +## Claves de firma JWT + +Cada clave contiene una `publicKey` (conservada indefinidamente para poder verificar los tokens emitidos anteriormente), una `privateKey` cifrada (usada solo mientras la clave sea la actual), un indicador `isCurrent` (exactamente una fila a la vez) y un `revokedAt` opcional. + +### Rotar la clave actual + +Establece `SIGNING_KEY_ROTATION_DAYS` para habilitarlo: un cron diario emite una nueva clave actual cuando la existente es más antigua que ese umbral. Las claves anteriores *no* se revocan, por lo que los tokens firmados con ellas siguen verificándose. Deja la variable sin configurar para desactivar la rotación automática. + +La rotación automática se incluye a partir de la versión v2.6+. + +### Revocar una clave (solo en caso de filtración / emergencia) + +**Settings → Admin Panel → Signing keys → Revoke** en una fila que no sea la actual. Borra el material privado cifrado, establece `revokedAt` y rechaza todos los tokens existentes firmados con ese `kid`. + +## Rotar `ENCRYPTION_KEY` + +El comando `secret-encryption:rotate` descrito a continuación se incluye a partir de la versión v2.6+. + +Cada valor cifrado se encapsula como `enc:v2:\:\`, donde `\` es un prefijo hexadecimal de 8 caracteres derivado de la clave en bruto. La rotación es en línea y reanudable. + +1. **Generar una nueva clave**: `openssl rand -base64 32`. + +2. **Configurar ambas claves en paralelo** en `.env`, luego reinicie: + ```ini + ENCRYPTION_KEY=NEW_VALUE + FALLBACK_ENCRYPTION_KEY=OLD_VALUE + ``` + Las nuevas escrituras usan la nueva clave, las filas existentes todavía se descifran mediante la clave de respaldo. + +3. **Volver a cifrar las filas existentes**: + + ```bash + docker exec -it {server_container} yarn command:prod secret-encryption:rotate + ``` + + El comando recorre seis sitios (`connected-account-tokens`, `application-variable`, `application-registration-variable`, `signing-key-private-keys`, `sensitive-config-storage`, `totp-secrets`). Un filtro SQL omite las filas que ya están en el nuevo `\`, por lo que el comando es idempotente: interrúmpalo y vuelva a ejecutarlo según sea necesario. Finaliza con código distinto de cero si falla alguna fila; vuelva a ejecutar para reintentar. + + | Opción | Descripción | + | ---------------------------------------- | ----------------------------------------------------------- | + | `-s, --site \` | Limitar a un solo sitio. | + | `-b, --batch-size \` | Filas por lote (predeterminado `200`, máximo `5000`). | + | `-d, --dry-run` | Descifrar + volver a cifrar en memoria, omitir el `UPDATE`. | + +4. **Elimine la clave de respaldo** una vez que `--dry-run` muestre cero filas restantes: quite `FALLBACK_ENCRYPTION_KEY` y reinicie. + +## Compatibilidad heredada con `APP_SECRET` + +Las instancias más antiguas que nunca configuraron `ENCRYPTION_KEY` usan `APP_SECRET` como la clave de cifrado en reposo (y como el secreto de la cookie de sesión, derivado de esta). Esta ruta se conserva por compatibilidad retroactiva, pero está **en desuso**: configure una `ENCRYPTION_KEY` dedicada y siga el procedimiento de rotación anterior para migrar y dejar de usarla. El propio `APP_SECRET` sigue utilizándose para verificar tokens de acceso HS256 heredados. diff --git a/packages/twenty-docs/l/es/developers/self-host/capabilities/setup.mdx b/packages/twenty-docs/l/es/developers/self-host/capabilities/setup.mdx index 20ca962fed..a768859841 100644 --- a/packages/twenty-docs/l/es/developers/self-host/capabilities/setup.mdx +++ b/packages/twenty-docs/l/es/developers/self-host/capabilities/setup.mdx @@ -1,11 +1,12 @@ --- title: Configuración +icon: engranaje --- # Gestión de Configuración - **¿Instalando por primera vez?** Siga la [guía de instalación de Docker Compose](/l/es/developers/self-host/capabilities/docker-compose) para ejecutar Twenty, luego regrese aquí para la configuración. +**¿Instalando por primera vez?** Siga la [guía de instalación de Docker Compose](/l/es/developers/self-host/capabilities/docker-compose) para ejecutar Twenty, luego regrese aquí para la configuración. Twenty ofrece **dos modos de configuración** para adaptarse a diferentes necesidades de implementación: @@ -26,7 +27,7 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # default 4. Los cambios se aplican inmediatamente (dentro de 15 segundos para implementaciones multicontenedor) - **Implementaciones Multicontenedor:** Al usar la configuración de base de datos (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`), tanto los contenedores del servidor como los de trabajo leen de la misma base de datos. Los cambios en el panel de administración afectan a ambos automáticamente, eliminando la necesidad de duplicar las variables de entorno entre contenedores (excepto para las variables de infraestructura). +**Implementaciones Multicontenedor:** Al usar la configuración de base de datos (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`), tanto los contenedores del servidor como los de trabajo leen de la misma base de datos. Los cambios en el panel de administración afectan a ambos automáticamente, eliminando la necesidad de duplicar las variables de entorno entre contenedores (excepto para las variables de infraestructura). **Qué se puede configurar a través del panel de administración:** @@ -41,12 +42,27 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # default ![Variables de Configuración del Panel de Administración](/images/user-guide/setup/admin-panel-config-variables.png) - Cada variable está documentada con descripciones en su panel de administración en **Configuración → Panel de Administración → Variables de Configuración**. - Algunas configuraciones de infraestructura como las conexiones de base de datos (`PG_DATABASE_URL`), URLs del servidor (`SERVER_URL`), y secretos de la aplicación (`APP_SECRET`) solo se pueden configurar a través del archivo `.env`. +Cada variable está documentada con descripciones en su panel de administración en **Configuración → Panel de Administración → Variables de Configuración**. +Algunas configuraciones de infraestructura como las conexiones de base de datos (`PG_DATABASE_URL`), URLs del servidor (`SERVER_URL`), y secretos (`ENCRYPTION_KEY`, `FALLBACK_ENCRYPTION_KEY`) solo se pueden configurar a través del archivo `.env`. - [Referencia técnica completa →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts) +[Referencia técnica completa →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts) +## Claves de cifrado + +Twenty utiliza dos claves de cifrado definidas únicamente mediante variables de entorno: + +| Variable | Propósito | Obligatorio | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `ENCRYPTION_KEY` | Clave principal utilizada para cifrar secretos en reposo (tokens OAuth, variables de la aplicación, claves privadas de firma, secretos TOTP, valores de configuración confidenciales). | Sí para nuevas instalaciones (las instalaciones heredadas pueden en su lugar depender de `APP_SECRET` — ver más abajo) | +| `FALLBACK_ENCRYPTION_KEY` | Clave solo para verificación. Se establece durante una rotación como la *anterior* `ENCRYPTION_KEY` para que las filas existentes sigan siendo descifrables. | Solo durante la rotación | + +Para mantener la compatibilidad con versiones anteriores, si `ENCRYPTION_KEY` no está definida, Twenty recurre a `APP_SECRET` para el cifrado en reposo, lo que coincide con el comportamiento heredado de implementaciones más antiguas. Las nuevas instalaciones siempre deben establecer una `ENCRYPTION_KEY` dedicada. + +Genera valores con `openssl rand -base64 32` y guárdalos en un lugar seguro (un gestor de secretos, configuración sellada, etc.). Perder la `ENCRYPTION_KEY` significa perder el acceso a todos los secretos almacenados en la base de datos. + +Para rotar `ENCRYPTION_KEY` sin tiempo de inactividad, consulta la [Guía de rotación de claves](/l/es/developers/self-host/capabilities/key-rotation). + ## 2. Configuración Solo de Entorno ```bash @@ -93,7 +109,7 @@ Habilita el modo de múltiples espacios de trabajo para implementaciones tipo Sa * Configuraciones específicas del espacio de trabajo, como subdominio y dominio personalizado, están disponibles en la configuración del espacio de trabajo - **Configuración solo por entorno:** `IS_MULTIWORKSPACE_ENABLED` solo se puede configurar mediante el archivo `.env` y requiere un reinicio. No se puede cambiar a través del panel de administración. +**Configuración solo por entorno:** `IS_MULTIWORKSPACE_ENABLED` solo se puede configurar mediante el archivo `.env` y requiere un reinicio. No se puede cambiar a través del panel de administración. ### Configuración de DNS para múltiples espacios de trabajo @@ -149,7 +165,7 @@ Cuando está habilitado, solo los usuarios con `canAccessFullAdminPanel` pueden * `AUTH_GOOGLE_APIS_CALLBACK_URL=https://{your-domain}/auth/google-apis/get-access-token` - **Modo solo de entorno:** Si establece `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, agregue estas variables a su archivo `.env` en su lugar. +**Modo solo de entorno:** Si establece `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, agregue estas variables a su archivo `.env` en su lugar. **Ámbitos requeridos** (configurados automáticamente): @@ -168,7 +184,7 @@ En [Pantalla de consentimiento OAuth](https://console.cloud.google.com/apis/cred ## Integración con Microsoft 365 - Los usuarios deben tener una [licencia de Microsoft 365](https://admin.microsoft.com/Adminportal/Home) para poder usar la API de Calendar y Messaging. No podrán sincronizar su cuenta en Twenty sin una. +Los usuarios deben tener una [licencia de Microsoft 365](https://admin.microsoft.com/Adminportal/Home) para poder usar la API de Calendar y Messaging. No podrán sincronizar su cuenta en Twenty sin una. ### Cree un proyecto en Microsoft Azure @@ -211,7 +227,7 @@ Necesita agregar las siguientes URIs de redirección a su proyecto: * `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://{your-domain}/auth/microsoft-apis/get-access-token` - **Modo solo de entorno:** Si establece `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, agregue estas variables a su archivo `.env` en su lugar. +**Modo solo de entorno:** Si establece `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, agregue estas variables a su archivo `.env` en su lugar. ### Configurar ámbitos @@ -256,82 +272,111 @@ yarn command:prod cron:workflow:automated-cron-trigger 3. Configure su configuración SMTP: - - Necesitará proporcionar una [Contraseña de Aplicación](https://support.google.com/accounts/answer/185833). + + + Necesitará proporcionar una [Contraseña de Aplicación](https://support.google.com/accounts/answer/185833). + * EMAIL_DRIVER=smtp + * EMAIL_SMTP_HOST=smtp.gmail.com + * EMAIL_SMTP_PORT=465 + * EMAIL_SMTP_USER=gmail_email_address + * EMAIL_SMTP_PASSWORD='gmail_app_password' + + + + + + Tenga en cuenta que si tiene la autenticación de dos factores habilitada, necesitará proporcionar una [Contraseña de Aplicación](https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9). + * EMAIL_DRIVER=smtp + * EMAIL_SMTP_HOST=smtp.office365.com + * EMAIL_SMTP_PORT=587 + * EMAIL_SMTP_USER=office365_email_address + * EMAIL_SMTP_PASSWORD='office365_password' + + + + + + **smtp4dev** es un servidor de correo SMTP falso para desarrollo y pruebas. + * Ejecute la imagen de smtp4dev: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev` + * Acceda a la interfaz de smtp4dev aquí: [http://localhost:8090](http://localhost:8090) + * Establezca las siguientes variables: * EMAIL_DRIVER=smtp - * EMAIL_SMTP_HOST=smtp.gmail.com - * EMAIL_SMTP_PORT=465 - * EMAIL_SMTP_USER=gmail_email_address - * EMAIL_SMTP_PASSWORD='gmail_app_password' + * EMAIL_SMTP_HOST=localhost + * EMAIL_SMTP_PORT=2525 + - - Tenga en cuenta que si tiene la autenticación de dos factores habilitada, necesitará proporcionar una [Contraseña de Aplicación](https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9). - - * EMAIL_DRIVER=smtp - * EMAIL_SMTP_HOST=smtp.office365.com - * EMAIL_SMTP_PORT=587 - * EMAIL_SMTP_USER=office365_email_address - * EMAIL_SMTP_PASSWORD='office365_password' - - - - **smtp4dev** es un servidor de correo SMTP falso para desarrollo y pruebas. - - * Ejecute la imagen de smtp4dev: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev` - * Acceda a la interfaz de smtp4dev aquí: [http://localhost:8090](http://localhost:8090) - * Establezca las siguientes variables: - * EMAIL_DRIVER=smtp - * EMAIL_SMTP_HOST=localhost - * EMAIL_SMTP_PORT=2525 - - **Modo solo de entorno:** Si establece `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, agregue estas variables a su archivo `.env` en su lugar. +**Modo solo de entorno:** Si establece `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, agregue estas variables a su archivo `.env` en su lugar. -## Funciones de lógica - -Twenty admite funciones de lógica para flujos de trabajo y lógica personalizada. El entorno de ejecución se configura mediante la variable de entorno `SERVERLESS_TYPE`. +## Almacenamiento de S3 - **Aviso de seguridad:** El controlador local (`SERVERLESS_TYPE=LOCAL`) ejecuta código directamente en el host en un proceso de Node.js sin aislamiento. Solo debe utilizarse para código de confianza en desarrollo. Para implementaciones de producción que manejen código no confiable, recomendamos encarecidamente usar `SERVERLESS_TYPE=LAMBDA` o `SERVERLESS_TYPE=DISABLED`. +De forma predeterminada, Twenty almacena los archivos cargados en el sistema de archivos local. Para implementaciones en producción, usa S3 o un servicio compatible con S3 (MinIO, DigitalOcean Spaces, etc.). para garantizar que los archivos persistan tras los reinicios de los contenedores y escalen en varias instancias de servidor. -### Controladores disponibles +Establece `STORAGE_TYPE=S_3` y configura las variables `STORAGE_S3_*` a través del panel de administración o `.env`. Consulta la [referencia de config-variables.ts](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts) para ver la lista completa de variables de S3. -| Controlador | Variable de entorno | Caso de uso | Nivel de seguridad | -| ----------- | -------------------------- | ------------------------------------------------ | -------------------------------------- | -| Desactivado | `SERVERLESS_TYPE=DISABLED` | Desactivar completamente las funciones de lógica | N/A | -| Local | `SERVERLESS_TYPE=LOCAL` | Entornos de desarrollo y de confianza | Bajo (sin aislamiento) | -| Lambda | `SERVERLESS_TYPE=LAMBDA` | Producción con código no confiable | Alto (aislamiento a nivel de hardware) | +Al usar S3 con funciones que dependen de CORS (p. ej., descargas de archivos en el navegador), asegúrate de que tu bucket permita el origen de tu frontend de Twenty en su configuración de CORS. -### Configuración recomendada +## Funciones de lógica e intérprete de código + +Twenty admite funciones de lógica para flujos de trabajo y el intérprete de código para el análisis de datos con IA. Ambos ejecutan código proporcionado por el usuario y requieren una configuración explícita por motivos de seguridad. + +### Valores Predeterminados de Seguridad + +**En producción (NODE_ENV=production):** Tanto las funciones de lógica como el intérprete de código tienen como valor predeterminado **Desactivado**. Debes habilitarlos explícitamente con `LOGIC_FUNCTION_TYPE` y `CODE_INTERPRETER_TYPE` si necesitas estas funciones. + +**En desarrollo (NODE_ENV=development):** Ambos usan **LOCAL** de forma predeterminada para mayor comodidad al ejecutarse localmente. + + +**Aviso de seguridad:** El controlador local (`LOGIC_FUNCTION_TYPE=LOCAL` o `CODE_INTERPRETER_TYPE=LOCAL`) ejecuta código directamente en el host en un proceso de Node.js sin aislamiento. Solo debe utilizarse para código de confianza en desarrollo. Para implementaciones de producción que manejen código no confiable, use `LOGIC_FUNCTION_TYPE=LAMBDA` o `CODE_INTERPRETER_TYPE=E2B` (con sandboxing), o manténgalos deshabilitados. + + +### Funciones de lógica - Controladores disponibles + +| Controlador | Variable de entorno | Caso de uso | Nivel de seguridad | +| ----------- | ------------------------------ | ------------------------------------------------ | -------------------------------------- | +| Desactivado | `LOGIC_FUNCTION_TYPE=DISABLED` | Desactivar completamente las funciones de lógica | N/A | +| Local | `LOGIC_FUNCTION_TYPE=LOCAL` | Entornos de desarrollo y de confianza | Bajo (sin aislamiento) | +| Lambda | `LOGIC_FUNCTION_TYPE=LAMBDA` | Producción con código no confiable | Alto (aislamiento a nivel de hardware) | + +### Funciones de lógica - Configuración recomendada **Para desarrollo:** ```bash -SERVERLESS_TYPE=LOCAL # default +LOGIC_FUNCTION_TYPE=LOCAL # default when NODE_ENV=development ``` **Para producción (AWS):** ```bash -SERVERLESS_TYPE=LAMBDA -SERVERLESS_LAMBDA_REGION=us-east-1 -SERVERLESS_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role -SERVERLESS_LAMBDA_ACCESS_KEY_ID=your-access-key -SERVERLESS_LAMBDA_SECRET_ACCESS_KEY=your-secret-key +LOGIC_FUNCTION_TYPE=LAMBDA +LOGIC_FUNCTION_LAMBDA_REGION=us-east-1 +LOGIC_FUNCTION_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role +LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID=your-access-key +LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY=your-secret-key ``` **Para desactivar las funciones de lógica:** ```bash -SERVERLESS_TYPE=DISABLED +LOGIC_FUNCTION_TYPE=DISABLED # default when NODE_ENV=production ``` +### Intérprete de código - Controladores disponibles + +| Controlador | Variable de entorno | Caso de uso | Nivel de seguridad | +| ----------- | -------------------------------- | ----------------------------------------- | ---------------------- | +| Desactivado | `CODE_INTERPRETER_TYPE=DISABLED` | Deshabilitar la ejecución de código de IA | N/A | +| Local | `CODE_INTERPRETER_TYPE=LOCAL` | Solo para desarrollo | Bajo (sin aislamiento) | +| E2B | `CODE_INTERPRETER_TYPE=E_2_B` | Producción con ejecución en sandbox | Alta (sandbox aislado) | + - Al usar `SERVERLESS_TYPE=DISABLED`, cualquier intento de ejecutar una función de lógica devolverá un error. Esto es útil si desea ejecutar Twenty sin capacidades de funciones de lógica. +Al usar `LOGIC_FUNCTION_TYPE=DISABLED` o `CODE_INTERPRETER_TYPE=DISABLED`, cualquier intento de ejecución devolverá un error. Esto es útil si desea ejecutar Twenty sin estas capacidades. diff --git a/packages/twenty-docs/l/es/developers/self-host/capabilities/troubleshooting.mdx b/packages/twenty-docs/l/es/developers/self-host/capabilities/troubleshooting.mdx index fad4c8c15b..6308faf59b 100644 --- a/packages/twenty-docs/l/es/developers/self-host/capabilities/troubleshooting.mdx +++ b/packages/twenty-docs/l/es/developers/self-host/capabilities/troubleshooting.mdx @@ -1,5 +1,6 @@ --- title: Solución de problemas +icon: llave inglesa --- ## Solución de Problemas @@ -53,7 +54,7 @@ Asegúrese de ejecutar yarn en el directorio raíz y luego ejecute `npx nx serve #### Lint on Save no funciona -Esto debería funcionar sin configuración adicional con la extensión de Oxlint instalada. Si esto no funciona, intente agregar esto a su configuración de vscode (en el ámbito del contenedor de desarrollo): +Esto debería funcionar sin configuración adicional con la extensión de Oxc (`oxc.oxc-vscode`) instalada. Si esto no funciona, intente agregar esto a su configuración de vscode (en el ámbito del contenedor de desarrollo): ``` "editor.codeActionsOnSave": { @@ -166,6 +167,10 @@ plugins: [ Ejecute `UPDATE core."user" SET "canAccessFullAdminPanel" = TRUE WHERE email = 'you@yourdomain.com';` en el contenedor de la base de datos para obtener acceso al panel de administración. +#### Al ejecutar un flujo de trabajo, la ejecución del flujo de trabajo falla con "La ejecución de la función lógica está deshabilitada. Configura LOGIC_FUNCTION_TYPE en LOCAL o LAMBDA para habilitarlas." + +En producción, las funciones lógicas están deshabilitadas de forma predeterminada. Configura la variable de entorno `LOGIC_FUNCTION_TYPE` en `LOCAL` o `LAMBDA` para habilitarlas. Esto se puede configurar mediante variables de entorno o a través de las variables de base de datos del panel de administración. Consulta la [guía de configuración de funciones lógicas](/l/es/developers/self-host/capabilities/setup#logic-functions-available-drivers) para obtener más detalles. + ### 1-clic con Docker Compose #### No se puede iniciar sesión diff --git a/packages/twenty-docs/l/es/developers/self-host/capabilities/upgrade-guide.mdx b/packages/twenty-docs/l/es/developers/self-host/capabilities/upgrade-guide.mdx index ec90dff260..3841aeb83f 100644 --- a/packages/twenty-docs/l/es/developers/self-host/capabilities/upgrade-guide.mdx +++ b/packages/twenty-docs/l/es/developers/self-host/capabilities/upgrade-guide.mdx @@ -1,379 +1,102 @@ --- title: Guía de actualización +icon: arrow-up-right-dots --- ## Guías generales -**Asegúrese siempre de respaldar su base de datos antes de iniciar el proceso de actualización** ejecutando `docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql`. - -Para restaurar el respaldo, ejecute `cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}`. - -Si usó Docker Compose, siga estos pasos: - -1. En una terminal, en el host donde Twenty está funcionando, apague Twenty: `docker compose down` - -2. Actualice la versión cambiando el valor de `TAG` en el archivo .env cerca de su docker-compose. ( Recomendamos consumir la versión `major.minor` como `v0.53` ) - -3. Vuelva a conectar Twenty con `docker compose up -d` - -Si desea actualizar su instancia por algunas versiones, por ejemplo de v0.33.0 a v0.35.0, debe actualizar su instancia secuencialmente, en este ejemplo de v0.33.0 a v0.34.0, luego de v0.34.0 a v0.35.0. - -**Asegúrese de que después de cada versión actualizada tenga una copia de respaldo no corrupta.** - -## Pasos de actualización específicos por versión - -## v1.0 - -¡Hola Twenty v1.0! 🎉 - -## v0.60 - -### Mejoras de rendimiento - -Todas las interacciones con la API de metadatos han sido optimizadas para un mejor rendimiento, particularmente para la manipulación de metadatos de objetos y operaciones de creación de espacios de trabajo. - -Hemos reestructurado nuestra estrategia de almacenamiento en caché para priorizar los aciertos de caché sobre las consultas de base de datos cuando sea posible, mejorando significativamente el rendimiento de las operaciones de la API de metadatos. - -Si encuentra problemas de ejecución después de actualizar, es posible que deba vaciar su caché para asegurar que esté sincronizado con los cambios más recientes. Ejecute este comando en su contenedor del servidor de twenty: +**Haga siempre una copia de seguridad de su base de datos antes de iniciar el proceso de actualización** ejecutando: ```bash -yarn command:prod cache:flush +docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql ``` -### v0.55 - -Actualice su instancia de Twenty para usar la imagen v0.55 - -Ya no necesita ejecutar ningún comando, la nueva imagen se encargará automáticamente de ejecutar todas las migraciones necesarias. - -### Error: `El usuario no tiene permiso` - -Si encuentra errores de autorización en la mayoría de solicitudes después de actualizar, es posible que deba vaciar su caché para recalcular los permisos más recientes. - -En su contenedor `twenty-server`, ejecute: +Para restaurar desde la copia de seguridad: ```bash -yarn command:prod cache:flush +cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user} ``` -Este problema es específico de esta versión de Twenty y no debería ser necesario para futuras actualizaciones. +Si usa Docker Compose, siga estos pasos: -### v0.54 +1. Detenga Twenty: `docker compose down` +2. Cambie el valor de `TAG` en el archivo `.env` junto a su `docker-compose.yml` +3. Inicie Twenty: `docker compose up -d` -Desde la versión `0.53`, no se necesitan acciones manuales. +El servidor ejecuta automáticamente todas las migraciones de actualización necesarias al iniciarse. No se requiere ningún comando manual. -#### Desaparición del esquema de metadatos +## Actualizaciones entre versiones (v1.22+) -Hemos fusionado el esquema `metadata` en el esquema `core` para simplificar la recuperación de datos desde `TypeORM`. -Hemos fusionado el paso del comando `migrate` dentro del comando `upgrade`. No recomendamos ejecutar `migrate` manualmente dentro de ninguno de sus contenedores de servidor/trabajador. +A partir de la **v1.22**, Twenty admite actualizaciones entre versiones. Puede actualizar directamente desde cualquier versión compatible a la última versión sin tener que pasar por cada versión intermedia. -### Desde v0.53 +Por ejemplo, actualizar de la v1.22 directamente a la v2.0 está totalmente admitido. -A partir de `0.53`, la actualización se realiza de forma programática dentro del `DockerFile`, esto significa que de ahora en adelante, no debería necesitar ejecutar ningún comando manualmente. +## Actualización a v2.5+ — envoltura de cifrado de datos en reposo -Asegúrese de seguir actualizando su instancia secuencialmente, sin omitir ninguna versión principal (por ejemplo, de `0.43.3` a `0.44.0` está permitido, pero de `0.43.1` a `0.45.0` no lo está), de lo contrario, podría provocar un desincronización de la versión del espacio de trabajo que podría resultar en errores de ejecución y funciones faltantes. +A partir de la **v2.5**, Twenty almacena los secretos en reposo (tokens OAuth, variables de aplicación, claves privadas de firma, valores de configuración sensibles, secretos TOTP) dentro de una envoltura versionada `enc:v2:` cifrada con `ENCRYPTION_KEY` (o `APP_SECRET` si `ENCRYPTION_KEY` no está definido). -Para verificar si un espacio de trabajo se ha migrado correctamente, puede revisar su versión en la base de datos en la tabla `core.workspace`. +El primer arranque en la v2.5 ejecuta comandos de actualización lentos que **rellenan** las filas existentes en la nueva envoltura. Son idempotentes: si se interrumpe y se reinicia el servidor, se reanuda desde donde se quedó, pero pueden tardar un tiempo en bases de datos grandes. Puedes supervisar el progreso con `upgrade:status`. -Siempre debería estar dentro del rango de la versión `major.minor` actual de su instancia de Twenty; puede ver la versión de su instancia en el panel de administración (en `/settings/admin-panel`, accesible si su usuario tiene la propiedad `canAccessFullAdminPanel` establecida en verdadero en la base de datos) o ejecutando `echo $APP_VERSION` en su contenedor `twenty-server`. +Debes establecer una `ENCRYPTION_KEY` dedicada **antes** de la actualización a v2.5 para que el proceso de relleno escriba las filas bajo esa clave desde el principio. Cambiar de clave después del relleno requiere una [rotación](/l/es/developers/self-host/capabilities/key-rotation). -Para corregir una versión de espacio de trabajo desincronizada, tendrá que actualizar desde la correspondiente versión de twenty siguiendo la guía de actualización relacionada secuencialmente, y así sucesivamente hasta alcanzar la versión deseada. +## Rotación de secretos y claves de firma -#### Eliminación de `auditLog` +Para las tareas operativas del día a día, como rotar `ENCRYPTION_KEY`, rotar la clave de firma JWT o revocar una clave de firma filtrada, consulta la [Guía de rotación de claves](/l/es/developers/self-host/capabilities/key-rotation). -Hemos eliminado el objeto estándar auditLog, lo que significa que el tamaño de su copia de seguridad podría reducirse significativamente después de esta migración. +## Comprobación del estado de la actualización -### v0.51 a v0.52 +El comando `upgrade:status` le permite inspeccionar el estado actual de su instancia y de las migraciones de los espacios de trabajo. Es útil para depurar problemas de actualización o al enviar una solicitud de soporte. -Actualice su instancia de Twenty para usar la imagen v0.52 +Ejecútelo desde el contenedor del servidor: -``` -yarn database:migrate:prod -yarn command:prod upgrade +```bash +docker exec -it {server_container_name_or_id} yarn command:prod upgrade:status ``` -#### Tengo un espacio de trabajo bloqueado en la versión entre `0.52.0` y `0.52.6` +Salida de ejemplo: -Desafortunadamente, `0.52.0` y `0.52.6` se han eliminado completamente de dockerHub. -Tendrá que actualizar manualmente la versión de su espacio de trabajo a `0.51.0` en la base de datos y actualizar usando la versión twenty `0.52.11` siguiendo su guía de actualización justo arriba. +```sh +APP_VERSION: v1.23.0 -### v0.50 a v0.51 +Instance + Inferred version: 1.23.0 + Latest command: 1.23.0_DropWorkspaceVersionColumnFastInstanceCommand_1785000000000 + Status: Up to date + Executed by: v1.23.0 + At: 2026-04-16T11:43:58.823Z -Actualice su instancia de Twenty para usar la imagen v0.51 +Workspace + Apple (20202020-1c25-4d02-bf25-6aeccf7ea419) + Inferred version: 1.23.0 + Latest command: 1.23.0_UpdateGlobalObjectContextCommandMenuItemsCommand_1780000005000 + Status: Up to date + Executed by: v1.23.0 + At: 2026-04-16T11:44:09.361Z -``` -yarn database:migrate:prod -yarn command:prod upgrade +Summary + Instance: Up to date + Workspaces: 1 up to date, 0 behind, 0 failed (1 total) ``` -### v0.44.0 a v0.50.0 +### Opciones -Actualice su instancia de Twenty para usar la imagen v0.50.0 +| Opción | Descripción | +| ------------------------- | -------------------------------------------------------------------------------------------------------- | +| `-w, --workspace-id ` | Filtra por un espacio de trabajo específico. Puede pasarse varias veces. | +| `-f, --failed-only` | Oculta los espacios de trabajo actualizados, y solo muestra los que están desactualizados o han fallado. | -``` -yarn database:migrate:prod -yarn command:prod upgrade +## Solución de Problemas + +Si la actualización falla en algunos espacios de trabajo, el servidor no avanzará más allá del paso con errores. Al reiniciar el servidor (`docker compose up -d`), se reintentará la actualización desde donde se detuvo. + +Para identificar rápidamente los problemas, ejecute: + +```bash +docker exec -it {server_container_name_or_id} yarn command:prod upgrade:status --failed-only ``` -#### Mutación del docker-compose.yml +Esto muestra únicamente los espacios de trabajo que están desactualizados o han fallado, junto con el mensaje de error de cada fallo. -Esta versión incluye una mutación del `docker-compose.yml` para dar acceso al servicio `worker` al volumen `server-local-data`. -Actualice su `docker-compose.yml` local con el [docker-compose.yml de v0.50.0](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml) +## Antes de la v1.22 -### v0.43.0 a v0.44.0 - -Actualice su instancia de Twenty para usar la imagen v0.44.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade -``` - -### v0.42.0 a v0.43.0 - -Actualice su instancia de Twenty para usar la imagen v0.43.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade -``` - -En esta versión, también hemos cambiado a la imagen de postgres:16 en docker-compose.yml. - -#### (Opción 1) Migración de base de datos - -Mantener la imagen postgres-spilo existente está bien, pero tendrá que congelar la versión en su docker-compose.yml a 0.43.0. - -#### (Opción 2) Migración de base de datos - -Si desea migrar su base de datos a la nueva imagen de postgres:16, siga estos pasos: - -1. Descargue su base de datos del contenedor antiguo de postgres-spilo - -``` -docker exec -it twenty-db-1 sh -pg_dump -U {YOUR_POSTGRES_USER} -d {YOUR_POSTGRES_DB} > databases_backup.sql -exit -docker cp twenty-db-1:/home/postgres/databases_backup.sql . -``` - -Asegúrese de que su archivo de respaldo no esté vacío. - -2. Actualice su docker-compose.yml para usar la imagen de postgres:16 como en el archivo [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml). - -3. Restaure la base de datos al nuevo contenedor postgres:16 - -``` -docker cp databases_backup.sql twenty-db-1:/databases_backup.sql -docker exec -it twenty-db-1 sh -psql -U {YOUR_POSTGRES_USER} -d {YOUR_POSTGRES_DB} -f databases_backup.sql -exit -``` - -### v0.41.0 a v0.42.0 - -Actualice su instancia de Twenty para usar la imagen v0.42.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.42 -``` - -**Variables del entorno** - -* Removidos: `FRONT_PORT`, `FRONT_PROTOCOL`, `FRONT_DOMAIN`, `PORT` -* Agregados: `FRONTEND_URL`, `NODE_PORT`, `MAX_NUMBER_OF_WORKSPACES_DELETED_PER_EXECUTION`, `MESSAGING_PROVIDER_MICROSOFT_ENABLED`, `CALENDAR_PROVIDER_MICROSOFT_ENABLED`, `IS_MICROSOFT_SYNC_ENABLED` - -### v0.40.0 a v0.41.0 - -Actualice su instancia de Twenty para usar la imagen v0.41.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.41 -``` - -**Variables del entorno** - -* Removido: `AUTH_MICROSOFT_TENANT_ID` - -### v0.35.0 a v0.40.0 - -Actualice su instancia de Twenty para usar la imagen v0.40.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.40 -``` - -**Variables del entorno** - -* Agregados: `IS_EMAIL_VERIFICATION_REQUIRED`, `EMAIL_VERIFICATION_TOKEN_EXPIRES_IN`, `WORKFLOW_EXEC_THROTTLE_LIMIT`, `WORKFLOW_EXEC_THROTTLE_TTL` - -### v0.34.0 a v0.35.0 - -Actualice su instancia de Twenty para usar la imagen v0.35.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.35 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.35` se encarga de la migración de datos de todos los espacios de trabajo. - -**Variables del entorno** - -* Reemplazamos `ENABLE_DB_MIGRATIONS` por `DISABLE_DB_MIGRATIONS` (valor predeterminado ahora es `false`, probablemente no tenga que establecer nada) - -### v0.33.0 a v0.34.0 - -Actualice su instancia de Twenty para usar la imagen v0.34.0 - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.34 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.34` se encarga de la migración de datos de todos los espacios de trabajo. - -**Variables del entorno** - -* Removido: `FRONT_BASE_URL` -* Agregados: `FRONT_DOMAIN`, `FRONT_PROTOCOL`, `FRONT_PORT` - -Hemos actualizado la forma en que manejamos la URL del frontend. -Ahora puede configurar la URL del frontend usando las variables `FRONT_DOMAIN`, `FRONT_PROTOCOL` y `FRONT_PORT`. -Si FRONT_DOMAIN no está configurado, la URL del frontend volverá a `SERVER_URL`. - -### v0.32.0 a v0.33.0 - -Actualice su instancia de Twenty para usar la imagen v0.33.0 - -``` -yarn command:prod cache:flush -yarn database:migrate:prod -yarn command:prod upgrade-0.33 -``` - -El comando `yarn command:prod cache:flush` eliminará la caché de Redis. -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.33` se encarga de la migración de datos de todos los espacios de trabajo. - -A partir de esta versión, la imagen twenty-postgres para DB quedó obsoleta y ahora se usa twenty-postgres-spilo. -Si desea seguir usando la imagen twenty-postgres, simplemente reemplace `twentycrm/twenty-postgres:${TAG}` con `twentycrm/twenty-postgres` en docker-compose.yml. - -### v0.31.0 a v0.32.0 - -Actualice su instancia de Twenty para usar la imagen v0.32.0 - -**Migración de esquemas y datos** - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.32 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.32` se encarga de la migración de datos de todos los espacios de trabajo. - -**Variables del entorno** - -Hemos actualizado la forma en que manejamos la conexión Redis. - -* Removidos: `REDIS_HOST`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD` -* Agregado: `REDIS_URL` - -Actualice su archivo `.env` para usar la nueva variable `REDIS_URL` en lugar de los parámetros de conexión individuales de Redis. - -También hemos simplificado la forma en que manejamos los tokens JWT. - -* Removidos: `ACCESS_TOKEN_SECRET`, `LOGIN_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `FILE_TOKEN_SECRET` -* Agregado: `APP_SECRET` - -Actualice su archivo `.env` para usar la nueva variable `APP_SECRET` en lugar de los secretos de tokens individuales (puede usar el mismo secreto que antes o generar una nueva cadena aleatoria) - -**Cuenta conectada** - -Si está utilizando cuentas conectadas para sincronizar sus correos electrónicos y calendarios de Google, deberá activar la [API de People](https://developers.google.com/people) en su consola de administración de Google. - -### v0.30.0 a v0.31.0 - -Actualice su instancia de Twenty para usar la imagen v0.31.0 - -**Migración de esquemas y datos:** - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.31 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.31` se encarga de la migración de datos de todos los espacios de trabajo. - -### v0.24.0 a v0.30.0 - -Actualice su instancia de Twenty para usar la imagen v0.30.0 - -**Cambio importante**: -Para mejorar el rendimiento, Twenty ahora requiere que la caché redis esté configurada. Hemos actualizado nuestro [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml) para reflejar esto. -Asegúrese de actualizar su configuración y sus variables de entorno en consecuencia: - -``` -REDIS_HOST={your-redis-host} -REDIS_PORT={your-redis-port} -CACHE_STORAGE_TYPE=redis -``` - -**Migración de esquemas y datos:** - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.30 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.30` se encarga de la migración de datos de todos los espacios de trabajo. - -### v0.23.0 a v0.24.0 - -Actualice su instancia de Twenty para usar la imagen v0.24.0 - -Ejecución de los siguientes comandos: - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.24 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la estructura de la base de datos (esquemas core y metadata) -El `yarn command:prod upgrade-0.24` se encarga de la migración de datos de todos los espacios de trabajo. - -### v0.22.0 a v0.23.0 - -Actualice su instancia de Twenty para usar la imagen v0.23.0 - -Ejecución de los siguientes comandos: - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.23 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la base de datos. -El `yarn command:prod upgrade-0.23` se encarga de la migración de datos, incluyendo la transferencia de actividades a tareas/notas. - -### v0.21.0 a v0.22.0 - -Actualice su instancia de Twenty para usar la imagen v0.22.0 - -Ejecución de los siguientes comandos: - -``` -yarn database:migrate:prod -yarn command:prod upgrade-0.22 -``` - -El comando `yarn database:migrate:prod` aplicará las migraciones a la base de datos. -El comando `yarn command:prod upgrade-0.22` aplicará transformaciones de datos específicas para adaptarse a las nuevas opciones predeterminadas de instrumentación de solicitud de objetos. +Si su instancia es anterior a la v1.22, debe actualizar de forma incremental a través de cada versión principal etiquetada (de la v1.6 a la v1.7, luego de la v1.7 a la v1.8, y así sucesivamente) hasta llegar a la v1.22. A partir de ahí, puede actualizar directamente a la última versión. diff --git a/packages/twenty-docs/l/es/navigation.json b/packages/twenty-docs/l/es/navigation.json index 4b022c374e..fba951d368 100644 --- a/packages/twenty-docs/l/es/navigation.json +++ b/packages/twenty-docs/l/es/navigation.json @@ -1,24 +1,27 @@ { "tabs": { + "gettingStarted": { + "label": "Primeros pasos", + "groups": { + "welcome": { + "label": "Bienvenido" + }, + "coreConcepts": { + "label": "Conceptos clave" + } + } + }, "userGuide": { "label": "Guía de usuario", "groups": { - "discoverTwenty": { - "label": "Descubre Twenty", - "groups": { - "gettingStartedCapabilities": { - "label": "Capacidades" - }, - "gettingStartedHowTos": { - "label": "Guías prácticas" - } - } + "userGuideOverview": { + "label": "Resumen" }, "dataModel": { "label": "Modelo de datos", "groups": { - "dataModelCapabilities": { - "label": "Capacidades" + "dataModelReference": { + "label": "Referencia" }, "dataModelHowTos": { "label": "Guías prácticas" @@ -28,8 +31,8 @@ "dataMigration": { "label": "Migración de datos", "groups": { - "dataMigrationCapabilities": { - "label": "Capacidades" + "dataMigrationReference": { + "label": "Referencia" }, "dataMigrationHowTos": { "label": "Guías prácticas" @@ -39,8 +42,8 @@ "calendarEmails": { "label": "Calendario y correos electrónicos", "groups": { - "calendarEmailsCapabilities": { - "label": "Capacidades" + "calendarEmailsReference": { + "label": "Referencia" }, "calendarEmailsHowTos": { "label": "Guías prácticas" @@ -50,8 +53,8 @@ "workflows": { "label": "Flujos de trabajo", "groups": { - "workflowsCapabilities": { - "label": "Capacidades" + "workflowsReference": { + "label": "Referencia" }, "workflowsHowTos": { "label": "Guías prácticas", @@ -75,21 +78,26 @@ "ai": { "label": "IA", "groups": { - "aiCapabilities": { - "label": "Capacidades" + "aiReference": { + "label": "Referencia" }, "aiHowTos": { "label": "Guías prácticas" } } }, - "viewsPipelines": { - "label": "Vistas y canalizaciones", + "layout": { + "label": "Diseño", "groups": { - "viewsPipelinesCapabilities": { - "label": "Capacidades" + "layoutReference": { + "label": "Referencia", + "groups": { + "layoutViews": { + "label": "Vistas" + } + } }, - "viewsPipelinesHowTos": { + "layoutHowTos": { "label": "Guías prácticas" } } @@ -97,8 +105,8 @@ "dashboards": { "label": "Tableros", "groups": { - "dashboardsCapabilities": { - "label": "Capacidades" + "dashboardsReference": { + "label": "Referencia" }, "dashboardsHowTos": { "label": "Guías prácticas" @@ -108,8 +116,8 @@ "permissionsAccess": { "label": "Permisos y acceso", "groups": { - "permissionsAccessCapabilities": { - "label": "Capacidades" + "permissionsAccessReference": { + "label": "Referencia" }, "permissionsAccessHowTos": { "label": "Guías prácticas" @@ -119,8 +127,8 @@ "billing": { "label": "Facturación", "groups": { - "billingCapabilities": { - "label": "Capacidades" + "billingReference": { + "label": "Referencia" }, "billingHowTos": { "label": "Guías prácticas" @@ -130,8 +138,8 @@ "settings": { "label": "Configuración", "groups": { - "settingsCapabilities": { - "label": "Capacidades" + "settingsReference": { + "label": "Referencia" }, "settingsHowTos": { "label": "Guías prácticas" @@ -143,59 +151,40 @@ "developers": { "label": "Desarrolladores", "groups": { - "developersGroup": { - "label": "Desarrolladores" + "developersOverview": { + "label": "Resumen" }, - "extend": { - "label": "Ampliar", + "apps": { + "label": "Aplicaciones", "groups": { - "extendCapabilities": { - "label": "Capacidades" + "appsGettingStarted": { + "label": "Primeros pasos" + }, + "appsConfig": { + "label": "Configuración" + }, + "appsData": { + "label": "Datos" + }, + "appsLogic": { + "label": "Lógica" + }, + "appsLayout": { + "label": "Diseño" + }, + "appsOperations": { + "label": "Operaciones" } } }, + "api": { + "label": "API" + }, "selfHost": { - "label": "Autoalojamiento", - "groups": { - "selfHostCapabilities": { - "label": "Capacidades" - } - } + "label": "Autoalojamiento" }, "contribute": { - "label": "Contribuir", - "groups": { - "contributeCapabilities": { - "label": "Capacidades", - "groups": { - "frontendDevelopment": { - "label": "Desarrollo Frontend", - "groups": { - "twentyUi": { - "label": "Twenty UI", - "groups": { - "display": { - "label": "Mostrar" - }, - "feedback": { - "label": "Retroalimentación" - }, - "input": { - "label": "Entrada" - }, - "navigation": { - "label": "Navegación" - } - } - } - } - }, - "backendDevelopment": { - "label": "Desarrollo Backend" - } - } - } - } + "label": "Contribuir" } } } diff --git a/packages/twenty-docs/l/es/twenty-ui/display/app-tooltip.mdx b/packages/twenty-docs/l/es/twenty-ui/display/app-tooltip.mdx index 6d56450f2a..8aafa173b7 100644 --- a/packages/twenty-docs/l/es/twenty-ui/display/app-tooltip.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/display/app-tooltip.mdx @@ -1,5 +1,6 @@ --- title: Consejo de la aplicación +icon: mensaje --- @@ -9,46 +10,54 @@ title: Consejo de la aplicación Un breve mensaje que muestra información adicional cuando un usuario interactúa con un elemento. - - ```jsx - import { AppTooltip } from "@/ui/display/tooltip/AppTooltip"; + - export const MyComponent = () => { - return ( - <> -

- Customer Insights -

- - - ); - }; - ``` -
+```jsx +import { AppTooltip } from "@/ui/display/tooltip/AppTooltip"; + +export const MyComponent = () => { + return ( + <> +

+ Customer Insights +

+ + + ); +}; +``` + +
+ + + + +| Props | Tipo | Descripción | +| ------------------ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| nombreDeClase | cadena | Clase CSS opcional para estilo adicional | +| anchorSelect | Selector CSS | Selector para el ancla del consejo (el elemento que activa el consejo) | +| contenido | cadena | El contenido que desea mostrar dentro del consejo | +| delayHide | número | The delay in seconds before hiding the tooltip after the cursor leaves the anchor | +| desplazamiento | número | El desplazamiento en píxeles para posicionar el consejo | +| sinFlecha | booleano | Si es `true`, oculta la flecha en el consejo | +| estáAbierto | booleano | Si es `true`, el consejo está abierto por defecto | +| lugar | Cadena `PlacesType` de `react-tooltip` | Especifica la colocación del consejo. Los valores incluyen `inferior`, `izquierda`, `derecha`, `superior`, `superior-inicio`, `superior-fin`, `derecha-inicio`, `derecha-fin`, `inferior-inicio`, `inferior-fin`, `izquierda-inicio` y `izquierda-fin` | +| estrategiaPosicion | Cadena `PositionStrategy` de `react-tooltip` | Estrategia de posición para el consejo. Tiene dos valores: `absoluto` y `fijo` | + + + + - - | Props | Tipo | Descripción | - | ------------------ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - | nombreDeClase | cadena | Clase CSS opcional para estilo adicional | - | anchorSelect | Selector CSS | Selector para el ancla del consejo (el elemento que activa el consejo) | - | contenido | cadena | El contenido que desea mostrar dentro del consejo | - | delayHide | número | The delay in seconds before hiding the tooltip after the cursor leaves the anchor | - | desplazamiento | número | El desplazamiento en píxeles para posicionar el consejo | - | sinFlecha | booleano | Si es `true`, oculta la flecha en el consejo | - | estáAbierto | booleano | Si es `true`, el consejo está abierto por defecto | - | lugar | Cadena `PlacesType` de `react-tooltip` | Especifica la colocación del consejo. Los valores incluyen `inferior`, `izquierda`, `derecha`, `superior`, `superior-inicio`, `superior-fin`, `derecha-inicio`, `derecha-fin`, `inferior-inicio`, `inferior-fin`, `izquierda-inicio` y `izquierda-fin` | - | estrategiaPosicion | Cadena `PositionStrategy` de `react-tooltip` | Estrategia de posición para el consejo. Tiene dos valores: `absoluto` y `fijo` | -
## Texto Desbordante con Consejo @@ -56,22 +65,30 @@ Un breve mensaje que muestra información adicional cuando un usuario interactú Maneja texto desbordante y muestra un consejo cuando el texto se desborda. - - ```jsx - import { OverflowingTextWithTooltip } from 'twenty-ui/display'; + - export const MyComponent = () => { - const crmTaskDescription = - 'Follow up with client regarding their recent product inquiry. Discuss pricing options, address any concerns, and provide additional product information. Record the details of the conversation in the CRM for future reference.'; +```jsx +import { OverflowingTextWithTooltip } from 'twenty-ui/display'; - return ; - }; - ``` - +export const MyComponent = () => { + const crmTaskDescription = + 'Follow up with client regarding their recent product inquiry. Discuss pricing options, address any concerns, and provide additional product information. Record the details of the conversation in the CRM for future reference.'; + + return ; +}; +``` + + + + + + +| Props | Tipo | Descripción | +| ----- | ------ | -------------------------------------------------------------- | +| texto | cadena | El contenido que desea mostrar en el área de texto desbordante | + + + + - - | Props | Tipo | Descripción | - | ----- | ------ | -------------------------------------------------------------- | - | texto | cadena | El contenido que desea mostrar en el área de texto desbordante | - diff --git a/packages/twenty-docs/l/es/twenty-ui/display/checkmark.mdx b/packages/twenty-docs/l/es/twenty-ui/display/checkmark.mdx index 6962251312..55012d2f29 100644 --- a/packages/twenty-docs/l/es/twenty-ui/display/checkmark.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/display/checkmark.mdx @@ -1,5 +1,6 @@ --- title: Marca de verificación +icon: circle-check --- @@ -9,19 +10,25 @@ title: Marca de verificación Representa una acción exitosa o completada. - - ```jsx - import { Checkmark } from 'twenty-ui/display'; + - export const MyComponent = () => { - return ; - }; - ``` - +```jsx +import { Checkmark } from 'twenty-ui/display'; + +export const MyComponent = () => { + return ; +}; +``` + + + + + + +Extiende `React.ComponentPropsWithoutRef<'div'>` y acepta todas las propiedades de un elemento `div` regular. + + - - Extiende `React.ComponentPropsWithoutRef<'div'>` y acepta todas las propiedades de un elemento `div` regular. - ## Marca de verificación animada @@ -29,29 +36,38 @@ Representa una acción exitosa o completada. Representa un ícono de marca de verificación con la característica adicional de animación. - - ```jsx - import { AnimatedCheckmark } from 'twenty-ui/display'; - export const MyComponent = () => { - return ( - - ); - }; - ``` - + + +```jsx +import { AnimatedCheckmark } from 'twenty-ui/display'; + +export const MyComponent = () => { + return ( + + ); +}; +``` + + + + + + +| Props | Tipo | Descripción | Predeterminado | +| ----------- | -------- | -------------------------------------------------- | -------------- | +| isAnimating | booleano | Controla si la marca de verificación está animando | falso | +| color | cadena | Color de la marca de verificación | | +| duración | número | La duración de la animación en segundos | 0.5 segundos | +| tamaño | número | El tamaño de la marca de verificación | 28 píxeles | + + + + - - | Props | Tipo | Descripción | Predeterminado | - | ----------- | -------- | -------------------------------------------------- | -------------- | - | isAnimating | booleano | Controla si la marca de verificación está animando | falso | - | color | cadena | Color de la marca de verificación | | - | duración | número | La duración de la animación en segundos | 0.5 segundos | - | tamaño | número | El tamaño de la marca de verificación | 28 píxeles | - diff --git a/packages/twenty-docs/l/es/twenty-ui/display/chip.mdx b/packages/twenty-docs/l/es/twenty-ui/display/chip.mdx index 5406401e61..a8e4aa7e76 100644 --- a/packages/twenty-docs/l/es/twenty-ui/display/chip.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/display/chip.mdx @@ -9,40 +9,48 @@ title: Chip Un elemento visual que puedes usar como contenedor con o sin posibilidad de hacer clic, con una etiqueta, componentes opcionales a la izquierda y a la derecha y varias opciones de estilo para mostrar rótulos y etiquetas. - - ```jsx - import { Chip } from 'twenty-ui/components'; - export const MyComponent = () => { - return ( - - ); - }; + - ``` - +```jsx +import { Chip } from 'twenty-ui/components'; - - | Props | Tipo | Descripción | - | ------------ | ------------------------ | ------------------------------------------------------------------------------------------------ | - | linkToEntity | cadena | El enlace a la entidad | - | entityId | cadena | El identificador único de la entidad | - | nombre | cadena | El nombre de la entidad | - | pictureUrl | cadena | s foto", | - | avatarType | Tipo de Avatar | El tipo de avatar que quieres mostrar. Tiene dos opciones: `redondeado` y `cuadrado` | - | variante | `EntityChipVariant` enum | Variante del chip de entidad que quieres mostrar. Tiene dos opciones: `regular` y `transparente` | - | LeftIcon | IconComponent | Un componente de React que representa un ícono. Mostrado en el lado izquierdo del chip | - +export const MyComponent = () => { + return ( + + ); +}; + +``` + + + + + + +| Props | Tipo | Descripción | +| ------------ | ------------------------ | ------------------------------------------------------------------------------------------------ | +| linkToEntity | cadena | El enlace a la entidad | +| entityId | cadena | El identificador único de la entidad | +| nombre | cadena | El nombre de la entidad | +| pictureUrl | cadena | s foto", | +| avatarType | Tipo de Avatar | El tipo de avatar que quieres mostrar. Tiene dos opciones: `redondeado` y `cuadrado` | +| variante | `EntityChipVariant` enum | Variante del chip de entidad que quieres mostrar. Tiene dos opciones: `regular` y `transparente` | +| LeftIcon | IconComponent | Un componente de React que representa un ícono. Mostrado en el lado izquierdo del chip | + + + + ## Ejemplos @@ -99,39 +107,47 @@ export const MyComponent = () => { Un elemento tipo Chip para mostrar información sobre una entidad. - - ```jsx - import { BrowserRouter as Router } from 'react-router-dom'; - import { IconTwentyStar } from 'twenty-ui/display'; - import { Chip } from 'twenty-ui/components'; - export const MyComponent = () => { - return ( - - - - ); - }; - ``` - + - - | Props | Tipo | Descripción | - | ------------ | ------------------------ | ------------------------------------------------------------------------------------------------ | - | linkToEntity | cadena | El enlace a la entidad | - | entityId | cadena | El identificador único de la entidad | - | nombre | cadena | El nombre de la entidad | - | pictureUrl | cadena | s foto", | - | avatarType | Tipo de Avatar | El tipo de avatar que quieres mostrar. Tiene dos opciones: `redondeado` y `cuadrado` | - | variante | `EntityChipVariant` enum | Variante del chip de entidad que quieres mostrar. Tiene dos opciones: `regular` y `transparente` | - | LeftIcon | IconComponent | Un componente de React que representa un ícono. Mostrado en el lado izquierdo del chip | - +```jsx +import { BrowserRouter as Router } from 'react-router-dom'; +import { IconTwentyStar } from 'twenty-ui/display'; +import { Chip } from 'twenty-ui/components'; + +export const MyComponent = () => { + return ( + + + + ); +}; +``` + + + + + + +| Props | Tipo | Descripción | +| ------------ | ------------------------ | ------------------------------------------------------------------------------------------------ | +| linkToEntity | cadena | El enlace a la entidad | +| entityId | cadena | El identificador único de la entidad | +| nombre | cadena | El nombre de la entidad | +| pictureUrl | cadena | s foto", | +| avatarType | Tipo de Avatar | El tipo de avatar que quieres mostrar. Tiene dos opciones: `redondeado` y `cuadrado` | +| variante | `EntityChipVariant` enum | Variante del chip de entidad que quieres mostrar. Tiene dos opciones: `regular` y `transparente` | +| LeftIcon | IconComponent | Un componente de React que representa un ícono. Mostrado en el lado izquierdo del chip | + + + + diff --git a/packages/twenty-docs/l/es/twenty-ui/display/icons.mdx b/packages/twenty-docs/l/es/twenty-ui/display/icons.mdx index 00bcdc8db2..6788ea6561 100644 --- a/packages/twenty-docs/l/es/twenty-ui/display/icons.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/display/icons.mdx @@ -1,5 +1,6 @@ --- title: Iconos +icon: iconos --- @@ -13,35 +14,44 @@ Una lista de iconos utilizados en toda nuestra aplicación. Usamos iconos Tabler para React en toda la aplicación. - -
- ``` - yarn add @tabler/icons-react - ``` -
+ +
- - Puede importar cada icono como un componente. Aquí hay un ejemplo: +``` +yarn add @tabler/icons-react +``` -
+
- ```jsx - import { IconArrowLeft } from "@tabler/icons-react"; + - export const MyComponent = () => { - return ; - }; - ``` - +Puede importar cada icono como un componente. Aquí hay un ejemplo: +
+ +```jsx +import { IconArrowLeft } from "@tabler/icons-react"; + +export const MyComponent = () => { + return ; +}; +``` + +
+ + + + +| Propiedades | Tipo | Descripción | Predeterminado | +| ----------- | ------ | ----------------------------------------- | -------------- | +| tamaño | número | La altura y el ancho del icono en píxeles | 24 | +| color | cadena | El color de los iconos | currentColor | +| trazo | número | El ancho del trazo del icono en píxeles | 2 | + + + + - - | Propiedades | Tipo | Descripción | Predeterminado | - | ----------- | ------ | ----------------------------------------- | -------------- | - | tamaño | número | La altura y el ancho del icono en píxeles | 24 | - | color | cadena | El color de los iconos | currentColor | - | trazo | número | El ancho del trazo del icono en píxeles | 2 | -
## Iconos Personalizados @@ -53,20 +63,29 @@ Además de los iconos Tabler, la aplicación también utiliza algunos iconos per Muestra un icono de libreta de direcciones. - - ```jsx - import { IconAddressBook } from 'twenty-ui/display'; - export const MyComponent = () => { - return ; - }; - ``` - + + +```jsx +import { IconAddressBook } from 'twenty-ui/display'; + +export const MyComponent = () => { + return ; +}; +``` + + + + + + +| "Props" | Tipo | Descripción | Predeterminado | +| ------- | ------ | ----------------------------------------- | -------------- | +| tamaño | número | La altura y el ancho del icono en píxeles | 24 | +| trazo | número | El ancho del trazo del icono en píxeles | 2 | + + + + - - | "Props" | Tipo | Descripción | Predeterminado | - | ------- | ------ | ----------------------------------------- | -------------- | - | tamaño | número | La altura y el ancho del icono en píxeles | 24 | - | trazo | número | El ancho del trazo del icono en píxeles | 2 | - diff --git a/packages/twenty-docs/l/es/twenty-ui/display/soon-pill.mdx b/packages/twenty-docs/l/es/twenty-ui/display/soon-pill.mdx index bb5a4e9045..189793ab7e 100644 --- a/packages/twenty-docs/l/es/twenty-ui/display/soon-pill.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/display/soon-pill.mdx @@ -2,7 +2,6 @@ title: Soon Pill --- - Una pequeña insignia o "pastilla" para indicar que algo estará disponible próximamente. ```jsx diff --git a/packages/twenty-docs/l/es/twenty-ui/display/tag.mdx b/packages/twenty-docs/l/es/twenty-ui/display/tag.mdx index e368b3873c..14a3199c9e 100644 --- a/packages/twenty-docs/l/es/twenty-ui/display/tag.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/display/tag.mdx @@ -1,34 +1,43 @@ --- title: Etiqueta +icon: etiqueta --- - Componente para categorizar o etiquetar contenido visualmente. - - ```jsx - import { Tag } from "@/ui/display/tag/components/Tag"; - export const MyComponent = () => { - return ( - console.log("click")} - /> - ); - }; - ``` - + + +```jsx +import { Tag } from "@/ui/display/tag/components/Tag"; + +export const MyComponent = () => { + return ( + console.log("click")} + /> + ); +}; +``` + + + + + + +| Props | Tipo | Descripción | +| ------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| nombreDeClase | cadena | Nombre opcional para estilo adicional | +| color | cadena | Color de la etiqueta. Las opciones incluyen: `verde`, `turquesa`, `cielo`, `azul`, `púrpura`, `rosa`, `rojo`, `naranja`, `amarillo`, `gris` | +| texto | cadena | El contenido de la etiqueta | +| enClic | función | Función opcional llamada cuando un usuario hace clic en la etiqueta | + + + + - - | Props | Tipo | Descripción | - | ------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- | - | nombreDeClase | cadena | Nombre opcional para estilo adicional | - | color | cadena | Color de la etiqueta. Las opciones incluyen: `verde`, `turquesa`, `cielo`, `azul`, `púrpura`, `rosa`, `rojo`, `naranja`, `amarillo`, `gris` | - | texto | cadena | El contenido de la etiqueta | - | enClic | función | Función opcional llamada cuando un usuario hace clic en la etiqueta | - diff --git a/packages/twenty-docs/l/es/twenty-ui/input/block-editor.mdx b/packages/twenty-docs/l/es/twenty-ui/input/block-editor.mdx index 6066d72fa8..cca3f9047f 100644 --- a/packages/twenty-docs/l/es/twenty-ui/input/block-editor.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/input/block-editor.mdx @@ -9,22 +9,28 @@ title: Editor de Bloques Usa un editor de texto enriquecido basado en bloques de [BlockNote](https://www.blocknotejs.org/) para permitir a los usuarios editar y ver bloques de contenido. - - ```jsx - import { useBlockNote } from "@blocknote/react"; - import { BlockEditor } from "@/ui/input/editor/components/BlockEditor"; + - export const MyComponent = () => { - const BlockNoteEditor = useBlockNote(); +```jsx +import { useBlockNote } from "@blocknote/react"; +import { BlockEditor } from "@/ui/input/editor/components/BlockEditor"; - return ; - }; - ``` - +export const MyComponent = () => { + const BlockNoteEditor = useBlockNote(); - - | Props | Tipo | Descripción | - | ------ | ----------------- | -------------------------------------------------- | - | editor | `BlockNoteEditor` | La instancia o configuración del editor de bloques | - + return ; +}; +``` + + + + + +| Props | Tipo | Descripción | +| ------ | ----------------- | -------------------------------------------------- | +| editor | `BlockNoteEditor` | La instancia o configuración del editor de bloques | + + + + diff --git a/packages/twenty-docs/l/es/twenty-ui/input/buttons.mdx b/packages/twenty-docs/l/es/twenty-ui/input/buttons.mdx index 621845df83..bf7e25d692 100644 --- a/packages/twenty-docs/l/es/twenty-ui/input/buttons.mdx +++ b/packages/twenty-docs/l/es/twenty-ui/input/buttons.mdx @@ -1,5 +1,6 @@ --- title: Botones +icon: hand-pointer --- @@ -11,428 +12,511 @@ Una lista de botones y grupos de botones utilizados en toda la aplicación. ## Botón - - ```jsx - import { Button } from "@/ui/input/button/components/Button"; - export const MyComponent = () => { - return ( -