i18n - docs translations (#16774)
Created by Github action Pulls the latest documentation translations from Crowdin for all supported languages: - French (fr) - Arabic (ar) - Czech (cs) - German (de) - Spanish (es) - Italian (it) - Japanese (ja) - Korean (ko) - Portuguese (pt) - Romanian (ro) - Russian (ru) - Turkish (tr) - Chinese (zh-CN) --------- Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
@@ -42,31 +42,14 @@ jobs:
|
||||
token: ${{ github.token }}
|
||||
ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
uses: ./.github/actions/yarn-install
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Setup i18n branch
|
||||
- name: Setup i18n-docs branch
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
git fetch origin i18n || true
|
||||
git checkout -B i18n origin/i18n || git checkout -b i18n
|
||||
git fetch origin i18n-docs || true
|
||||
git checkout -B i18n-docs origin/i18n-docs || git checkout -b i18n-docs
|
||||
|
||||
- name: Configure git
|
||||
run: |
|
||||
@@ -79,31 +62,35 @@ jobs:
|
||||
git add .
|
||||
git stash || true
|
||||
|
||||
# Install Crowdin CLI for downloading translations
|
||||
- name: Install Crowdin CLI
|
||||
if: github.event_name != 'pull_request' && (inputs.force_pull == true || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
run: npm install -g @crowdin/cli
|
||||
|
||||
# Pull docs translations from Crowdin one language at a time
|
||||
# This avoids build timeout issues when processing all languages at once
|
||||
- name: Pull translated docs from Crowdin
|
||||
if: github.event_name != 'pull_request' && (inputs.force_pull == true || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
source: 'packages/twenty-docs/**/*.mdx'
|
||||
translation: 'packages/twenty-docs/l/%two_letters_code%/**/%original_file_name%'
|
||||
export_only_approved: false
|
||||
localization_branch_name: i18n
|
||||
base_url: 'https://twenty.api.crowdin.com'
|
||||
skip_untranslated_files: true
|
||||
push_translations: false
|
||||
create_pull_request: false
|
||||
skip_ref_checkout: true
|
||||
dryrun_action: false
|
||||
config: 'crowdin-docs.yml'
|
||||
# Only download languages supported by Mintlify (see supported-languages.ts)
|
||||
# Using multiple -l flags since download_language only accepts single language
|
||||
download_translations_args: '-l fr -l ar -l cs -l de -l es -l it -l ja -l ko -l pt -l ro -l ru -l tr -l zh-CN'
|
||||
run: |
|
||||
# Languages supported by Mintlify (see packages/twenty-docs/src/shared/supported-languages.ts)
|
||||
LANGUAGES="fr ar cs de es it ja ko pt ro ru tr zh-CN"
|
||||
|
||||
for lang in $LANGUAGES; do
|
||||
echo "=== Pulling translations for $lang ==="
|
||||
crowdin download \
|
||||
--config crowdin-docs.yml \
|
||||
--token "$CROWDIN_PERSONAL_TOKEN" \
|
||||
--base-url "https://twenty.api.crowdin.com" \
|
||||
--language "$lang" \
|
||||
--skip-untranslated-strings=false \
|
||||
--skip-untranslated-files=false \
|
||||
--export-only-approved=false \
|
||||
--verbose || echo "Warning: Failed to pull $lang, continuing with other languages..."
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Download complete ==="
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
# Docs translations project
|
||||
CROWDIN_PROJECT_ID: '2'
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
||||
- name: Fix file permissions
|
||||
@@ -147,13 +134,13 @@ jobs:
|
||||
|
||||
- name: Push changes
|
||||
if: github.event_name != 'pull_request' && steps.check_changes.outputs.changes_detected == 'true'
|
||||
run: git push origin HEAD:i18n
|
||||
run: git push origin HEAD:i18n-docs
|
||||
|
||||
- name: Create pull request
|
||||
if: github.event_name != 'pull_request' && steps.check_changes.outputs.changes_detected == 'true'
|
||||
run: |
|
||||
if git diff --name-only origin/main..HEAD | grep -q .; then
|
||||
gh pr create -B main -H i18n --title 'i18n - docs translations' --body 'Created by Github action' || true
|
||||
gh pr create -B main -H i18n-docs --title 'i18n - docs translations' --body 'Created by Github action' || true
|
||||
else
|
||||
echo "No file differences between branches, skipping PR creation"
|
||||
fi
|
||||
|
||||
@@ -7,10 +7,11 @@ on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
push:
|
||||
branches: ['main', 'docs-localized-navigation']
|
||||
branches: ['main']
|
||||
paths:
|
||||
- 'packages/twenty-docs/**/*.mdx'
|
||||
- '!packages/twenty-docs/fr/**'
|
||||
- '!packages/twenty-docs/l/**'
|
||||
- 'packages/twenty-docs/navigation/navigation.template.json'
|
||||
- 'crowdin-docs.yml'
|
||||
|
||||
concurrency:
|
||||
@@ -28,15 +29,8 @@ jobs:
|
||||
token: ${{ github.token }}
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
uses: ./.github/actions/yarn-install
|
||||
|
||||
- name: Generate navigation template for Crowdin
|
||||
run: yarn docs:generate-navigation-template
|
||||
@@ -47,7 +41,7 @@ jobs:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: false
|
||||
localization_branch_name: i18n
|
||||
localization_branch_name: i18n-docs
|
||||
base_url: 'https://twenty.api.crowdin.com'
|
||||
config: 'crowdin-docs.yml'
|
||||
env:
|
||||
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
push_sources: false
|
||||
skip_untranslated_strings: false
|
||||
skip_untranslated_files: false
|
||||
push_translations: true
|
||||
push_translations: false
|
||||
create_pull_request: false
|
||||
skip_ref_checkout: true
|
||||
dryrun_action: false
|
||||
@@ -116,8 +116,6 @@ jobs:
|
||||
npx nx run twenty-emails:lingui:compile
|
||||
npx nx run twenty-front:lingui:compile
|
||||
git status
|
||||
git config --global user.name 'github-actions'
|
||||
git config --global user.email 'github-actions@twenty.com'
|
||||
git add .
|
||||
if ! git diff --staged --quiet --exit-code; then
|
||||
git commit -m "chore: compile translations"
|
||||
|
||||
+2
-1
@@ -1,10 +1,11 @@
|
||||
#
|
||||
# Crowdin CLI configuration for Documentation translations
|
||||
# Project ID: 2
|
||||
# See https://crowdin.github.io/crowdin-cli/configuration for more information
|
||||
#
|
||||
|
||||
"project_id": 2
|
||||
"preserve_hierarchy": true
|
||||
"base_url": "https://twenty.api.crowdin.com"
|
||||
|
||||
files: [
|
||||
{
|
||||
|
||||
@@ -911,7 +911,7 @@
|
||||
"language": "ar",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "دليل المستخدم",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -1079,7 +1079,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI",
|
||||
"group": "الذكاء الاصطناعي",
|
||||
"icon": "robot",
|
||||
"pages": [
|
||||
"l/ar/user-guide/ai/overview",
|
||||
@@ -1130,7 +1130,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "لوحات القيادة",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/ar/user-guide/dashboards/overview",
|
||||
@@ -1170,7 +1170,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "الفوترة",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/ar/user-guide/billing/overview",
|
||||
@@ -1190,7 +1190,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "الإعدادات",
|
||||
"group": "\\ا\\ل\\إ\\ع\\د\\ا\\د\\ا\\ت",
|
||||
"icon": "gear",
|
||||
"pages": [
|
||||
"l/ar/user-guide/settings/overview",
|
||||
@@ -1219,7 +1219,7 @@
|
||||
"tab": "المطورون",
|
||||
"groups": [
|
||||
{
|
||||
"group": "المطورين",
|
||||
"group": "المطورون",
|
||||
"pages": [
|
||||
"l/ar/developers/introduction"
|
||||
]
|
||||
@@ -1267,7 +1267,7 @@
|
||||
"l/ar/developers/contribute/capabilities/local-setup",
|
||||
"l/ar/developers/contribute/capabilities/bug-and-requests",
|
||||
{
|
||||
"group": "تطوير الواجهة",
|
||||
"group": "تطوير الواجهة الأمامية",
|
||||
"pages": [
|
||||
"l/ar/developers/contribute/capabilities/frontend-development/storybook",
|
||||
{
|
||||
@@ -1286,13 +1286,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "الملاحظات",
|
||||
"group": "التغذية الراجعة",
|
||||
"pages": [
|
||||
"l/ar/twenty-ui/progress-bar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Input",
|
||||
"group": "إدخال",
|
||||
"pages": [
|
||||
"l/ar/twenty-ui/input/buttons",
|
||||
"l/ar/twenty-ui/input/color-scheme",
|
||||
@@ -1328,7 +1328,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "تطوير الخلفية",
|
||||
"group": "تطوير الواجهة الخلفية",
|
||||
"pages": [
|
||||
"l/ar/developers/contribute/capabilities/backend-development/server-commands",
|
||||
"l/ar/developers/contribute/capabilities/backend-development/feature-flags",
|
||||
@@ -1351,7 +1351,7 @@
|
||||
"language": "cs",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Uživatelská příručka",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -1507,7 +1507,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Need More Help",
|
||||
"group": "Potřebujete další pomoc",
|
||||
"pages": [
|
||||
"l/cs/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting",
|
||||
"l/cs/user-guide/workflows/how-tos/need-more-help/workflows-faq",
|
||||
@@ -1570,7 +1570,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "Panely",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/cs/user-guide/dashboards/overview",
|
||||
@@ -1610,7 +1610,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Fakturace",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/cs/user-guide/billing/overview",
|
||||
@@ -1715,7 +1715,7 @@
|
||||
"pages": [
|
||||
"l/cs/twenty-ui/introduction",
|
||||
{
|
||||
"group": "Zobrazení",
|
||||
"group": "Zobrazit",
|
||||
"pages": [
|
||||
"l/cs/twenty-ui/display/checkmark",
|
||||
"l/cs/twenty-ui/display/chip",
|
||||
@@ -1726,13 +1726,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Komentář",
|
||||
"group": "Zpětná vazba",
|
||||
"pages": [
|
||||
"l/cs/twenty-ui/progress-bar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Input",
|
||||
"group": "Vstup",
|
||||
"pages": [
|
||||
"l/cs/twenty-ui/input/buttons",
|
||||
"l/cs/twenty-ui/input/color-scheme",
|
||||
@@ -1747,7 +1747,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Navigation",
|
||||
"group": "Navigace",
|
||||
"pages": [
|
||||
"l/cs/twenty-ui/navigation",
|
||||
"l/cs/twenty-ui/navigation/breadcrumb",
|
||||
@@ -1791,7 +1791,7 @@
|
||||
"language": "de",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Benutzerhandbuch",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -1947,7 +1947,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Need More Help",
|
||||
"group": "Brauchen Sie mehr Hilfe",
|
||||
"pages": [
|
||||
"l/de/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting",
|
||||
"l/de/user-guide/workflows/how-tos/need-more-help/workflows-faq",
|
||||
@@ -1959,7 +1959,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI",
|
||||
"group": "KI",
|
||||
"icon": "robot",
|
||||
"pages": [
|
||||
"l/de/user-guide/ai/overview",
|
||||
@@ -2050,7 +2050,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Abrechnung",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/de/user-guide/billing/overview",
|
||||
@@ -2155,7 +2155,7 @@
|
||||
"pages": [
|
||||
"l/de/twenty-ui/introduction",
|
||||
{
|
||||
"group": "Anzeige",
|
||||
"group": "Anzeigen",
|
||||
"pages": [
|
||||
"l/de/twenty-ui/display/checkmark",
|
||||
"l/de/twenty-ui/display/chip",
|
||||
@@ -2166,13 +2166,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Feedback",
|
||||
"group": "Rückmeldung",
|
||||
"pages": [
|
||||
"l/de/twenty-ui/progress-bar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Input",
|
||||
"group": "Eingabe",
|
||||
"pages": [
|
||||
"l/de/twenty-ui/input/buttons",
|
||||
"l/de/twenty-ui/input/color-scheme",
|
||||
@@ -2231,7 +2231,7 @@
|
||||
"language": "es",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Guía de usuario",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -2339,7 +2339,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Workflows",
|
||||
"group": "Flujos de trabajo",
|
||||
"icon": "bolt",
|
||||
"pages": [
|
||||
"l/es/user-guide/workflows/overview",
|
||||
@@ -2399,7 +2399,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI",
|
||||
"group": "IA",
|
||||
"icon": "robot",
|
||||
"pages": [
|
||||
"l/es/user-guide/ai/overview",
|
||||
@@ -2450,7 +2450,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "Tableros",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/es/user-guide/dashboards/overview",
|
||||
@@ -2490,7 +2490,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Facturación",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/es/user-guide/billing/overview",
|
||||
@@ -2648,7 +2648,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Desarrollo de backend",
|
||||
"group": "Desarrollo Backend",
|
||||
"pages": [
|
||||
"l/es/developers/contribute/capabilities/backend-development/server-commands",
|
||||
"l/es/developers/contribute/capabilities/backend-development/feature-flags",
|
||||
@@ -2671,7 +2671,7 @@
|
||||
"language": "it",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Guida Utente",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -2779,7 +2779,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Workflows",
|
||||
"group": "Flussi di Lavoro",
|
||||
"icon": "bolt",
|
||||
"pages": [
|
||||
"l/it/user-guide/workflows/overview",
|
||||
@@ -2890,7 +2890,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "Cruscotti",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/it/user-guide/dashboards/overview",
|
||||
@@ -2930,7 +2930,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Fatturazione",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/it/user-guide/billing/overview",
|
||||
@@ -3035,7 +3035,7 @@
|
||||
"pages": [
|
||||
"l/it/twenty-ui/introduction",
|
||||
{
|
||||
"group": "Visualizzazione",
|
||||
"group": "Mostra",
|
||||
"pages": [
|
||||
"l/it/twenty-ui/display/checkmark",
|
||||
"l/it/twenty-ui/display/chip",
|
||||
@@ -3067,7 +3067,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Navigation",
|
||||
"group": "Navigazione",
|
||||
"pages": [
|
||||
"l/it/twenty-ui/navigation",
|
||||
"l/it/twenty-ui/navigation/breadcrumb",
|
||||
@@ -4431,7 +4431,7 @@
|
||||
"language": "ro",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Ghid de utilizator",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -4587,7 +4587,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Need More Help",
|
||||
"group": "Ai nevoie de mai mult ajutor",
|
||||
"pages": [
|
||||
"l/ro/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting",
|
||||
"l/ro/user-guide/workflows/how-tos/need-more-help/workflows-faq",
|
||||
@@ -4620,7 +4620,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Views & Pipelines",
|
||||
"group": "Vizualizări și fluxuri",
|
||||
"icon": "table",
|
||||
"pages": [
|
||||
"l/ro/user-guide/views-pipelines/overview",
|
||||
@@ -4650,7 +4650,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "Tablouri de Bord",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/ro/user-guide/dashboards/overview",
|
||||
@@ -4690,7 +4690,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Facturare",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/ro/user-guide/billing/overview",
|
||||
@@ -4787,7 +4787,7 @@
|
||||
"l/ro/developers/contribute/capabilities/local-setup",
|
||||
"l/ro/developers/contribute/capabilities/bug-and-requests",
|
||||
{
|
||||
"group": "Dezvoltare Frontend",
|
||||
"group": "Frontend Development",
|
||||
"pages": [
|
||||
"l/ro/developers/contribute/capabilities/frontend-development/storybook",
|
||||
{
|
||||
@@ -4827,7 +4827,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Navigation",
|
||||
"group": "Navigare",
|
||||
"pages": [
|
||||
"l/ro/twenty-ui/navigation",
|
||||
"l/ro/twenty-ui/navigation/breadcrumb",
|
||||
@@ -4871,7 +4871,7 @@
|
||||
"language": "ru",
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Руководство пользователя",
|
||||
"tab": "User Guide",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Discover Twenty",
|
||||
@@ -5027,7 +5027,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Need More Help",
|
||||
"group": "Нужна дополнительная помощь",
|
||||
"pages": [
|
||||
"l/ru/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting",
|
||||
"l/ru/user-guide/workflows/how-tos/need-more-help/workflows-faq",
|
||||
@@ -5039,7 +5039,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "AI",
|
||||
"group": "ИИ",
|
||||
"icon": "robot",
|
||||
"pages": [
|
||||
"l/ru/user-guide/ai/overview",
|
||||
@@ -5060,7 +5060,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Views & Pipelines",
|
||||
"group": "Представления и воронки",
|
||||
"icon": "table",
|
||||
"pages": [
|
||||
"l/ru/user-guide/views-pipelines/overview",
|
||||
@@ -5090,7 +5090,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "Панели управления",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/ru/user-guide/dashboards/overview",
|
||||
@@ -5130,7 +5130,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Биллинг",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/ru/user-guide/billing/overview",
|
||||
@@ -5288,7 +5288,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Бэкенд разработка",
|
||||
"group": "Разработка серверной части",
|
||||
"pages": [
|
||||
"l/ru/developers/contribute/capabilities/backend-development/server-commands",
|
||||
"l/ru/developers/contribute/capabilities/backend-development/feature-flags",
|
||||
@@ -5467,7 +5467,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Need More Help",
|
||||
"group": "Daha Fazla Yardım mı İhtiyacınız Var",
|
||||
"pages": [
|
||||
"l/tr/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting",
|
||||
"l/tr/user-guide/workflows/how-tos/need-more-help/workflows-faq",
|
||||
@@ -5530,7 +5530,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Dashboards",
|
||||
"group": "Gösterge Panelleri",
|
||||
"icon": "chart-bar",
|
||||
"pages": [
|
||||
"l/tr/user-guide/dashboards/overview",
|
||||
@@ -5570,7 +5570,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Billing",
|
||||
"group": "Faturalandırma",
|
||||
"icon": "credit-card",
|
||||
"pages": [
|
||||
"l/tr/user-guide/billing/overview",
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: أفضل الممارسات
|
||||
---
|
||||
|
||||
This document outlines the best practices you should follow when working on the backend.
|
||||
|
||||
## اتبع نهجًا معياريًا
|
||||
|
||||
الواجهة الخلفية تتبع نهجًا معياريًا، وهو مبدأ أساسي عند العمل مع NestJS. تأكد من تقسيم الكود إلى وحدات قابلة لإعادة الاستخدام للحفاظ على كود مرتب ومنظم.
|
||||
يجب أن تحتوي كل وحدة على ميزة أو وظيفة معينة وأن يكون لها نطاق محدد بوضوح. يتيح هذا النهج المعياري فصل الاهتمامات بوضوح ويزيل التعقيدات غير الضرورية.
|
||||
|
||||
## إتاحة الخدمات لاستخدامها في الوحدات
|
||||
|
||||
قم دائمًا بإنشاء خدمات ذات مسؤولية واضحة ووحيدة، مما يعزز من قابلية قراءة وصيانة الكود. Name the services descriptively and consistently.
|
||||
|
||||
يجب أيضًا إتاحة الخدمات التي تريد استخدامها في وحدات أخرى. إتاحة الخدمات للوحدات الأخرى ممكنة عبر نظام حقن التبعيات القوي في NestJS، كما يعزز ذلك الاقتران الضعيف بين المكونات.
|
||||
|
||||
## تجنب استخدام نوع `أي`
|
||||
|
||||
عند إعلان متغير كـ `أي`، فإن مدقق الأنواع في TypeScript لا يقوم بأي عملية تدقيق للنوع، مما يجعله ممكنًا لتعيين أي نوع من القيم للمتغير. يستخدم TypeScript الاستدلال النوعي لتحديد نوع المتغير بناءً على القيمة. من خلال استخدام `أي`، لم يعد بإمكان TypeScript استنتاج النوع. هذا يجعل من الصعب ضبط أخطاء النوع أثناء التطوير، مما يؤدي إلى حدوث أخطاء في وقت التشغيل ويجعل الكود أقل قابلية للصيانة وأقل موثوقية ويصعب فهمه على الآخرين.
|
||||
|
||||
لهذا السبب يجب أن يكون لكل شيء نوع. لذا إن قمت بإنشاء كائن جديد يحتوي على الاسم الأول واسم العائلة، يجب عليك إنشاء واجهة أو نوع يحتوي على الاسم الأول واسم العائلة الذي يحدد شكل الكائن الذي تتعامل معه.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Feature Flags
|
||||
---
|
||||
|
||||
تُستخدم أعلام الميزات لإخفاء الميزات التجريبية. For Twenty, they are set on workspace level and not on a user level.
|
||||
|
||||
## إضافة علم ميزة جديد
|
||||
|
||||
في ملف `FeatureFlagKey.ts` أضف علم الميزة:
|
||||
|
||||
```ts
|
||||
type FeatureFlagKey =
|
||||
| 'IS_FEATURENAME_ENABLED'
|
||||
| ...;
|
||||
```
|
||||
|
||||
أيضًا أضفه إلى التعداد في `feature-flag.entity.ts`:
|
||||
|
||||
```ts
|
||||
enum FeatureFlagKeys {
|
||||
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
لتطبيق علم ميزة على ميزة **الخلفية** استخدم:
|
||||
|
||||
```ts
|
||||
@Gate({
|
||||
featureFlag: 'IS_FEATURENAME_ENABLED',
|
||||
})
|
||||
```
|
||||
|
||||
لتطبيق علم ميزة على ميزة **الواجهة الأمامية** استخدم:
|
||||
|
||||
```ts
|
||||
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
|
||||
```
|
||||
|
||||
## تكوين أعلام الميزات للنشر
|
||||
|
||||
تغيير السجل المعني في جدول `core.featureFlag`:
|
||||
|
||||
| المُعرّف | المفتاح | معرف مساحة العمل | القيمة |
|
||||
| -------- | ------------------------ | ---------------- | ------ |
|
||||
| عشوائي | `IS_FEATURENAME_ENABLED` | معرف مساحة العمل | `صحيح` |
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: هيكلية المجلدات
|
||||
info: نظرة تفصيلية داخل هيكلية مجلدات الخادم
|
||||
---
|
||||
|
||||
الهيكلية الدليلية للمكونات الخلفية كالتالي:
|
||||
|
||||
```
|
||||
server
|
||||
└───ability
|
||||
└───constants
|
||||
└───core
|
||||
└───database
|
||||
└───decorators
|
||||
└───filters
|
||||
└───guards
|
||||
└───health
|
||||
└───integrations
|
||||
└───metadata
|
||||
└───workspace
|
||||
└───utils
|
||||
```
|
||||
|
||||
## قدرات
|
||||
|
||||
تعريف الأذونات وتتضمن معالجات لكل كيان.
|
||||
|
||||
## زخارف
|
||||
|
||||
تعريف الزخارف المخصصة في NestJS لإضافة وظائف جديدة.
|
||||
|
||||
شاهد [زخارف مخصصة](https://docs.nestjs.com/custom-decorators) لمزيد من التفاصيل.
|
||||
|
||||
## الفلاتر
|
||||
|
||||
تتضمن فلاتر استثناء لمعالجة الحالات الطارئة التي قد تحدث في نقاط GraphQL النهائية.
|
||||
|
||||
## حمايات
|
||||
|
||||
شاهد [الحمايات](https://docs.nestjs.com/guards) لمزيد من التفاصيل.
|
||||
|
||||
## الصحة
|
||||
|
||||
تتضمن واجهة برمجية متاحة للجميع (healthz) تعيد جيسون لتأكيد ما إذا كانت قاعدة البيانات تعمل كما هو متوقع.
|
||||
|
||||
## البيانات الوصفية
|
||||
|
||||
تعريف الأشياء المخصصة وتوفر واجهة برمجية GraphQL (graphql/metadata).
|
||||
|
||||
## مساحة العمل
|
||||
|
||||
توليد وتقديم مخطط GraphQL مخصص بناءً على البيانات الوصفية.
|
||||
|
||||
### هيكلية دليل مساحة العمل
|
||||
|
||||
```
|
||||
workspace
|
||||
|
||||
└───workspace-schema-builder
|
||||
└───factories
|
||||
└───graphql-types
|
||||
└───database
|
||||
└───interfaces
|
||||
└───object-definitions
|
||||
└───services
|
||||
└───storage
|
||||
└───utils
|
||||
└───workspace-resolver-builder
|
||||
└───factories
|
||||
└───interfaces
|
||||
└───workspace-query-builder
|
||||
└───factories
|
||||
└───interfaces
|
||||
└───workspace-query-runner
|
||||
└───interfaces
|
||||
└───utils
|
||||
└───workspace-datasource
|
||||
└───workspace-manager
|
||||
└───workspace-migration-runner
|
||||
└───utils
|
||||
└───workspace.module.ts
|
||||
└───workspace.factory.spec.ts
|
||||
└───workspace.factory.ts
|
||||
```
|
||||
|
||||
يحتوي الجذر في دليل مساحة العمل على `workspace.factory.ts`، ملف يحتوي على وظيفة `createGraphQLSchema`. تولّد هذه الوظيفة مخططًا خاصًا بمساحة العمل باستخدام البيانات الوصفية لتخصيص المخطط لمساحات العمل الفردية. من خلال فصل بناء المخطط والمستعرض، نستخدم وظيفة `makeExecutableSchema`، التي تجمع بين هذه العناصر المنفصلة.
|
||||
|
||||
تعتمد هذه الاستراتيجية على التنظيم ولا تساعد فقط في التنظيم، لكنها أيضًا تحسن الأداء، مثل تخزين تعريفات النوع المولدة مؤقتًا لتعزيز الأداء والتوسع.
|
||||
|
||||
### منشئ مخطط مساحة العمل
|
||||
|
||||
يولد مخطط GraphQL، ويتضمن:
|
||||
|
||||
#### مصانع:
|
||||
|
||||
مصانع متخصصة لتوليد الإنشاءات المتعلقة بـ GraphQL.
|
||||
|
||||
* المصنع النوعي يترجم بيانات الحقول الوصفية إلى أنواع GraphQL باستخدام `TypeMapperService`.
|
||||
* The type-definition.factory creates GraphQL input or output objects derived from `objectMetadata`.
|
||||
|
||||
#### أنواع GraphQL
|
||||
|
||||
يتضمن تعدادات، إدخالات، كائنات، وأشكال بسيطة، ويعمل كنقاط بناء للمخطط.
|
||||
|
||||
#### واجهات وتعريفات الكائنات
|
||||
|
||||
تحتوي على مخططات للكيانات GraphQL، وتشتمل على أنواع معرفة مسبقًا ومخصصة مثل `MONEY` أو `URL`.
|
||||
|
||||
#### خدمات
|
||||
|
||||
تحتوي على الخدمة المسؤولة عن ربط FieldMetadataType بنوع GraphQL المناسب أو أدوات التعديل الاستعلامية.
|
||||
|
||||
#### التخزين
|
||||
|
||||
يتضمن فئة `TypeDefinitionsStorage` التي تحتوي على تعريفات نوع قابلة لإعادة الاستخدام، مما يمنع تكرار أنواع GraphQL.
|
||||
|
||||
### منشئ محلل مساحة العمل
|
||||
|
||||
إنشاء وظائف المعالجات للاستعلام وتعديل مخطط GraphQL.
|
||||
|
||||
كل مصنع في هذا الدليل مسؤول عن إنتاج نوع محلل مميز، مثل `FindManyResolverFactory`، المصمم للتطبيق المتكيف عبر جداول مختلفة.
|
||||
|
||||
### مشغل استعلامات مساحة العمل
|
||||
|
||||
يشغل الاستعلامات المولدة على قاعدة البيانات ويحلل النتيجة.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: Message Queue
|
||||
---
|
||||
|
||||
تسهل القوائم العمليات غير المتزامنة. يمكن استخدامها لأداء مهام الخلفية مثل إرسال بريد ترحيبي عند التسجيل.
|
||||
سيكون لكل حالة استخدام فئة قائمة خاصة بها ممتدة من `MessageQueueServiceBase`.
|
||||
|
||||
حاليًا، ندعم `bull-mq`[bull-mq](https://bullmq.io/) فقط كبرنامج تشغيل القائمة.
|
||||
|
||||
## خطوات إنشاء واستخدام قائمة جديدة
|
||||
|
||||
1. أضف اسم قائمة لقائمة جديدة تحت التعداد `MESSAGE_QUEUES`.
|
||||
2. Provide the factory implementation of the queue with the queue name as the dependency token.
|
||||
3. قم بإدراج القائمة التي أنشأتها في الوحدة/الخدمة المطلوبة مع اسم القائمة كرمز تبعية.
|
||||
4. Add worker class with token based injection just like producer.
|
||||
|
||||
### نموذج للاستخدام
|
||||
|
||||
```ts
|
||||
class Resolver {
|
||||
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {}
|
||||
|
||||
async onSomeAction() {
|
||||
//منطق العمل
|
||||
await this.queue.add(someData);
|
||||
}
|
||||
}
|
||||
|
||||
//عامل غير متزامن
|
||||
class CustomWorker {
|
||||
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {
|
||||
this.initWorker();
|
||||
}
|
||||
|
||||
async initWorker() {
|
||||
await this.queue.work(async ({ id, data }) => {
|
||||
//منطق العامل
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: الأوامر الخلفية
|
||||
---
|
||||
|
||||
## الأوامر المفيدة
|
||||
|
||||
يجب تنفيذ هذه الأوامر من مجلد packages/twenty-server.
|
||||
From any other folder you can run `npx nx {command} twenty-server` (or `npx nx run twenty-server:{command}`).
|
||||
|
||||
### إعداد المرة الأولى
|
||||
|
||||
```
|
||||
npx nx database:reset twenty-server # إعداد قاعدة البيانات مع بذور التطوير
|
||||
```
|
||||
|
||||
### بدء الخادم
|
||||
|
||||
```
|
||||
npx nx run twenty-server:start
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```
|
||||
npx nx run twenty-server:lint # pass --fix to fix lint errors
|
||||
```
|
||||
|
||||
### تجربة
|
||||
|
||||
```
|
||||
npx nx run twenty-server:test:unit # تشغيل اختبارات الوحدة
|
||||
npx nx run twenty-server:test:integration # تشغيل اختبارات التكامل
|
||||
```
|
||||
|
||||
ملاحظة: يمكنك تشغيل `npx nx run twenty-server:test:integration:with-db-reset` في حالة احتياجك لإعادة تعيين قاعدة البيانات قبل تشغيل اختبارات التكامل.
|
||||
|
||||
### إعادة تعيين قاعدة البيانات
|
||||
|
||||
If you want to reset and seed the database, you can run the following command:
|
||||
|
||||
```bash
|
||||
npx nx run twenty-server:database:reset
|
||||
```
|
||||
|
||||
### Migrations
|
||||
|
||||
#### للكائنات داخل مخططات Core/Metadata (TypeORM)
|
||||
|
||||
```bash
|
||||
npx nx run twenty-server:typeorm migration:generate src/database/typeorm/core/migrations/nameOfYourMigration -d src/database/typeorm/core/core.datasource.ts
|
||||
```
|
||||
|
||||
#### لكائنات مساحة العمل
|
||||
|
||||
لا توجد ملفات هجيرات، يتم إنشاء الهجيرات تلقائيًا لكل مساحة عمل،
|
||||
مخزنة في قاعدة البيانات، ويتم تطبيقها مع هذا الأمر
|
||||
|
||||
```bash
|
||||
npx nx run twenty-server:command workspace:sync-metadata -f
|
||||
```
|
||||
|
||||
<Warning>
|
||||
سيؤدي هذا إلى إسقاط قاعدة البيانات وإعادة تشغيل الهجرات والبذور.
|
||||
|
||||
تأكد من عمل نسخة احتياطية لأي بيانات تريد الاحتفاظ بها قبل تشغيل هذا الأمر.
|
||||
</Warning>
|
||||
|
||||
## "التقنية المستخدمة"
|
||||
|
||||
Twenty primarily uses NestJS for the backend.
|
||||
|
||||
Prisma كان أول ORM استخدمناه. ولكن للسماح للمستخدمين بإنشاء الحقول والعناصر المخصصة، كان من المنطقي استخدام مستوى أقل حيث نحتاج إلى تحكم دقيق. الآن يستخدم المشروع TypeORM.
|
||||
|
||||
إليك شكل العناصر التقنية الآن.
|
||||
|
||||
**Core**
|
||||
|
||||
* [NestJS](https://nestjs.com/)
|
||||
* [TypeORM](https://typeorm.io/)
|
||||
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
|
||||
|
||||
**قاعدة البيانات**
|
||||
|
||||
* [Postgres](https://www.postgresql.org/)
|
||||
|
||||
**التكاملات مع جهات خارجية**
|
||||
|
||||
* [Sentry](https://sentry.io/welcome/) لتتبع الأخطاء
|
||||
|
||||
**الاختبار**
|
||||
|
||||
* [Jest](https://jestjs.io/)
|
||||
|
||||
**الأدوات**
|
||||
|
||||
* [Yarn](https://yarnpkg.com/)
|
||||
* [ESLint](https://eslint.org/)
|
||||
|
||||
**التطوير**
|
||||
|
||||
* [AWS EKS](https://aws.amazon.com/eks/)
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Bugs, Requests & Pull Requests
|
||||
info: Report issues, request features, and contribute code
|
||||
---
|
||||
|
||||
## الإبلاغ عن الأخطاء
|
||||
|
||||
للإبلاغ عن خطأ، يرجى [إنشاء مشكلة على GitHub](https://github.com/twentyhq/twenty/issues/new).
|
||||
|
||||
يمكنك أيضًا طلب المساعدة عبر [Discord](https://discord.gg/cx5n4Jzs57).
|
||||
|
||||
## Feature Requests
|
||||
|
||||
إذا لم تكن متأكدًا مما إذا كانت مشكلة أو إذا كنت تشعر بأنها مجرد طلب ميزة، فيمكنك على الأرجح [فتح نقاش بدلاً من ذلك](https://github.com/twentyhq/twenty/discussions/new).
|
||||
|
||||
## Submit a Pull Request
|
||||
|
||||
Contributing code to Twenty starts with a pull request (PR).
|
||||
|
||||
### قبل أن تبدأ
|
||||
|
||||
1. Check [existing issues](https://github.com/twentyhq/twenty/issues) for related work
|
||||
2. For new features, open an issue first to discuss
|
||||
3. Review our [Code of Conduct](https://github.com/twentyhq/twenty/blob/main/CODE_OF_CONDUCT.md)
|
||||
|
||||
### Fork and Clone
|
||||
|
||||
1. Fork the repository on GitHub
|
||||
2. Clone your fork:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/twenty.git
|
||||
cd twenty
|
||||
```
|
||||
|
||||
3. Add upstream remote:
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/twentyhq/twenty.git
|
||||
```
|
||||
|
||||
### Create a Branch
|
||||
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
Use descriptive branch names:
|
||||
|
||||
* `feature/add-export-button`
|
||||
* `fix/login-redirect-issue`
|
||||
* `docs/update-api-guide`
|
||||
|
||||
### Make Your Changes
|
||||
|
||||
1. Write clean, well-documented code
|
||||
2. Follow existing code style
|
||||
3. Add tests for new functionality
|
||||
4. Update documentation if needed
|
||||
|
||||
### Submit Your PR
|
||||
|
||||
1. Push your branch:
|
||||
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
|
||||
2. Open a PR on GitHub
|
||||
3. Fill in the PR template
|
||||
4. Link related issues
|
||||
|
||||
### PR Checklist
|
||||
|
||||
* [ ] Code follows project style guidelines
|
||||
* [ ] Tests pass locally
|
||||
* [ ] Documentation is updated
|
||||
* [ ] PR description explains the changes
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
---
|
||||
title: أفضل الممارسات
|
||||
---
|
||||
|
||||
تحدد هذه الوثيقة أفضل الممارسات التي يجب اتباعها عند العمل في الواجهة الأمامية.
|
||||
|
||||
## إدارة الحالة
|
||||
|
||||
تقوم React و Recoil بإدارة الحالة في قاعدة الشيفرة.
|
||||
|
||||
### استخدم `useRecoilState` لتخزين الحالة
|
||||
|
||||
من الجيد إنشاء أكبر عدد ممكن من الذرات لتخزين الحالة الخاصة بك.
|
||||
|
||||
<Warning>
|
||||
من الأفضل استخدام ذرات إضافية بدلاً من محاولة أن تكون مقتضبًا باستخدام تمرير الخصائص.
|
||||
</Warning>
|
||||
|
||||
```tsx
|
||||
export const myAtomState = atom({
|
||||
key: 'myAtomState',
|
||||
default: 'default value',
|
||||
});
|
||||
|
||||
export const MyComponent = () => {
|
||||
const [myAtom, setMyAtom] = useRecoilState(myAtomState);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={myAtom}
|
||||
onChange={(e) => setMyAtom(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### لا تستخدم `useRef` لتخزين الحالة
|
||||
|
||||
تجنب استخدام `useRef` لتخزين الحالة.
|
||||
|
||||
إذا كنت ترغب في تخزين الحالة، يجب أن تستخدم `useState` أو `useRecoilState`.
|
||||
|
||||
انظر [كيفية إدارة إعادة العرض](#managing-re-renders) إذا شعرت أنك بحاجة إلى `useRef` لمنع بعض إعادة العرض من الحدوث.
|
||||
|
||||
## إدارة إعادة العرض
|
||||
|
||||
يمكن أن تكون إعادة العرض صعبة الإدارة في React.
|
||||
|
||||
إليك بعض القواعد التي يجب اتباعها لتجنب إعادة العرض غير الضرورية.
|
||||
|
||||
تذكر أنه يمكنك **دائمًا** تجنب إعادة العرض من خلال فهم سببها.
|
||||
|
||||
### العمل على المستوى الجذري
|
||||
|
||||
تجنب إعادة العرض في الميزات الجديدة أصبح سهلاً الآن عن طريق إزالتها على المستوى الجذري.
|
||||
|
||||
مكون الجانب `PageChangeEffect` يحتوي فقط على `useEffect` واحد يقوم بعقد جميع المنطق لتنفيذه عند تغيير الصفحة.
|
||||
|
||||
بهذه الطريقة، تعرف أن هناك مكان واحد فقط يمكنه تحفيز إعادة العرض.
|
||||
|
||||
### فكر جيدًا قبل إضافة `useEffect` في قاعدة التعليمات البرمجية الخاصة بك
|
||||
|
||||
غالبًا ما تكون إعادة العرض ناجمة عن `useEffect` غير ضروري.
|
||||
|
||||
يجب أن تفكر في ما إذا كنت بحاجة إلى `useEffect`، أو ما إذا كان بإمكانك نقل المنطق إلى وظيفة معالج الحدث.
|
||||
|
||||
ستجد أنه من السهل عمومًا نقل المنطق إلى وظيفة `handleClick` أو `handleChange`.
|
||||
|
||||
يمكنك أيضًا العثور عليها في المكتبات مثل Apollo: `onCompleted`، `onError`، إلخ.
|
||||
|
||||
### استخدم مكونًا متماثلاً لاستخراج `useEffect` أو منطق استدعاء البيانات
|
||||
|
||||
إذا شعرت أنك بحاجة إلى إضافة `useEffect` في مكون الجذر الخاص بك، يجب أن تفكر في استخراجه في مكون الجانب.
|
||||
|
||||
يمكنك تطبيق نفس الشيء على منطق جلب البيانات، مع الخُطافات Apollo.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, will cause re-renders even if data is not changing,
|
||||
// because useEffect needs to be re-evaluated
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
setData(someDependency);
|
||||
}
|
||||
}, [someDependency]);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, will not cause re-renders if data is not changing,
|
||||
// because useEffect is re-evaluated in another sibling component
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const PageData = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
setData(someDependency);
|
||||
}
|
||||
}, [someDependency]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageData />
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
);
|
||||
```
|
||||
|
||||
### استخدم حالات عائلة Recoil ومحددات عائلة Recoil
|
||||
|
||||
حالات عائلة Recoil والمحددات تعتبر طريقة رائعة لتجنب إعادة العرض.
|
||||
|
||||
إنها مفيدة عندما تحتاج إلى تخزين قائمة من العناصر.
|
||||
|
||||
### يجب ألا تستخدم `React.memo(MyComponent)`
|
||||
|
||||
تجنب استخدام `React.memo()` لأنه لا يحل سبب إعادة العرض، بل يكسر سلسلة إعادة العرض، مما قد يؤدي إلى سلوك غير متوقع ويجعل التعليمات البرمجية صعبة التعديل.
|
||||
|
||||
### حدد استخدام `useCallback` أو `useMemo`
|
||||
|
||||
غالبًا ما لا تكون ضرورية وستجعل التعليمات البرمجية أصعب في القراءة والصيانة لأداء غير ملحوظ.
|
||||
|
||||
## Console.logs
|
||||
|
||||
تصريحات `console.log` ذات قيمة أثناء التطوير، حيث تقدم رؤى في الوقت الفعلي عن قيمة المتغيرات وتدفق التعليمات البرمجية. ولكن، تركها في التعليمات البرمجية في الإنتاج قد يؤدي إلى عدة مشكلات:
|
||||
|
||||
1. **الأداء**: تسجيل كثير قد يؤثر على أداء وقت التشغيل، خاصة في التطبيقات على الجانب العميل.
|
||||
|
||||
2. **الأمان**: تسجيل البيانات الحساسة قد يكشف المعلومات الحرجة لأي شخص يقوم بتفتيش وحدة التحكم في المتصفح.
|
||||
|
||||
3. **النظافة**: ملء وحدة التحكم بالسجلات قد يحجب التحذيرات أو الأخطاء الهامة التي يحتاج المطورون أو الأدوات إلى رؤيتها.
|
||||
|
||||
4. **الاحترافية**: المستخدمون النهائيون أو العملاء الذين يفحصون وحدة التحكم ويجدون الكثير من تصريحات السجلات قد يشككون في جودة وتأنق التعليمات البرمجية.
|
||||
|
||||
تأكد من إزالة جميع تصريحات `console.log` قبل دفع التعليمات البرمجية إلى الإنتاج.
|
||||
|
||||
## التسمية
|
||||
|
||||
### تسمية المتغيرات
|
||||
|
||||
يجب أن تعبر أسماء المتغيرات بدقة عن الغرض أو وظيفة المتغير.
|
||||
|
||||
#### المشكلة مع الأسماء العامة
|
||||
|
||||
الأسماء العامة في البرمجة ليست مثالية لأنها تفتقر إلى التحديد، مما يؤدي إلى الغموض وتقليل قابلية قراءة التعليمات البرمجية. مثل هذه الأسماء تفشل في التعبير عن الغرض من المتغير أو الوظيفة، مما يجعل من الصعب على المطورين فهم نية التعليمات البرمجية دون تحقيق أعمق. يمكن أن يؤدي ذلك إلى زيادة وقت إزالة الأخطاء، وزيادة قابلية التعرض للأخطاء، وصعوبات في الصيانة والتعاون. في الوقت نفسه، تجعل التسمية الوصفية التعليمات البرمجية تفسيرية بذاتها وأسهل في التنقل، مما يعزز جودة التعليمات البرمجية وإنتاجية المطور.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, uses a generic name that doesn't communicate its
|
||||
// purpose or content clearly
|
||||
const [value, setValue] = useState('');
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, uses a descriptive name
|
||||
const [email, setEmail] = useState('');
|
||||
```
|
||||
|
||||
#### بعض الكلمات يجب تجنبها في أسماء المتغيرات
|
||||
|
||||
* dummy
|
||||
|
||||
### معالجات الأحداث
|
||||
|
||||
يجب أن تبدأ أسماء معالجات الأحداث بكلمة `handle`، بينما يعتبر `on` بادئة تستخدم لتسمية الأحداث في خصائص المكونات.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
const onEmailChange = (val: string) => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good
|
||||
const handleEmailChange = (val: string) => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
## الخصائص الاختيارية
|
||||
|
||||
تجنب تمرير القيمة الافتراضية لخاصية اختيارية.
|
||||
|
||||
**مثال**
|
||||
|
||||
خذ مكون`EmailField` المحدد أدناه:
|
||||
|
||||
```tsx
|
||||
type EmailFieldProps = {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
|
||||
<TextInput value={value} disabled={disabled} fullWidth />
|
||||
);
|
||||
```
|
||||
|
||||
**الاستخدام**
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, passing in the same value as the default value adds no value
|
||||
const Form = () => <EmailField value="username@email.com" disabled={false} />;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, assumes the default value
|
||||
const Form = () => <EmailField value="username@email.com" />;
|
||||
```
|
||||
|
||||
## المكون كخصائص
|
||||
|
||||
حاول قدر الإمكان تمرير المكونات غير المنشأة كمكونات، بحيث يمكن للأطفال تحديد ما يحتاجون لتمريره.
|
||||
|
||||
المثال الأكثر شيوعا لذلك هو مكونات الأيقونات:
|
||||
|
||||
```tsx
|
||||
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
|
||||
|
||||
// In MyComponent
|
||||
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MyIcon size={theme.icon.size.md}>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
لفهم React أن المكون هو مكون، يجب عليك استخدام PascalCase، للتمكن من معاملته لاحقًا كـ `<MyIcon>`
|
||||
|
||||
## تمرير الخصائص: اجعلها محدودة
|
||||
|
||||
يشير تمرير الخصائص في سياق React إلى ممارسة تمرير متغيرات الحالة وأدوات تحكمها عبر العديد من طبقات المكونات، حتى لو لم تستخدمها المكونات الوسيطة. رغم أنها تكون ضرورية في بعض الأحيان، إلا أن تمرير الخصائص الزائد يمكن أن يؤدي إلى:
|
||||
|
||||
1. **انخفاض القابلية للقراءة**: يمكن أن يصبح تتبع مصدر الخاصية أو مكان استخدامها معقدا في هيكل مكون معقد.
|
||||
|
||||
2. **تحديات الصيانة**: قد تتطلب التغييرات في هيكل خاصية أحد المكونات تعديلات في عدة مكونات، حتى لو لم تستخدم الخاصية مباشرة.
|
||||
|
||||
3. **تقليل إعادة استخدام المكونات**: يصبح المكون الذي يتلقى الكثير من الخصائص لتمريرها فقط أقل شمولية وأصعب في إعادة استخدامه في سياقات مختلفة.
|
||||
|
||||
إذا شعرت أنك تستخدم تمرير الخصائص بشكل مفرط، راجع [أفضل ممارسات إدارة الحالة](#state-management).
|
||||
|
||||
## استيرادات
|
||||
|
||||
عند الاستيراد، اختر الأسماء المستعارة المتعينة بدلا من تحديد المسارات كاملة أو نسبية.
|
||||
|
||||
**الأسماء المستعارة**
|
||||
|
||||
```js
|
||||
{
|
||||
alias: {
|
||||
"~": path.resolve(__dirname, "src"),
|
||||
"@": path.resolve(__dirname, "src/modules"),
|
||||
"@testing": path.resolve(__dirname, "src/testing"),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**الاستخدام**
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, specifies the entire relative path
|
||||
import {
|
||||
CatalogDecorator
|
||||
} from '../../../../../testing/decorators/CatalogDecorator';
|
||||
import {
|
||||
ComponentDecorator
|
||||
} from '../../../../../testing/decorators/ComponentDecorator';
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, utilises the designated aliases
|
||||
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
```
|
||||
|
||||
## التحقق من المخططات
|
||||
|
||||
[Zod](https://github.com/colinhacks/zod) هو مدقق المخططات للكائنات غير المTyped:
|
||||
|
||||
```js
|
||||
const validationSchema = z
|
||||
.object({
|
||||
exist: z.boolean(),
|
||||
email: z
|
||||
.string()
|
||||
.email('Email must be a valid email'),
|
||||
password: z
|
||||
.string()
|
||||
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
|
||||
})
|
||||
.required();
|
||||
|
||||
type Form = z.infer<typeof validationSchema>;
|
||||
```
|
||||
|
||||
## التغييرات الجذرية
|
||||
|
||||
قم دائمًا بإجراء اختبارات يدوية شاملة قبل المتابعة لضمان أن التعديلات لم تسبب تعطيلًا في أماكن أخرى، نظرًا لأن الاختبارات لم تدمج حتى الآن بشكل كبير.
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
---
|
||||
title: مفاتيح الاختصار
|
||||
---
|
||||
|
||||
## مقدمة
|
||||
|
||||
عندما تحتاج إلى الاستماع إلى مفتاح اختصار، فإنه عادةً ما تستخدم مستمع الحدث `onKeyDown`.
|
||||
|
||||
في `twenty-front`، قد تواجه تعارضات بين نفس مفاتيح الاختصار المستخدمة في مكونات مختلفة، مركبة في الوقت نفسه.
|
||||
|
||||
على سبيل المثال، إذا كان لديك صفحة تستمع لمفتاح Enter ومودال يستمع لمفتاح Enter، ولكن به مكون Select يستمع لمفتاح Enter، فقد تواجه تعارضًا عند تركيب الجميع في الوقت نفسه.
|
||||
|
||||
## الخطاف `useScopedHotkeys`
|
||||
|
||||
لمعالجة هذه المشكلة، لدينا خطاف مخصص يمكن من الاستماع لمفاتيح الاختصار دون أي تعارض.
|
||||
|
||||
You place it in a component, and it will listen to the hotkeys only when the component is mounted AND when the specified **hotkey scope** is active.
|
||||
|
||||
## How to listen for hotkeys in practice?
|
||||
|
||||
هناك خطوتان متضمنتان في إعداد الاستماع لمفاتيح الاختصار:
|
||||
|
||||
1. تعيين [نطاق المفتاح](#what-is-a-hotkey-scope-) الذي سيستمع لمفاتيح الاختصار
|
||||
2. استخدام الخطاف `useScopedHotkeys` للاستماع لمفاتيح الاختصار
|
||||
|
||||
إعداد نطاقات مفاتيح الاختصار ضروري حتى في الصفحات البسيطة، لأن عناصر أخرى في واجهة المستخدم مثل القائمة اليسرى أو قائمة الأوامر قد تستمع أيضًا لمفاتيح الاختصار.
|
||||
|
||||
## حالات الاستخدام لمفاتيح الاختصار
|
||||
|
||||
بشكل عام، سيكون لديك حالتا استخدام تتطلبان مفاتيح الاختصار:
|
||||
|
||||
1. في صفحة أو مكون مركب في صفحة
|
||||
2. في مكون من نوع مودال يتخذ التركيز بسبب إجراء من المستخدم
|
||||
|
||||
يمكن حدوث الحالة الثانية بشكل متكرر: على سبيل المثال، في قائمة منسدلة في مودال.
|
||||
|
||||
### الاستماع لمفاتيح الاختصار في صفحة
|
||||
|
||||
مثال:
|
||||
|
||||
```tsx
|
||||
const PageListeningEnter = () => {
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
// 1. تعيين نطاق المفتاح في استخدام التأثير
|
||||
useEffect(() => {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
ExampleHotkeyScopes.ExampleEnterPage,
|
||||
);
|
||||
|
||||
// العودة إلى نطاق المفتاح السابق عند إلغاء تركيب المكون
|
||||
return () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
};
|
||||
}, [goBackToPreviousHotkeyScope, setHotkeyScopeAndMemorizePreviousScope]);
|
||||
|
||||
// 2. استخدام خطاف useScopedHotkeys
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
// بعض المنطق المنفذ في هذه الصفحة عند ضغط المستخدم على Enter
|
||||
// ...
|
||||
},
|
||||
ExampleHotkeyScopes.ExampleEnterPage,
|
||||
);
|
||||
|
||||
return <div>صفحتي التي تستمع لمفتاح Enter</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### الاستماع لمفاتيح الاختصار في مكون من نوع مودال
|
||||
|
||||
For this example we'll use a modal component that listens for the Escape key to tell its parent to close it.
|
||||
|
||||
Here the user interaction is changing the scope.
|
||||
|
||||
```tsx
|
||||
const ExamplePageWithModal = () => {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const handleOpenModalClick = () => {
|
||||
// 1. تعيين نطاق المفتاح عند فتح المستخدم المودال
|
||||
setShowModal(true);
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
ExampleHotkeyScopes.ExampleModal,
|
||||
);
|
||||
};
|
||||
|
||||
const handleModalClose = () => {
|
||||
// 1. العودة إلى نطاق المفتاح السابق عند إغلاق المودال
|
||||
setShowModal(false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
};
|
||||
|
||||
return <div>
|
||||
<h1>صفحتي التي تحتوي على مودال</h1>
|
||||
<button onClick={handleOpenModalClick}>فتح المودال</button>
|
||||
{showModal && <MyModalComponent onClose={handleModalClose} />}
|
||||
</div>;
|
||||
};
|
||||
```
|
||||
|
||||
ثم في مكون المودال:
|
||||
|
||||
```tsx
|
||||
const MyDropdownComponent = ({ onClose }: { onClose: () => void }) => {
|
||||
// 2. استخدام خطاف useScopedHotkeys للاستماع لمفتاح Escape.
|
||||
// لاحظ أن مفتاح Escape هو مفتاح اختصار شائع يمكن استخدامه من قبل مكونات أخرى كثيرة.
|
||||
// لذلك من المهم استخدام نطاق مفتاح لتجنب التعارضات.
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
onClose()
|
||||
},
|
||||
ExampleHotkeyScopes.ExampleModal,
|
||||
);
|
||||
|
||||
return <div>مكون المودال الخاص بي</div>;
|
||||
};
|
||||
```
|
||||
|
||||
من المهم استخدام هذا النمط عندما لست متأكدًا من أن استخدام useEffect مع التركيب/إلغاء التركيب يكفي لتجنب التعارضات.
|
||||
|
||||
تلك التعارضات يمكن أن تكون صعبة التصحيح، وربما تحدث بشكل متكرر مع useEffects.
|
||||
|
||||
## ما هو نطاق المفتاح؟
|
||||
|
||||
نطاق المفتاح هو سلسلة تمثل السياق الذي تكون فيه مفاتيح الاختصار نشطة. عادة ما تكون مشفرة كنوع مفصل.
|
||||
|
||||
عندما تقوم بتغيير نطاق المفتاح، سيتم تمكين مفاتيح الاختصار المستمعة لهذا النطاق وتعطيل المفاتيح المستمعة لنطاقات أخرى.
|
||||
|
||||
يمكنك تعيين نطاق واحد فقط في كل مرة.
|
||||
|
||||
على سبيل المثال، يتم تعريف نطاقات مفاتيح الاختصار لكل صفحة في نوع "PageHotkeyScope" المفصل:
|
||||
|
||||
```tsx
|
||||
export enum PageHotkeyScope {
|
||||
Settings = 'settings',
|
||||
CreateWorkspace = 'create-workspace',
|
||||
SignInUp = 'sign-in-up',
|
||||
CreateProfile = 'create-profile',
|
||||
PlanRequired = 'plan-required',
|
||||
ShowPage = 'show-page',
|
||||
PersonShowPage = 'person-show-page',
|
||||
CompanyShowPage = 'company-show-page',
|
||||
CompaniesPage = 'companies-page',
|
||||
PeoplePage = 'people-page',
|
||||
OpportunitiesPage = 'opportunities-page',
|
||||
ProfilePage = 'profile-page',
|
||||
WorkspaceMemberPage = 'workspace-member-page',
|
||||
TaskPage = 'task-page',
|
||||
}
|
||||
```
|
||||
|
||||
داخليًا، يتم تخزين النطاق المحدد حاليًا في حالة Recoil مشتركة عبر التطبيق:
|
||||
|
||||
```tsx
|
||||
export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
key: 'currentHotkeyScopeState',
|
||||
defaultValue: INITIAL_HOTKEYS_SCOPE,
|
||||
});
|
||||
```
|
||||
|
||||
لكن لا يجب التعامل مع هذه الحالة Recoil يدويًا! سنرى كيف يمكن استخدامها في القسم التالي.
|
||||
|
||||
## كيف يعمل داخليًا؟
|
||||
|
||||
قمنا بإنشاء غلاف رقيق فوق [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) والذي يجعله أكثر كفاءة ويتجنب عمليات إعادة التقديم غير الضرورية.
|
||||
|
||||
ونقوم أيضًا بإنشاء حالة Recoil للتعامل مع حالة نطاق المفتاح وجعلها متاحة في جميع أنحاء التطبيق.
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Storybook
|
||||
description: Browse Twenty's UI component library
|
||||
---
|
||||
|
||||
View our complete component library and documentation in Storybook.
|
||||
|
||||
[Open Storybook →](https://storybook.twenty.com)
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
---
|
||||
title: دليل الأسلوب
|
||||
---
|
||||
|
||||
تشمل هذه الوثيقة القواعد التي يجب اتباعها عند كتابة التعليمات البرمجية.
|
||||
|
||||
The goal here is to have a consistent codebase, which is easy to read and easy to maintain.
|
||||
|
||||
لهذا، من الأفضل أن تكون تفصيلًا أكثر قليلاً بدلاً من أن تكون موجزًا للغاية.
|
||||
|
||||
دائمًا ضع في اعتبارك أن الناس يقرؤون التعليمات البرمجية أكثر مما يكتبونها، وخاصة في المشاريع مفتوحة المصدر، حيث يمكن لأي شخص المساهمة.
|
||||
|
||||
هناك العديد من القواعد التي لم يتم تعريفها هنا، ولكن يتم التحقق منها تلقائيًا بواسطة أدوات الفحص.
|
||||
|
||||
## React
|
||||
|
||||
### استخدام المكونات الوظيفية
|
||||
|
||||
استخدم دائمًا مكونات TSX الوظيفية.
|
||||
|
||||
لا تستخدم `import` الافتراضي مع `const`، لأنه أصعب من حيث القراءة والدمج باستخدام إكمال التعليمات البرمجية.
|
||||
|
||||
```tsx
|
||||
// ❌ سيئ، أصعب في القراءة، أصعب في الدمج باستخدام إكمال التعليمات البرمجية
|
||||
const MyComponent = () => {
|
||||
return <div>Hello World</div>;
|
||||
};
|
||||
|
||||
export default MyComponent;
|
||||
|
||||
// ✅ جيد، سهل القراءة، سهل الدمج باستخدام إكمال التعليمات البرمجية
|
||||
export function MyComponent() {
|
||||
return <div>Hello World</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### الإزاحة
|
||||
|
||||
قم بإنشاء نوع الخصائص واطلق عليه `(ComponentName)Props` إذا لم يكن هناك حاجة لتصديره.
|
||||
|
||||
استخدام تفكيك الخصائص.
|
||||
|
||||
```tsx
|
||||
// ❌ سيئ، لا يوجد نوع
|
||||
export const MyComponent = (props) => <div>Hello {props.name}</div>;
|
||||
|
||||
// ✅ جيد، النوع
|
||||
type MyComponentProps = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
|
||||
```
|
||||
|
||||
#### امتنع عن استخدام `React.FC` أو `React.FunctionComponent` لتحديد أنواع الخصائص
|
||||
|
||||
```tsx
|
||||
/* ❌ - سيئ، يحدد أنماط المكون باستخدام `FC`
|
||||
* - باستخدام `React.FC`، يقبل المكون ضمنيًا خاصية `children`
|
||||
* حتى لو لم تكن محددة في نوع الخاصية. قد لا يكون هذا دائمًا
|
||||
* مرغوبًا فيه، خاصةً إذا لم يكن المكون ينوي عرض
|
||||
* الأطفال.
|
||||
*/
|
||||
const EmailField: React.FC<{
|
||||
value: string;
|
||||
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
|
||||
* component's props
|
||||
* - This method doesn't automatically include the children prop. If
|
||||
* you want to include it, you have to specify it in OwnProps.
|
||||
*/
|
||||
type EmailFieldProps = {
|
||||
value: string;
|
||||
};
|
||||
|
||||
const EmailField = ({ value }: EmailFieldProps) => (
|
||||
<TextInput value={value} disabled fullWidth />
|
||||
);
|
||||
```
|
||||
|
||||
#### No Single Variable Prop Spreading in JSX Elements
|
||||
|
||||
تجنب استخدام انتشار متغير فردي للخصائص في عناصر JSX، مثل `{...props}`. غالبًا ما تؤدي هذه الممارسة إلى شكل تعليمي أقل قابلية للقراءة وأصعب في الصيانة لأنه من غير الواضح أي الخصائص يتلقاها المكون.
|
||||
|
||||
```tsx
|
||||
/* ❌ - سيء، ينثر متغير فردي للخصائص في المكون الأساسي
|
||||
*/
|
||||
const MyComponent = (props: OwnProps) => {
|
||||
return <OtherComponent {...props} />;
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* ✅ - Good, Explicitly lists all props
|
||||
* - Enhances readability and maintainability
|
||||
*/
|
||||
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
|
||||
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
|
||||
};
|
||||
```
|
||||
|
||||
المبرر:
|
||||
|
||||
* نظرة سريعة تسهل معرفة الخصائص التي تمررها التعليمات البرمجية، مما يسهل من الفهم والصيانة.
|
||||
* يساعد على منع نشوء تعقيدات كبيرة بين المكونات من خلال خصائصها.
|
||||
* أدوات الفحص تجعل من السهل تحديد الخصائص التي بها أخطاء إملائية أو غير مستخدمة عندما تسرد الخصائص بشكل صريح.
|
||||
|
||||
## JavaScript
|
||||
|
||||
### Use nullish-coalescing operator `??`
|
||||
|
||||
```tsx
|
||||
// ❌ سيء، قد يعيد 'default' حتى إذا كانت القيمة 0 أو ''
|
||||
const value = process.env.MY_VALUE || 'default';
|
||||
|
||||
// ✅ جيد، سيعيد 'default' فقط إذا كانت القيمة null أو غير معرّفة
|
||||
const value = process.env.MY_VALUE ?? 'default';
|
||||
```
|
||||
|
||||
### Use optional chaining `?.`
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
onClick && onClick();
|
||||
|
||||
// ✅ Good
|
||||
onClick?.();
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
### استخدام `type` بدلاً من `interface`
|
||||
|
||||
استخدم دائمًا `type` بدلاً من `interface`، لأنهما تقريبًا دائمًا متداخلين، و `type` أكثر مرونة.
|
||||
|
||||
```tsx
|
||||
// ❌ سيء
|
||||
interface MyInterface {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// ✅ جيد
|
||||
type MyType = {
|
||||
name: string;
|
||||
};
|
||||
```
|
||||
|
||||
### Use string literals instead of enums
|
||||
|
||||
[الحروف المشفوعة](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) هي الطريقة المفضلة للتعامل مع القيم الشبيهة بالأعداد المخصصة في TypeScript. من السهل توسيعها باستخدام Pick و Omit، وتقدم تجربة مطور أفضل، خاصة مع إكمال التعليمات البرمجية.
|
||||
|
||||
يمكنك معرفة السبب في أن TypeScript توصي بتجنب الأعداد [هنا](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums).
|
||||
|
||||
```tsx
|
||||
// ❌ سيء، يستخدم عدد مخصص
|
||||
enum Color {
|
||||
Red = "red",
|
||||
Green = "green",
|
||||
Blue = "blue",
|
||||
}
|
||||
|
||||
let color = Color.Red;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ جيد، يستخدم حرفا مشفوعا
|
||||
|
||||
let color: "red" | "green" | "blue" = "red";
|
||||
```
|
||||
|
||||
#### GraphQL والمكتبات الداخلية
|
||||
|
||||
يجب عليك استخدام الأعداد التي يقوم بإنشائها GraphQL codegen.
|
||||
|
||||
من الأفضل أيضًا استخدام عدد مخصص عند استخدام مكتبة داخلية، بحيث لا تضطر المكتبة الداخلية إلى تعريض نوع حرف مشفوع غير متعلق بـ API الداخلي.
|
||||
|
||||
مثال:
|
||||
|
||||
```TSX
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
### استخدام مكونات منسقة
|
||||
|
||||
قم بتنسيق المكونات باستخدام [styled-components](https://emotion.sh/docs/styled).
|
||||
|
||||
```tsx
|
||||
// ❌ سيء
|
||||
<div className="my-class">Hello World</div>
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ جيد
|
||||
const StyledTitle = styled.div`
|
||||
color: red;
|
||||
`;
|
||||
```
|
||||
|
||||
قم بإضافة بادئة للمكونات المنسقة بـ "Styled" لتمييزها عن المكونات "الحقيقية".
|
||||
|
||||
```tsx
|
||||
// ❌ سيء
|
||||
const Title = styled.div`
|
||||
color: red;
|
||||
`;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ جيد
|
||||
const StyledTitle = styled.div`
|
||||
color: red;
|
||||
`;
|
||||
```
|
||||
|
||||
### Theming
|
||||
|
||||
استخدام السمة لتنسيق معظم المكونات هو النهج المفضل.
|
||||
|
||||
#### وحدات القياس
|
||||
|
||||
تجنب استخدام قيم `px` أو `rem` مباشرة داخل المكونات المنسقة. بشكل عام، عادة ما تكون القيم المحددة مسبقًا موجودة بالفعل في السمة، لذا يفضل استخدام السمة لتحقيق هذا الغرض.
|
||||
|
||||
#### ألوان
|
||||
|
||||
امتنع عن تقديم ألوان جديدة، بدلاً من ذلك، استخدم اللوحة الموجودة في السمة. إذا كانت هناك حالة لا تتطابق فيها اللوحة، يرجى ترك تعليق لكي تتمكن الفريق من تصحيحها.
|
||||
|
||||
```tsx
|
||||
// ❌ سيء، يحدد القيم المشفوعة للأسلوب دون استخدام السمة
|
||||
const StyledButton = styled.button`
|
||||
color: #333333;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
margin-left: 4px;
|
||||
border-radius: 50px;
|
||||
`;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ جيد، يستعمل السمة
|
||||
const StyledButton = styled.button`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
border-radius: ${({ theme }) => theme.border.rounded};
|
||||
`;
|
||||
```
|
||||
|
||||
## تطبيق قاعدة "عدم استيراد الأنواع"
|
||||
|
||||
تجنب استيراد الأنواع. للحد من هذه الممارسة، تتحقق قاعدة ESLint وتبلغ عن أي استيرادات من هذا النوع. يساعد هذا على الحفاظ على الاتساق وقابلية القراءة في كود TypeScript.
|
||||
|
||||
```tsx
|
||||
// ❌ سيء
|
||||
import { type Meta, type StoryObj } from '@storybook/react';
|
||||
|
||||
// ❌ سيء
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
// ✅ جيد
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
```
|
||||
|
||||
### لماذا لا نوع استيرادات
|
||||
|
||||
* **الاتساق**: من خلال تجنب استيراد الأنواع واستخدام أسلوب استيراد واحد لكل من الأنواع والقيم، يبقى الكود موحدًا في أسلوب استيراد الوحدة.
|
||||
|
||||
* **القراءة**: استيرادات بلا نوع تحسن من قابلية القراءة للرمز من خلال توضيح عندما تقوم باستيراد القيم أو الأنواع. يقلل هذا من الغموض ويجعل من الأسهل فهم الهدف من الرموز المستوردة.
|
||||
|
||||
* **الصيانة**: يعزز الصيانة داخل قاعدة الكود لأن المطوّرين يمكنهم تحديد مواقع استيرادات الأنواع فقط عند استعراض أو تعديل الكود.
|
||||
|
||||
### قاعدة ESLint
|
||||
|
||||
تفرض قاعدة ESLint، `@typescript-eslint/consistent-type-imports`, معيار عدم استيراد الأنواع. ستولد هذه القاعدة تحذيرات أو أخطاء عن أي انتهاكات لاستيراد الأنواع.
|
||||
|
||||
يرجى ملاحظة أن هذه القاعدة تتناول بشكل خاص حالات الحافة النادرة حيث تحدث استيرادات الأنواع دون قصد. يمنع TypeScript نفسه هذه الممارسة، كما هو موضح في [ملاحظات إصدار TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). في معظم الحالات، لا ينبغي لك أن تستخدم استيرادات الأنواع وحدها.
|
||||
|
||||
لضمان امتثال الكود الخاص بك لهذه القاعدة، تأكد من تشغيل ESLint كجزء من سير العمل الخاص بالتطوير لديك.
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: العمل مع فيجما
|
||||
info: Learn how you can collaborate with Twenty's Figma
|
||||
---
|
||||
|
||||
فيجما هي أداة تصميم واجهات تعاونية تساعد في سد فجوة التواصل بين المصممين والمطورين.
|
||||
يشرح هذا الدليل كيف يمكنك التعاون مع فيجما.
|
||||
|
||||
## الوصول
|
||||
|
||||
1. **الوصول إلى الرابط المشترك:** يمكنك الوصول إلى ملف فيجما الخاص بالمشروع [هنا](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty).
|
||||
2. **تسجيل الدخول:** إذا لم تكن قد سجلت دخولك بالفعل، سيطلب منك فيجما القيام بذلك.
|
||||
تتوفر المميزات الرئيسية فقط للمستخدمين الذين قاموا بتسجيل الدخول، مثل وضع المطور والقدرة على اختيار إطار مخصص.
|
||||
|
||||
<Warning>
|
||||
لن تتمكن من التعاون بفعالية بدون حساب.
|
||||
</Warning>
|
||||
|
||||
## هيكل فيجما
|
||||
|
||||
On the left sidebar, you can access the different pages of Twenty's Figma. هكذا هم مُنظمون:
|
||||
|
||||
* **صفحة المكونات:** هذه هي الصفحة الأولى. يستخدمها المصمم لإنشاء وتنظيم العناصر التصميمية القابلة لإعادة الاستخدام في ملف التصميم. على سبيل المثال، الأزرار، الأيقونات، الرموز أو أي مكونات أخرى قابلة لإعادة الاستخدام. تعمل على الحفاظ على التناسق عبر التصميم.
|
||||
* **الصفحة الرئيسية:** الصفحة الثانية هي الصفحة الرئيسية التي تظهر واجهة المستخدم الكاملة للمشروع. يمكنك الضغط على ***تشغيل*** لاستخدام النموذج الأولي الكامل للتطبيق.
|
||||
* **صفحات الميزات:** الصفحات الأخرى تكون مخصصة عادة للميزات قيد التقدم. تحتوي على تصميم الميزات أو الوحدات المحددة للتطبيق أو الموقع الإلكتروني. عادة ما تكون لا تزال قيد التقدم.
|
||||
|
||||
## نصائح مفيدة
|
||||
|
||||
مع الوصول لعرض فقط، لا يمكنك تحرير التصميم، ولكن يمكنك الوصول إلى جميع الميزات التي ستكون مفيدة لتحويل التصميمات إلى كود.
|
||||
|
||||
### استخدم وضع المطور
|
||||
|
||||
يعزز وضع المطور في فيجما إنتاجية المطورين من خلال توفير التنقل السهل في التصميم، إدارة فعالة للموارد، أدوات اتصال فعالة، تكاملات طقم الأدوات، مقتطفات كود سريعة، ومعلومات رئيسية عن الطبقات، مما يسد الفجوة بين التصميم والتطوير. يمكنك معرفة المزيد عن وضع المطور [هنا](https://www.figma.com/dev-mode/).
|
||||
|
||||
قم بالتبديل إلى وضع "المطور" في الجزء الأيمن من شريط الأدوات لتشاهد مواصفات التصميم، نسخ CSS، والوصول إلى الموارد.
|
||||
|
||||
### استخدم النموذج الأولي
|
||||
|
||||
انقر على أي عنصر على اللوحة واضغط على زر “تشغيل” في نهاية الحافة العلوية لواجهة المستخدم للوصول إلى عرض النموذج الأولي. يسمح لك وضع النموذج الأولي بالتفاعل مع التصميم كما لو كان المنتج النهائي. يوضح التدفق بين الشاشات وكيف تتصرف عناصر الواجهة مثل الأزرار، الروابط، أو القوائم عند التفاعل معها.
|
||||
|
||||
1. **فهم الانتقالات والرسوم المتحركة:** في وضع النموذج الأولي، يمكنك مشاهدة أي انتقالات أو رسوم متحركة أضافها المصمم بين الشاشات أو عناصر واجهة المستخدم، مما يوفر تعليمات بصرية واضحة للمطورين حول السلوك والنمط المقصود.
|
||||
2. **توضيح التنفيذ:** يمكن أن يساعد النموذج الأولي أيضًا في تقليل الغموض. يمكن للمطورين التفاعل معه لاكتساب فهم أفضل لوظيفة أو مظهر عناصر معينة.
|
||||
|
||||
للحصول على تفاصيل شاملة وإرشادات لتعلم منصة فيجما، يمكنك زيارة [وثائق فيجما الرسمية](https://help.figma.com/hc/en-us).
|
||||
|
||||
### قياس المسافات
|
||||
|
||||
حدد عنصرًا، اضغط مع الاستمرار على مفتاح `Option` (لماك) أو مفتاح `Alt` (لويندوز)، ثم مرر فوق عنصر آخر لرؤية المسافة بينهما.
|
||||
|
||||
### إضافة فيجما لـ VSCode (موصى به)
|
||||
|
||||
[فيجما لـ VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension) يتيح لك التنقل ومعاينة ملفات التصميم، التعاون مع المصممين، تتبع التغييرات، وتسريع التنفيذ - دون مغادرة محرر النصوص الخاص بك.
|
||||
إنها جزء من الإضافات الموصى بها لدينا.
|
||||
|
||||
## التعاون
|
||||
|
||||
1. **استخدام التعليقات:** يمكنك استخدام ميزة التعليق بالنقر على أيقونة الفقاعة في الجزء الأيسر من شريط الأدوات.
|
||||
2. **دردشة المؤشر:** ميزة لطيفة في فيجما هي دردشة المؤشر. فقط اضغط على `;` على ماك و`/` على ويندوز لإرسال رسالة إذا رأيت شخصًا آخر يستخدم فيجما في نفس الوقت الذي تستخدمه فيه.
|
||||
@@ -0,0 +1,333 @@
|
||||
---
|
||||
title: الإعداد المحلي
|
||||
description: The guide for contributors (or curious developers) who want to run Twenty locally.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux و MacOS">
|
||||
قبل أن تتمكن من تثبيت واستخدام Twenty، تأكد من تثبيت الأمور التالية على جهاز الكمبيوتر الخاص بك:
|
||||
|
||||
* [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)
|
||||
|
||||
<Warning>
|
||||
لن يعمل `npm` ، يجب عليك استخدام `yarn` بدلًا من ذلك. Yarn is now shipped with Node.js, so you don't need to install it separately.
|
||||
عليك فقط تشغيل `corepack enable` لتفعيل Yarn إذا لم تقم بذلك بعد.
|
||||
</Warning>
|
||||
</Tab>
|
||||
|
||||
<Tab title="ويندوز (WSL)">
|
||||
1. ثبّت WSL
|
||||
افتح PowerShell كمسؤول ثم نفّذ:
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
|
||||
يجب أن ترى الآن مطالبة لإعادة تشغيل جهاز الكمبيوتر الخاص بك. إذا لم يكن كذلك، فأعد تشغيله يدويًا.
|
||||
|
||||
Upon restart, a powershell window will open and install Ubuntu. قد يستغرق هذا وقتًا طويلاً.
|
||||
سترى مطالبة لإنشاء اسم المستخدم وكلمة المرور لتثبيت Ubuntu الخاص بك.
|
||||
|
||||
2. تثبيت وإعداد git
|
||||
|
||||
```bash
|
||||
sudo apt-get install git
|
||||
|
||||
git config --global user.name "Your Name"
|
||||
|
||||
git config --global user.email "youremail@domain.com"
|
||||
```
|
||||
|
||||
3. تثبيت nvm و node.js و yarn
|
||||
|
||||
<Warning>
|
||||
استخدم `nvm` لتثبيت نسخة `node` الصحيحة. الملف `.nvmrc` يضمن استخدام جميع المشاركين لنفس النسخة.
|
||||
</Warning>
|
||||
|
||||
```bash
|
||||
sudo apt-get install curl
|
||||
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
|
||||
```
|
||||
|
||||
أغلق وأعد فتح برنامجك الطرفي لاستخدام nvm. ثم قم بتشغيل الأوامر التالية.
|
||||
|
||||
```bash
|
||||
|
||||
nvm install # يثبت إصدار node الموصى به
|
||||
|
||||
nvm use # استخدم إصدار node الموصى به
|
||||
|
||||
corepack enable
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## الخطوة 1: استنساخ Git
|
||||
|
||||
في الطرفية الخاصة بك، قم بتشغيل الأمر التالي.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="SSH (موصى به)">
|
||||
إذا لم تكن قد أعددت مفاتيح SSH بالفعل، يمكنك معرفة كيفية القيام بذلك [هنا](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh).
|
||||
|
||||
```bash
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="HTTPS">
|
||||
```bash
|
||||
git clone https://github.com/twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 2: انتقل إلى جذر المشروع
|
||||
|
||||
```bash
|
||||
cd twenty
|
||||
```
|
||||
|
||||
يجب تشغيل جميع الأوامر في الخطوات التالية من جذر المشروع.
|
||||
|
||||
## الخطوة 3: إعداد قاعدة بيانات PostgreSQL
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
**الخيار 1 (المفضل):** لتوفير قاعدة بياناتك محليًا:
|
||||
استخدم الرابط التالي لتثبيت Postgresql على جهاز Linux الخاص بك: [تثبيت Postgresql](https://www.postgresql.org/download/linux/)
|
||||
|
||||
```bash
|
||||
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
|
||||
```
|
||||
|
||||
ملاحظة: قد تحتاج إلى إضافة `sudo -u postgres` إلى الأمر قبل `psql` لتجنب أخطاء الإذن.
|
||||
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="نظام Mac OS">
|
||||
**الخيار 1 (المفضل):** لتوفير قاعدة بياناتك محليًا مع `brew`:
|
||||
|
||||
```bash
|
||||
brew install postgresql@16
|
||||
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
|
||||
brew services start postgresql@16
|
||||
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
|
||||
```
|
||||
|
||||
يمكنك التحقق مما إذا كان خادم PostgreSQL يعمل بتنفيذ:
|
||||
|
||||
```bash
|
||||
brew services list
|
||||
```
|
||||
|
||||
المثبت قد لا ينشئ المستخدم `postgres` افتراضيًا عند التثبيت
|
||||
عبر Homebrew على MacOS. بدلاً من ذلك، فإنه ينشئ دور PostgreSQL يطابق
|
||||
اسم المستخدم الخاص بك في MacOS (مثل "john").
|
||||
للتحقق وإنشاء المستخدم `postgres` إذا لزم الأمر، اتبع هذه الخطوات:
|
||||
|
||||
```bash
|
||||
# قم بالاتصال بPostgreSQL
|
||||
psql postgres
|
||||
أو
|
||||
psql -U $(whoami) -d postgres
|
||||
```
|
||||
|
||||
بمجرد أن تكون عند مطالبة psql (postgres=#)، قم بتشغيل:
|
||||
|
||||
```bash
|
||||
# قائمة الأدوار الموجودة في PostgreSQL
|
||||
\du
|
||||
```
|
||||
|
||||
سترى مخرجات مشابهة ل:
|
||||
|
||||
```bash
|
||||
اسم الأدوار | الخصائص | عضو في
|
||||
-----------+-------------+-----------
|
||||
john | مشرف نظام | {}
|
||||
```
|
||||
|
||||
إذا لم ترَ دور `postgres` مدرجًا، انتقل إلى الخطوة التالية.
|
||||
قم بإنشاء دور `postgres` يدويًا:
|
||||
|
||||
```bash
|
||||
CREATE ROLE postgres WITH SUPERUSER LOGIN;
|
||||
```
|
||||
|
||||
يقوم هذا بإنشاء دور مشرف نظام باسم `postgres` مع إمكانية تسجيل الدخول.
|
||||
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="ويندوز (WSL)">
|
||||
يجب أن تُنفذ جميع الخطوات التالية في تيرمينال WSL (داخل جهازك الافتراضي)
|
||||
|
||||
**الخيار 1:** لتوفير قاعدة بيانات Postgresql الخاصة بك محليًا:
|
||||
استخدم الرابط التالي لتثبيت Postgresql على جهاز Linux الافتراضي الخاص بك: [تثبيت Postgresql](https://www.postgresql.org/download/linux/)
|
||||
|
||||
```bash
|
||||
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
|
||||
```
|
||||
|
||||
ملاحظة: قد تحتاج إلى إضافة `sudo -u postgres` إلى الأمر قبل `psql` لتجنب أخطاء الإذن.
|
||||
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
تشغيل Docker على WSL يضيف طبقة إضافية من التعقيد.
|
||||
استخدم هذا الخيار فقط إذا كنت مرتاحًا مع الخطوات الإضافية المتضمنة، بما في ذلك تشغيل [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
|
||||
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
يمكنك الآن الوصول إلى قاعدة البيانات على [localhost:5432](localhost:5432)، مع المستخدم `postgres` وكلمة المرور `postgres`.
|
||||
|
||||
## الخطوة 4: إعداد قاعدة بيانات Redis (للتخزين المؤقت)
|
||||
|
||||
يتطلب Twenty مخزن بيانات Redis لتقديم أفضل أداء
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
**الخيار 1:** لتوفير Redis الخاص بك محليًا:
|
||||
استخدم الرابط التالي لتثبيت Redis على جهاز Linux: [تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
|
||||
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Mac OS">
|
||||
**الخيار 1 (المفضل):** لتوفير Redis الخاص بك محليًا مع `brew`:
|
||||
|
||||
```bash
|
||||
brew install redis
|
||||
```
|
||||
|
||||
ابدأ خادم redis الخاص بك:
|
||||
`brew services start redis`
|
||||
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="ويندوز (WSL)">
|
||||
**الخيار 1:** لتوفير Redis الخاص بك محليًا:
|
||||
استخدم الرابط التالي لتثبيت Redis على جهاز Linux الافتراضي الخاص بك: [تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
|
||||
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
إذا كنت بحاجة إلى واجهة رسومية للعميل، نوصي بـ [redis insight](https://redis.io/insight/) (يتوفر إصدار مجاني)
|
||||
|
||||
## الخطوة 5: إعداد متغيرات البيئة
|
||||
|
||||
استخدم متغيرات البيئة أو ملفات `.env` لتكوين مشروعك. المزيد من المعلومات [هنا](/l/ar/developers/self-host/capabilities/setup)
|
||||
|
||||
انسخ ملفات `.env.example` الموجودة في `/front` و`/server`:
|
||||
|
||||
```bash
|
||||
cp ./packages/twenty-front/.env.example ./packages/twenty-front/.env
|
||||
cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Multi-Workspace Mode:** By default, Twenty runs in single-workspace mode where only one workspace can be created. To enable multi-workspace support (useful for testing subdomain-based features), set `IS_MULTIWORKSPACE_ENABLED=true` in your server `.env` file. See [Multi-Workspace Mode](/l/ar/developers/self-host/capabilities/setup#multi-workspace-mode) for details.
|
||||
</Info>
|
||||
|
||||
## الخطوة 6: تثبيت التبعيات
|
||||
|
||||
لبناء خادم Twenty وزرع بعض البيانات في قاعدة البيانات الخاصة بك، قم بتشغيل الأمر التالي:
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
لاحظ أن `npm` أو `pnpm` لن تعملا
|
||||
|
||||
## الخطوة 7: تشغيل المشروع
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux">
|
||||
اعتمادًا على توزيعة Linux الخاصة بك، قد يتم بدء خادم Redis تلقائيًا.
|
||||
إذا لم يكن كذلك، تحقق من [دليل تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) لتوزيعتك.
|
||||
</Tab>
|
||||
|
||||
<Tab title="نظام Mac OS">
|
||||
من المفترض أن يكون Redis قد تم تشغيله بالفعل. إذا لم يكن كذلك، قم بتشغيل:
|
||||
|
||||
```bash
|
||||
brew services start redis
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="ويندوز (WSL)">
|
||||
اعتمادًا على توزيعة Linux الخاصة بك، قد يتم بدء خادم Redis تلقائيًا.
|
||||
إذا لم يكن كذلك، تحقق من [دليل تثبيت ريديس](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) لتوزيعتك.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
قم بضبط قاعدة بياناتك بالأمر التالي:
|
||||
|
||||
```bash
|
||||
npx nx database:reset twenty-server
|
||||
```
|
||||
|
||||
ابدأ الخادم والخادم الثانوي وخدمات الواجهة الأمامية:
|
||||
|
||||
```bash
|
||||
npx nx start twenty-server
|
||||
npx nx worker twenty-server
|
||||
npx nx start twenty-front
|
||||
```
|
||||
|
||||
بدلاً من ذلك، يمكنك بدء جميع الخدمات مرة واحدة:
|
||||
|
||||
```bash
|
||||
npx nx start
|
||||
```
|
||||
|
||||
## الخطوة الثامنة: استخدم Twenty
|
||||
|
||||
**الواجهة الأمامية**
|
||||
|
||||
ستكون واجهة Twenty الأمامية تعمل على [http://localhost:3001](http://localhost:3001).
|
||||
يمكنك تسجيل الدخول باستخدام حساب العرض التوضيحي الافتراضي: `tim@apple.dev` (كلمة المرور: `tim@apple.dev`)
|
||||
|
||||
**الخلفية**
|
||||
|
||||
* سيكون خادم Twenty متصلاً ويعمل على [http://localhost:3000](http://localhost:3000)
|
||||
* يمكن الوصول إلى واجهة برمجة التطبيقات GraphQL في [http://localhost:3000/graphql](http://localhost:3000/graphql)
|
||||
* يمكن الوصول إلى واجهة برمجة التطبيقات REST في [http://localhost:3000/rest](http://localhost:3000/rest)
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
إذا واجهت أي مشكلة، فارجع إلى [استكشاف الأخطاء وإصلاحها](/l/ar/developers/self-host/capabilities/troubleshooting) للحصول على الحلول.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Contribute
|
||||
description: Contribute to Twenty's open-source development.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/github/github-header.png" alt="الذكاء الاصطناعي" />
|
||||
</Frame>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty is open-source and welcomes contributions from the community. Whether you're fixing bugs, adding features, or improving documentation, your contributions help make Twenty better for everyone.
|
||||
|
||||
## Ways to Contribute
|
||||
|
||||
* **Report bugs**: Help identify and document issues
|
||||
* **Submit features**: Propose and implement new functionality
|
||||
* **Improve documentation**: Make our docs clearer and more helpful
|
||||
* **Frontend development**: Work on the React-based UI
|
||||
* **Backend development**: Contribute to the NestJS server
|
||||
|
||||
## البدء
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="Bug Reports & Requests" icon="bug" href="/l/ar/developers/contribute/capabilities/bug-and-requests">
|
||||
Report issues or request features
|
||||
</Card>
|
||||
|
||||
<Card title="تطوير الواجهة الأمامية" icon="browser" href="/l/ar/developers/contribute/capabilities/frontend-development">
|
||||
Contribute to the UI
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: واجهات برمجة التطبيقات
|
||||
description: Query and modify your CRM data programmatically using REST or GraphQL.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
تم تصميم Twenty ليكون صديقًا للمطورين، حيث يوفر واجهات برمجة قوية تتكيف مع نموذج البيانات المخصص. نحن نوفر أربعة أنواع متميزة من واجهات برمجة التطبيقات لتلبية احتياجات التكامل المختلفة.
|
||||
|
||||
## النموذج الأول للمطورين
|
||||
|
||||
Twenty generates APIs specifically for your data model:
|
||||
|
||||
* **لا حاجة إلى معرفات طويلة**: استخدم أسماء الكائنات والحقول مباشرة في نقاط النهاية
|
||||
* **معالجة متساوية للأشياء القياسية والمخصصة**: تحصل أشياؤك المخصصة على نفس معاملة واجهة برمجة التطبيقات كما هو الحال مع الأشياء المضمنة
|
||||
* **نقاط نهاية مخصصة**: يحصل كل كائن وحقل على نقطة نهاية API الخاصة به
|
||||
* **وثائق مخصصة**: يتم إنشاؤها خصيصًا لنموذج بيانات مساحة عملك
|
||||
|
||||
<Note>
|
||||
Your personalized API documentation is available under **Settings → API & Webhooks** after creating an API key. Since Twenty generates APIs that match your custom data model, the documentation is unique to your workspace.
|
||||
</Note>
|
||||
|
||||
## The Two API Types
|
||||
|
||||
### واجهة برمجة التطبيقات الأساسية
|
||||
|
||||
يتم الوصول إليها عبر `/rest/` أو `/graphql/`
|
||||
|
||||
Work with your actual **records** (the data):
|
||||
|
||||
* Create, read, update, delete People, Companies, Opportunities, etc.
|
||||
* Query and filter data
|
||||
* إدارة العلاقات بين السجلات
|
||||
|
||||
### واجهة برمجة البيانات الوصفية
|
||||
|
||||
يتم الوصول إليها عبر `/rest/metadata/` أو `/metadata/`
|
||||
|
||||
Manage your **workspace and data model**:
|
||||
|
||||
* إنشاء أو تعديل أو حذف الكائنات والحقول
|
||||
* تكوين إعدادات مساحة العمل
|
||||
* Define relationships between objects
|
||||
|
||||
## REST vs GraphQL
|
||||
|
||||
Both Core and Metadata APIs are available in REST and GraphQL formats:
|
||||
|
||||
| التنسيق | Available Operations |
|
||||
| ----------- | ---------------------------------------------------------- |
|
||||
| **REST** | CRUD, batch operations, upserts |
|
||||
| **GraphQL** | Same + **batch upserts**, relationship queries in one call |
|
||||
|
||||
Choose based on your needs — both formats access the same data.
|
||||
|
||||
## نقاط نهاية API
|
||||
|
||||
| Environment | Base URL |
|
||||
| --------------- | ------------------------- |
|
||||
| **Cloud** | `https://api.twenty.com/` |
|
||||
| **Self-Hosted** | `https://{your-domain}/` |
|
||||
|
||||
## المصادقة
|
||||
|
||||
Every API request requires an API key in the header:
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
### قم بإنشاء مفتاح API
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks**
|
||||
2. Click **+ Create key**
|
||||
3. Configure:
|
||||
* **Name**: Descriptive name for the key
|
||||
* **Expiration Date**: When the key expires
|
||||
4. انقر على **حفظ**
|
||||
5. **Copy immediately** — the key is only shown once
|
||||
|
||||
<VimeoEmbed videoId="928786722" title="Creating API key" />
|
||||
|
||||
<Warning>
|
||||
Your API key grants access to sensitive data. Don't share it with untrusted services. If compromised, disable it immediately and generate a new one.
|
||||
</Warning>
|
||||
|
||||
### Assign a Role to an API Key
|
||||
|
||||
For better security, assign a specific role to limit access:
|
||||
|
||||
1. اذهب إلى **الإعدادات → الأدوار**
|
||||
2. Click on the role to assign
|
||||
3. افتح علامة التبويب **التعيين**
|
||||
4. Under **API Keys**, click **+ Assign to API key**
|
||||
5. Select the API key
|
||||
|
||||
The key will inherit that role's permissions. See [Permissions](/l/ar/user-guide/permissions-access/capabilities/permissions) for details.
|
||||
|
||||
### إدارة مفاتيح API
|
||||
|
||||
**Regenerate**: Settings → APIs & Webhooks → Click key → **Regenerate**
|
||||
|
||||
**Delete**: Settings → APIs & Webhooks → Click key → **Delete**
|
||||
|
||||
## API Playground
|
||||
|
||||
Test your APIs directly in the browser with our built-in playground — available for both **REST** and **GraphQL**.
|
||||
|
||||
### Access the Playground
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks**
|
||||
2. Create an API key (required)
|
||||
3. Click on **REST API** or **GraphQL API** to open the playground
|
||||
|
||||
### What You Get
|
||||
|
||||
* **Interactive documentation**: Generated for your specific data model
|
||||
* **Live testing**: Execute real API calls against your workspace
|
||||
* **Schema explorer**: Browse available objects, fields, and relationships
|
||||
* **Request builder**: Construct queries with autocomplete
|
||||
|
||||
The playground reflects your custom objects and fields, so documentation is always accurate for your workspace.
|
||||
|
||||
## عمليات المجموعة
|
||||
|
||||
Both REST and GraphQL support batch operations:
|
||||
|
||||
* **حجم المجموعة**: حتى 60 سجل لكل طلب
|
||||
* **Operations**: Create, update, delete multiple records
|
||||
|
||||
**GraphQL-only features:**
|
||||
|
||||
* **Batch Upsert**: Create or update in one call
|
||||
* Use plural object names (e.g., `CreateCompanies` instead of `CreateCompany`)
|
||||
|
||||
## Rate Limits
|
||||
|
||||
API requests are throttled to ensure platform stability:
|
||||
|
||||
| Limit | القيمة |
|
||||
| -------------- | -------------------- |
|
||||
| **Requests** | 100 calls per minute |
|
||||
| **Batch size** | 60 records per call |
|
||||
|
||||
<Tip>
|
||||
Use batch operations to maximize throughput — process up to 60 records in a single API call instead of making individual requests.
|
||||
</Tip>
|
||||
@@ -0,0 +1,522 @@
|
||||
---
|
||||
title: Twenty Apps
|
||||
description: Build and manage Twenty customizations as code.
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Apps are currently in alpha testing. The feature is functional but still evolving.
|
||||
</Warning>
|
||||
|
||||
## What Are Apps?
|
||||
|
||||
Apps let you build and manage Twenty customizations **as code**. Instead of configuring everything through the UI, you define your data model and serverless functions in code — making it faster to build, maintain, and roll out to multiple workspaces.
|
||||
|
||||
**What you can do today:**
|
||||
|
||||
* Define custom objects and fields as code (managed data model)
|
||||
* Build serverless functions with custom triggers
|
||||
* Deploy the same app across multiple workspaces
|
||||
|
||||
**Coming soon:**
|
||||
|
||||
* Custom UI layouts and components
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Node.js 24+ and Yarn 4
|
||||
* A Twenty workspace and an API key (create one at https://app.twenty.com/settings/api-webhooks)
|
||||
|
||||
## البدء
|
||||
|
||||
Create a new app using the official scaffolder, then authenticate and start developing:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Scaffold a new app
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# Authenticate using your API key (you'll be prompted)
|
||||
yarn auth
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn dev
|
||||
```
|
||||
|
||||
من هنا يمكنك:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new entity to your application (guided)
|
||||
yarn create-entity
|
||||
|
||||
# Generate a typed Twenty client and workspace entity types
|
||||
yarn generate
|
||||
|
||||
# Run a one‑time sync (instead of watch mode)
|
||||
yarn sync
|
||||
|
||||
# Watch your application's functions logs
|
||||
yarn logs
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
yarn uninstall
|
||||
|
||||
# Display commands' help
|
||||
yarn help
|
||||
```
|
||||
|
||||
See also: the CLI reference pages for [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) and [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
|
||||
|
||||
## Project structure (scaffolded)
|
||||
|
||||
When you run `npx create-twenty-app@latest my-twenty-app`, the scaffolder:
|
||||
|
||||
* Copies a minimal base application into `my-twenty-app/`
|
||||
* Adds a local `twenty-sdk` dependency and Yarn 4 configuration
|
||||
* Creates config files and scripts wired to the `twenty` CLI
|
||||
* Generates a default application config and a default function role
|
||||
|
||||
A freshly scaffolded app looks like this:
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
package.json
|
||||
yarn.lock
|
||||
.gitignore
|
||||
.nvmrc
|
||||
.yarnrc.yml
|
||||
.yarn/
|
||||
releases/
|
||||
yarn-4.9.2.cjs
|
||||
install-state.gz
|
||||
eslint.config.mjs
|
||||
tsconfig.json
|
||||
README.md
|
||||
src/
|
||||
application.config.ts
|
||||
role.config.ts
|
||||
// your entities, actions, and other app files
|
||||
```
|
||||
|
||||
At a high level:
|
||||
|
||||
* **package.json**: Declares the app name, version, engines (Node 24+, Yarn 4), and adds `twenty-sdk` plus scripts like `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall`, and `auth` that delegate to the local `twenty` CLI.
|
||||
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
|
||||
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
|
||||
* **.nvmrc**: Pins the Node.js version expected by the project.
|
||||
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app’s TypeScript sources.
|
||||
* **README.md**: A short README in the app root with basic instructions.
|
||||
* **src/**: The main place where you define your application-as-code:
|
||||
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See “Application config” below.
|
||||
* `role.config.ts`: Default function role used by your serverless functions. See “Default function role” below.
|
||||
* Future entities, actions/functions, and any supporting code you add.
|
||||
|
||||
Later commands will add more files and folders:
|
||||
|
||||
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
|
||||
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
|
||||
|
||||
## المصادقة
|
||||
|
||||
The first time you run `yarn auth`, you'll be prompted for:
|
||||
|
||||
* API URL (defaults to http://localhost:3000 or your current workspace profile)
|
||||
* API key
|
||||
|
||||
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch using `--workspace <name>`.
|
||||
|
||||
الأمثلة:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Login interactively (recommended)
|
||||
yarn auth
|
||||
|
||||
# Use a specific workspace profile
|
||||
yarn auth --workspace my-custom-workspace
|
||||
```
|
||||
|
||||
## Use the SDK resources (types & config)
|
||||
|
||||
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
|
||||
|
||||
### Defining objects
|
||||
|
||||
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
|
||||
|
||||
Here is an example `postCard` object from the Hello World app:
|
||||
|
||||
```typescript
|
||||
import { type Note } from '../../generated';
|
||||
|
||||
import {
|
||||
type AddressField,
|
||||
Field,
|
||||
FieldType,
|
||||
type FullNameField,
|
||||
Object,
|
||||
OnDeleteAction,
|
||||
Relation,
|
||||
RelationType,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk';
|
||||
|
||||
enum PostCardStatus {
|
||||
DRAFT = 'DRAFT',
|
||||
SENT = 'SENT',
|
||||
DELIVERED = 'DELIVERED',
|
||||
RETURNED = 'RETURNED',
|
||||
}
|
||||
|
||||
@Object({
|
||||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||||
nameSingular: 'postCard',
|
||||
namePlural: 'postCards',
|
||||
labelSingular: 'Post card',
|
||||
labelPlural: 'Post cards',
|
||||
description: ' A post card object',
|
||||
icon: 'IconMail',
|
||||
})
|
||||
export class PostCard {
|
||||
@Field({
|
||||
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
|
||||
type: FieldType.TEXT,
|
||||
label: 'Content',
|
||||
description: "Postcard's content",
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
content: string;
|
||||
|
||||
@Field({
|
||||
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
|
||||
type: FieldType.FULL_NAME,
|
||||
label: 'Recipient name',
|
||||
icon: 'IconUser',
|
||||
})
|
||||
recipientName: FullNameField;
|
||||
|
||||
@Field({
|
||||
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
|
||||
type: FieldType.ADDRESS,
|
||||
label: 'Recipient address',
|
||||
icon: 'IconHome',
|
||||
})
|
||||
recipientAddress: AddressField;
|
||||
|
||||
@Field({
|
||||
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
|
||||
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' },
|
||||
],
|
||||
})
|
||||
status: PostCardStatus;
|
||||
|
||||
@Relation({
|
||||
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
|
||||
type: RelationType.ONE_TO_MANY,
|
||||
label: 'Notes',
|
||||
icon: 'IconComment',
|
||||
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
|
||||
onDelete: OnDeleteAction.CASCADE,
|
||||
})
|
||||
notes: Note[];
|
||||
|
||||
@Field({
|
||||
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
|
||||
type: FieldType.DATE_TIME,
|
||||
label: 'Delivered at',
|
||||
icon: 'IconCheck',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
})
|
||||
deliveredAt?: Date;
|
||||
}
|
||||
```
|
||||
|
||||
Key points:
|
||||
|
||||
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
|
||||
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
|
||||
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
|
||||
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
|
||||
|
||||
### Application config (application.config.ts)
|
||||
|
||||
Every app has a single `application.config.ts` file that describes:
|
||||
|
||||
* **Who the app is**: identifiers, display name, and description.
|
||||
* **How its functions run**: which role they use for permissions.
|
||||
* **(Optional) variables**: key–value pairs exposed to your functions as environment variables.
|
||||
|
||||
When you scaffold a new app, you start with a minimal config:
|
||||
|
||||
```typescript
|
||||
import { type ApplicationConfig } from 'twenty-sdk';
|
||||
|
||||
const config: ApplicationConfig = {
|
||||
universalIdentifier: '<generated-app-uuid>',
|
||||
displayName: 'My Twenty App',
|
||||
description: 'My first Twenty app',
|
||||
functionRoleUniversalIdentifier: '<generated-role-uuid>',
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
|
||||
|
||||
```typescript
|
||||
import { type ApplicationConfig } from 'twenty-sdk';
|
||||
|
||||
const config: ApplicationConfig = {
|
||||
universalIdentifier: '<your-app-uuid>',
|
||||
displayName: 'My App',
|
||||
description: 'What your app does',
|
||||
icon: 'IconWorld', // Choose an icon by name
|
||||
applicationVariables: {
|
||||
DEFAULT_RECIPIENT_NAME: {
|
||||
universalIdentifier: '<uuid>',
|
||||
description: 'Default recipient used by functions',
|
||||
value: 'Jane Doe',
|
||||
isSecret: false,
|
||||
},
|
||||
},
|
||||
functionRoleUniversalIdentifier: '<your-role-uuid>',
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
|
||||
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
|
||||
|
||||
#### Roles and permissions
|
||||
|
||||
Applications can define roles that encapsulate permissions on your workspace’s objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app’s serverless functions.
|
||||
|
||||
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
|
||||
* The typed client will be restricted to the permissions granted to that role.
|
||||
* Follow least‑privilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
|
||||
|
||||
##### Default function role (role.config.ts)
|
||||
|
||||
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
|
||||
|
||||
```typescript
|
||||
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
|
||||
|
||||
export const functionRole: RoleConfig = {
|
||||
universalIdentifier: '<generated-role-uuid>',
|
||||
label: 'My Twenty App default function role',
|
||||
description: 'My Twenty App default function role',
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: false,
|
||||
};
|
||||
```
|
||||
|
||||
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
|
||||
|
||||
* **role.config.ts** defines what the default function role can do.
|
||||
* **application.config.ts** points to that role so your functions inherit its permissions.
|
||||
|
||||
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
|
||||
|
||||
```typescript
|
||||
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
|
||||
|
||||
export const functionRole: RoleConfig = {
|
||||
universalIdentifier: '<your-role-uuid>',
|
||||
label: 'Default function role',
|
||||
description: 'Default role for function Twenty client',
|
||||
canReadAllObjectRecords: false,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canUpdateAllSettings: false,
|
||||
canBeAssignedToAgents: false,
|
||||
canBeAssignedToUsers: false,
|
||||
canBeAssignedToApiKeys: false,
|
||||
objectPermissions: [
|
||||
{
|
||||
objectNameSingular: 'postCard',
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: true,
|
||||
canSoftDeleteObjectRecords: false,
|
||||
canDestroyObjectRecords: false,
|
||||
},
|
||||
],
|
||||
fieldPermissions: [
|
||||
{
|
||||
objectNameSingular: 'postCard',
|
||||
fieldName: 'content',
|
||||
canReadFieldValue: false,
|
||||
canUpdateFieldValue: false,
|
||||
},
|
||||
],
|
||||
permissionFlags: ['APPLICATIONS'],
|
||||
};
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* Start from the scaffolded role, then progressively restrict it following least‑privilege.
|
||||
* Replace the `objectPermissions` and `fieldPermissions` with the objects/fields your functions need.
|
||||
* `permissionFlags` control access to platform-level capabilities. Keep them minimal; add only what you need.
|
||||
* See a working example in the Hello World app: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
|
||||
|
||||
### Serverless function config and entrypoint
|
||||
|
||||
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
|
||||
|
||||
```typescript
|
||||
// src/actions/create-new-post-card.ts
|
||||
import type {
|
||||
FunctionConfig,
|
||||
DatabaseEventPayload,
|
||||
ObjectRecordCreateEvent,
|
||||
CronPayload,
|
||||
} from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '../generated';
|
||||
|
||||
// main handler can accept parameters from route, cron, or database events
|
||||
export const main = async (
|
||||
params:
|
||||
| { name?: string }
|
||||
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
|
||||
| CronPayload,
|
||||
) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const name = 'name' in params
|
||||
? params.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
|
||||
const result = await client.mutation({
|
||||
createPostCard: {
|
||||
__args: { data: { name } },
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
export const config: FunctionConfig = {
|
||||
universalIdentifier: '<function-uuid>',
|
||||
name: 'create-new-post-card',
|
||||
timeoutSeconds: 2,
|
||||
triggers: [
|
||||
// Public HTTP route trigger '/s/post-card/create'
|
||||
{
|
||||
universalIdentifier: '<route-trigger-uuid>',
|
||||
type: 'route',
|
||||
path: '/post-card/create',
|
||||
httpMethod: 'GET',
|
||||
isAuthRequired: false,
|
||||
},
|
||||
// Cron trigger (CRON pattern)
|
||||
{
|
||||
universalIdentifier: '<cron-trigger-uuid>',
|
||||
type: 'cron',
|
||||
pattern: '0 0 1 1 *',
|
||||
},
|
||||
// Database event trigger
|
||||
{
|
||||
universalIdentifier: '<db-trigger-uuid>',
|
||||
type: 'databaseEvent',
|
||||
eventName: 'person.created',
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
Common trigger types:
|
||||
|
||||
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
|
||||
|
||||
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
|
||||
|
||||
* cron: Runs your function on a schedule using a CRON expression.
|
||||
* databaseEvent: Runs on workspace object lifecycle events
|
||||
|
||||
> e.g. `person.created`
|
||||
|
||||
You can create new functions in two ways:
|
||||
|
||||
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
|
||||
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
|
||||
|
||||
### Generated typed client
|
||||
|
||||
Run yarn generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
|
||||
|
||||
```typescript
|
||||
import Twenty from './generated';
|
||||
|
||||
const client = new Twenty();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
The client is re-generated by `yarn generate`. Re-run after changing your objects and `yarn sync` or when onboarding to a new workspace.
|
||||
|
||||
#### Runtime credentials in serverless functions
|
||||
|
||||
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
|
||||
|
||||
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
|
||||
* `TWENTY_API_KEY`: Short‑lived key scoped to your application’s default function role.
|
||||
|
||||
Notes:
|
||||
|
||||
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
|
||||
* The API key’s permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
|
||||
* Applications can define roles to follow least‑privilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role’s universal identifier.
|
||||
|
||||
### Hello World example
|
||||
|
||||
Explore a minimal, end-to-end example that demonstrates objects, functions, and multiple triggers [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world):
|
||||
|
||||
## Manual setup (without the scaffolder)
|
||||
|
||||
While we recommend using `create-twenty-app` for the best getting-started experience, you can also set up a project manually. Do not install the CLI globally. Instead, add `twenty-sdk` as a local dependency and wire scripts in your package.json:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add -D twenty-sdk
|
||||
```
|
||||
|
||||
Then add scripts like these:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"scripts": {
|
||||
"auth": "twenty auth login",
|
||||
"generate": "twenty app generate",
|
||||
"dev": "twenty app dev",
|
||||
"sync": "twenty app sync",
|
||||
"uninstall": "twenty app uninstall",
|
||||
"logs": "twenty app logs",
|
||||
"create-entity": "twenty app add",
|
||||
"help": "twenty --help"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you can run the same commands via Yarn, e.g. `yarn dev`, `yarn sync`, etc.
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
* Authentication errors: run `yarn auth` and ensure your API key has the required permissions.
|
||||
* Cannot connect to server: verify the API URL and that the Twenty server is reachable.
|
||||
* Types or client missing/outdated: run `yarn generate` and then `yarn dev`.
|
||||
* Dev mode not syncing: ensure `yarn dev` is running and that changes are not ignored by your environment.
|
||||
|
||||
Discord Help Channel: https://discord.com/channels/1130383047699738754/1130386664812982322
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: الويب هوكس
|
||||
description: Receive real-time notifications when events occur in your CRM.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
Webhooks push data to your systems in real-time when events occur in Twenty — no polling required. Use them to keep external systems in sync, trigger automations, or send alerts.
|
||||
|
||||
## إنشاء ربط ويب
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks → Webhooks**
|
||||
2. انقر على **+ إنشاء ربط ويب**
|
||||
3. Enter your webhook URL (must be publicly accessible)
|
||||
4. انقر على **حفظ**
|
||||
|
||||
The webhook activates immediately and starts sending notifications.
|
||||
|
||||
<VimeoEmbed videoId="928786708" title="Creating a webhook" />
|
||||
|
||||
### إدارة Webhooks
|
||||
|
||||
**Edit**: Click the webhook → Update URL → **Save**
|
||||
|
||||
**Delete**: Click the webhook → **Delete** → Confirm
|
||||
|
||||
## الأحداث
|
||||
|
||||
Twenty sends webhooks for these event types:
|
||||
|
||||
| حدث | مثال |
|
||||
| ------------------ | ---------------------------------------------------------- |
|
||||
| **Record Created** | `person.created`, `company.created`, `note.created` |
|
||||
| **Record Updated** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **Record Deleted** | `person.deleted`, `company.deleted` |
|
||||
|
||||
All event types are sent to your webhook URL. Event filtering may be added in future releases.
|
||||
|
||||
## Payload Format
|
||||
|
||||
Each webhook sends an HTTP POST with a JSON body:
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
| الحقل | الوصف |
|
||||
| --------------- | ------------------------------------------------ |
|
||||
| `حدث` | What happened (e.g., `person.created`) |
|
||||
| `بيانات` | The full record that was created/updated/deleted |
|
||||
| `الطابع الزمني` | When the event occurred (UTC) |
|
||||
|
||||
<Note>
|
||||
Respond with a **2xx HTTP status** (200-299) to acknowledge receipt. Non-2xx responses are logged as delivery failures.
|
||||
</Note>
|
||||
|
||||
## Webhook Validation
|
||||
|
||||
Twenty signs each webhook request for security. Validate signatures to ensure requests are authentic.
|
||||
|
||||
### Headers
|
||||
|
||||
| رأس الصفحة | الوصف |
|
||||
| ---------------------------- | --------------------- |
|
||||
| `X-Twenty-Webhook-Signature` | HMAC SHA256 signature |
|
||||
| `X-Twenty-Webhook-Timestamp` | Request timestamp |
|
||||
|
||||
### Validation Steps
|
||||
|
||||
1. Get the timestamp from `X-Twenty-Webhook-Timestamp`
|
||||
2. Create the string: `{timestamp}:{JSON payload}`
|
||||
3. Compute HMAC SHA256 using your webhook secret
|
||||
4. Compare with `X-Twenty-Webhook-Signature`
|
||||
|
||||
### Example (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 isValid = expectedSignature === req.headers["x-twenty-webhook-signature"];
|
||||
```
|
||||
|
||||
## Webhooks vs Workflows
|
||||
|
||||
| طريقة | الاتجاه | Use Case |
|
||||
| ---------------------------- | ------- | ---------------------------------------------------------- |
|
||||
| **Webhooks** | OUT | Automatically notify external systems of any record change |
|
||||
| **Workflow + HTTP Request** | OUT | Send data out with custom logic (filters, transformations) |
|
||||
| **Workflow Webhook Trigger** | IN | Receive data into Twenty from external systems |
|
||||
|
||||
For receiving external data, see [Set Up a Webhook Trigger](/l/ar/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Extend
|
||||
description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/integrations/plug.png" alt="الذكاء الاصطناعي" />
|
||||
</Frame>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty is designed to be extensible. Use our APIs, webhooks, and app framework to integrate with your existing tools and build custom functionality.
|
||||
|
||||
## What You Can Do
|
||||
|
||||
* **APIs**: Query and modify your CRM data programmatically using REST or GraphQL
|
||||
* **Webhooks**: Receive real-time notifications when events occur in Twenty
|
||||
* **Apps**: Build custom applications that extend Twenty's capabilities - Coming soon!
|
||||
|
||||
## البدء
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="واجهات برمجة التطبيقات" icon="كود" href="/l/ar/developers/extend/capabilities/apis">
|
||||
Connect to Twenty programmatically
|
||||
</Card>
|
||||
|
||||
<Card title="الويب هوكس" icon="bell" href="/l/ar/developers/extend/capabilities/webhooks">
|
||||
Get notified of events in real-time
|
||||
</Card>
|
||||
|
||||
<Card title="Apps" icon="puzzle-piece" href="/l/ar/developers/extend/capabilities/apps">
|
||||
Build customizations as code (Alpha)
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: البدء
|
||||
description: Welcome to Twenty Developer Documentation, your resources for extending, self-hosting, and contributing to Twenty.
|
||||
---
|
||||
|
||||
import { CardTitle } from "/snippets/card-title.mdx"
|
||||
|
||||
<CardGroup cols={٣}>
|
||||
<Card href="/l/ar/developers/extend/extend" img="/images/user-guide/integrations/plug.png">
|
||||
<CardTitle>Extend</CardTitle>
|
||||
Build integrations with APIs, webhooks, and custom apps.
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/developers/self-host/self-host" img="/images/user-guide/what-is-twenty/20.png">
|
||||
<CardTitle>Self-Host</CardTitle>
|
||||
Deploy and manage Twenty on your own infrastructure.
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/developers/contribute/contribute" img="/images/user-guide/github/github-header.png">
|
||||
<CardTitle>Contribute</CardTitle>
|
||||
Join our open-source community and contribute to Twenty.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: طرق أخرى
|
||||
---
|
||||
|
||||
<Warning>
|
||||
هذا المستند يُحافظ عليه من قبل المجتمع. قد يحتوي على مشكلات.
|
||||
</Warning>
|
||||
|
||||
## Kubernetes عبر Terraform والمخططات
|
||||
|
||||
Community-led documentation for Kubernetes deployment is available [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-docker/k8s)
|
||||
|
||||
### Coolify
|
||||
|
||||
نشر Twenty على الخوادم باستخدام Coolify. (الصورة الرسمية على Coolify ستكون متاحة قريبًا)
|
||||
|
||||
[توثيق Coolify](https://coolify.io/docs/get-started/introduction)
|
||||
|
||||
### EasyPanel
|
||||
|
||||
نشر Twenty على EasyPanel مع القالب الذي يُحافظ عليه المجتمع أدناه.
|
||||
|
||||
[نشر على EasyPanel](https://easypanel.io/docs/templates/twenty)
|
||||
|
||||
### Elest.io
|
||||
|
||||
نشر Twenty على الخوادم باستخدام Elest.io عبر الرابط التالي.
|
||||
|
||||
[نشر على Elest.io](https://elest.io/open-source/twenty)
|
||||
|
||||
### Twenty على Railway
|
||||
|
||||
نشر Twenty على Railway مع القالب الذي يُحافظ عليه المجتمع أدناه.
|
||||
|
||||
[](https://railway.com/deploy/nAL3hA)
|
||||
|
||||
### Twenty على Sealos
|
||||
|
||||
انشر Twenty على Sealos باستخدام القالب الذي تتم صيانته من قِبل المجتمع أدناه.
|
||||
|
||||
[](https://sealos.io/products/app-store/twenty)
|
||||
|
||||
## أخرى
|
||||
|
||||
Please feel free to Open a PR to add more Cloud Provider options.
|
||||
@@ -0,0 +1,252 @@
|
||||
---
|
||||
title: 1-Click w/ Docker Compose
|
||||
---
|
||||
|
||||
<Warning>
|
||||
الحاويات الخاصة بدوكر مخصصة للاستضافة الإنتاجية أو الاستضافة الذاتية، للتحقيق يرجى التحقق من [الإعداد المحلي](/l/ar/developers/contribute/capabilities/local-setup).
|
||||
</Warning>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يوفر هذا الدليل تعليمات خطوة بخطوة لتثبيت وتكوين تطبيق "Twenty" باستخدام Docker Compose. الهدف هو جعل العملية مباشرة ومنع المشاكل الشائعة التي يمكن أن تؤدي إلى تعطيل إعدادك.
|
||||
|
||||
**مهم:** عدّل الإعدادات المذكورة صراحة في هذا الدليل فقط. قد يؤدي تعديل التكوينات الأخرى إلى مشاكل.
|
||||
|
||||
راجع المستندات الخاصة بـ [إعداد متغيرات البيئة](/l/ar/developers/self-host/capabilities/setup) لإعداد متقدم. يجب إعلان جميع متغيرات البيئة في الملف docker-compose.yml على مستوى الخادم و/أو العامل بناءً على المتغير.
|
||||
|
||||
## متطلبات النظام
|
||||
|
||||
* رام: تأكد من أن بيئتك تحتوي على ما لا يقل عن 2GB من ذاكرة الرام. قد يؤدي عدم كفاية الذاكرة إلى تعطل العمليات.
|
||||
* دوكر ودوكر كومبوز: تأكد من تثبيت كلاهما وتحديثهما.
|
||||
|
||||
## الخيار 1: نص سطر واحد
|
||||
|
||||
قم بتثبيت أحدث إصدار مستقر من Twenty باستخدام أمر واحد:
|
||||
|
||||
```bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/scripts/install.sh)
|
||||
```
|
||||
|
||||
لتثبيت إصدار محدد أو فرع:
|
||||
|
||||
```bash
|
||||
VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/scripts/install.sh)
|
||||
```
|
||||
|
||||
* استبدل x.y.z برقم الإصدار المطلوب.
|
||||
* استبدل branch-name بالاسم الفرعي المطلوب تثبيته.
|
||||
|
||||
## الخيار 2: خطوات يدوية
|
||||
|
||||
اتبع هذه الخطوات لإعداد يدوي.
|
||||
|
||||
### الخطوة 1: إعداد ملف البيئة
|
||||
|
||||
1. **إنشاء ملف .env**
|
||||
|
||||
Copy the example environment file to a new .env file in your working directory:
|
||||
|
||||
```bash
|
||||
curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example
|
||||
```
|
||||
|
||||
2. **إنشاء رموز سرية**
|
||||
|
||||
قم بتشغيل الأمر التالي لإنشاء سلسلة عشوائية فريدة:
|
||||
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
**مهم:** احتفظ بهذه القيمة سرية ولا تشاركها.
|
||||
|
||||
3. **تحديث الـ `.env`**
|
||||
|
||||
استبدل قيمة النائب في ملف .env بالقيمة الرمزية المولدة:
|
||||
|
||||
```ini
|
||||
APP_SECRET=first_random_string
|
||||
```
|
||||
|
||||
4. **تعيين كلمة مرور PostgreSQL**
|
||||
|
||||
قم بتحديث قيمة `PG_DATABASE_PASSWORD` في ملف .env باستخدام كلمة مرور قوية بدون أحرف خاصة.
|
||||
|
||||
```ini
|
||||
PG_DATABASE_PASSWORD=my_strong_password
|
||||
```
|
||||
|
||||
### الخطوة 2: الحصول على ملف Docker Compose
|
||||
|
||||
قم بتنزيل ملف `docker-compose.yml` إلى دليل العمل الخاص بك:
|
||||
|
||||
```bash
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/docker-compose.yml
|
||||
```
|
||||
|
||||
### الخطوة 3: إطلاق التطبيق
|
||||
|
||||
Start the Docker containers:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### الخطوة 4: الوصول إلى التطبيق
|
||||
|
||||
If you host twentyCRM on your own computer, open your browser and navigate to [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
If you host it on a server, check that the server is running and that everything is ok with
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000
|
||||
```
|
||||
|
||||
## التكوين
|
||||
|
||||
### جعل Twenty متاحاً للوصول الخارجي
|
||||
|
||||
افتراضياً، يعمل Twenty على `localhost` على المنفذ `3000`. للوصول إليه عبر نطاق خارجي أو عنوان IP، تحتاج إلى تكوين `SERVER_URL` في ملف `.env`.
|
||||
|
||||
#### فهم `SERVER_URL`
|
||||
|
||||
* **البروتوكول:** استخدم `http` أو `https` حسب الإعداد الخاص بك.
|
||||
* استخدم `http` إذا لم تقم بإعداد SSL.
|
||||
* استخدم `https` إذا كان لديك SSL تم تكوينه.
|
||||
* **النطاق/الآي بي:** هذا هو النطاق أو عنوان IP حيث يكون تطبيقك متاحاً.
|
||||
* **المنفذ:** اشمل رقم المنفذ إذا لم تكن تستخدم المنافذ الافتراضية (`80` لـ `http`, `443` لـ `https`).
|
||||
|
||||
### متطلبات SSL
|
||||
|
||||
SSL (HTTPS) مطلوب لعمل ميزات معينة في المتصفح بشكل صحيح. بينما قد تعمل هذه الميزات أثناء التطوير المحلي (بما أن المتصفحات تعامل localhost بشكل مختلف)، هناك حاجة إلى إعداد SSL صحيح عند استضافة Twenty على نطاق عادي.
|
||||
|
||||
على سبيل المثال، قد يطلب API للحافظة السياق الآمن - بعض الميزات مثل أزرار النسخ في جميع أنحاء التطبيق قد لا تعمل بدون تمكين HTTPS.
|
||||
|
||||
نوصي بشدة بإعداد Twenty خلف وكيل عكسي مع إنهاء SSL للأمان والوظيفية المثلى.
|
||||
|
||||
#### تكوين `SERVER_URL`
|
||||
|
||||
1. **حدد عنوان وصولك**
|
||||
* **بدون وكيل عكسي (الوصول المباشر):**
|
||||
|
||||
إذا كنت تصل إلى التطبيق مباشرة بدون وكيل عكسي:
|
||||
|
||||
```ini
|
||||
SERVER_URL=http://your-domain-or-ip:3000
|
||||
```
|
||||
|
||||
* **مع وكيل عكسي (المنافذ القياسية):**
|
||||
|
||||
إذا كنت تستخدم وكيل عكسي مثل Nginx أو Traefik وكان لديك SSL تم تكوينه:
|
||||
|
||||
```ini
|
||||
SERVER_URL=https://your-domain-or-ip
|
||||
```
|
||||
|
||||
* **مع وكيل عكسي (منافذ مخصصة):**
|
||||
|
||||
إذا كنت تستخدم منافذ غير قياسية:
|
||||
|
||||
```ini
|
||||
SERVER_URL=https://your-domain-or-ip:custom-port
|
||||
```
|
||||
|
||||
2. **تحديث ملف `.env`**
|
||||
|
||||
افتح ملف `.env` وقم بتحديث `SERVER_URL`:
|
||||
|
||||
```ini
|
||||
SERVER_URL=http(s)://your-domain-or-ip:your-port
|
||||
```
|
||||
|
||||
**أمثلة:**
|
||||
|
||||
* الوصول المباشر بدون SSL:
|
||||
```ini
|
||||
SERVER_URL=http://123.45.67.89:3000
|
||||
```
|
||||
* الوصول عبر النطاق باستخدام SSL:
|
||||
```ini
|
||||
SERVER_URL=https://mytwentyapp.com
|
||||
```
|
||||
|
||||
3. **إعادة تشغيل التطبيق**
|
||||
|
||||
لتطبيق التغييرات، أعد تشغيل حاويات Docker:
|
||||
|
||||
```bash
|
||||
docker compose down\ndocker compose up -d
|
||||
```
|
||||
|
||||
#### اعتبارات
|
||||
|
||||
* **تكوين الوكيل العكسي:**
|
||||
|
||||
تأكد من أن الوكيل العكسي يوجه الطلبات إلى المنفذ الداخلي الصحيح (افتراضيًا `3000`). قم بتكوين انتهاء SSL وأي ترويسات مطلوبة.
|
||||
|
||||
* **إعدادات الجدار الناري:**
|
||||
|
||||
Open necessary ports in your firewall to allow external access.
|
||||
|
||||
* **التناسق:**
|
||||
|
||||
يجب أن يتطابق `SERVER_URL` مع طريقة وصول المستخدمين إلى تطبيقك في متصفحاتهم.
|
||||
|
||||
#### Persistence
|
||||
|
||||
* **أحجام البيانات:**
|
||||
|
||||
The Docker Compose configuration uses volumes to persist data for the database and server storage.
|
||||
|
||||
* **البيئات غير المرتبطة بالحالة:**
|
||||
|
||||
إذا كنت تقوم بالنشر إلى بيئة غير مرتبطة بالحالة (مثل بعض خدمات السحابة)، فقم بتكوين التخزين الخارجي لحفظ البيانات.
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
Regular backups protect your CRM data from loss.
|
||||
|
||||
### Create a Database Backup
|
||||
|
||||
```bash
|
||||
docker exec twenty-postgres pg_dump -U postgres twenty > backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
### Automate Daily Backups
|
||||
|
||||
Add to your crontab (`crontab -e`):
|
||||
|
||||
```bash
|
||||
0 2 * * * docker exec twenty-postgres pg_dump -U postgres twenty > /backups/twenty_$(date +\%Y\%m\%d).sql
|
||||
```
|
||||
|
||||
### Restore from Backup
|
||||
|
||||
1. Stop the application:
|
||||
|
||||
```bash
|
||||
docker compose stop twenty-server twenty-front
|
||||
```
|
||||
|
||||
2. Restore the database:
|
||||
|
||||
```bash
|
||||
docker exec -i twenty-postgres psql -U postgres twenty < backup_20240115.sql
|
||||
```
|
||||
|
||||
3. Restart services:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Backup Best Practices
|
||||
|
||||
* **Test restores regularly** — verify backups actually work
|
||||
* **Store backups off-site** — use cloud storage (S3, GCS, etc.)
|
||||
* **Encrypt sensitive data** — protect backups with encryption
|
||||
* **Retain multiple copies** — keep daily, weekly, and monthly backups
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
إذا واجهت أي مشكلة، فارجع إلى [استكشاف الأخطاء وإصلاحها](/l/ar/developers/self-host/capabilities/troubleshooting) للحصول على الحلول.
|
||||
@@ -0,0 +1,293 @@
|
||||
---
|
||||
title: إعداد
|
||||
---
|
||||
|
||||
# إدارة الإعدادات
|
||||
|
||||
<Warning>
|
||||
**هل هي المرة الأولى التي تقوم فيها بالتثبيت؟** اتبع [دليل تثبيت Docker Compose](/l/ar/developers/self-host/capabilities/docker-compose) لتشغيل Twenty، ثم عد هنا للإعداد.
|
||||
</Warning>
|
||||
|
||||
يوفر Twenty **وضعين للإعداد** ليلائم احتياجات النشر المختلفة:
|
||||
|
||||
**الوصول إلى لوحة الإدارة:** يمكن للمستخدمين الذين لديهم صلاحيات المسؤول (`canAccessFullAdminPanel: true`) فقط الوصول إلى واجهة التكوين.
|
||||
|
||||
## 1. إعداد لوحة الإدارة (افتراضي)
|
||||
|
||||
```bash
|
||||
IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # افتراضي
|
||||
```
|
||||
|
||||
**يحدث أغلب التكوين عبر واجهة المستخدم** بعد التثبيت:
|
||||
|
||||
1. الوصول إلى نسخة Twenty الخاصة بك (عادة `http://localhost:3000`)
|
||||
2. اذهب إلى **الإعدادات / لوحة الإدارة / متغيرات التكوين**
|
||||
3. اضبط التكاملات، والبريد الإلكتروني، والتخزين، والمزيد
|
||||
4. تسري التغييرات على الفور (خلال 15 ثانية لعمليات النشر متعددة الحاويات)
|
||||
|
||||
<Warning>
|
||||
**نشرات متعددة الحاويات:** عند استخدام إعدادات قاعدة البيانات (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`)، يقوم كل من حاويات الخادم والعامل بالقراءة من نفس قاعدة البيانات. التغييرات في لوحة الإدارة تؤثر عليهما تلقائيًا، مما يلغي الحاجة إلى تكرار متغيرات البيئة بين الحاويات (باستثناء متغيرات البنية التحتية).
|
||||
</Warning>
|
||||
|
||||
**ما يمكنك تكوينه عبر لوحة الإدارة:**
|
||||
|
||||
* **المصادقة** - Google/Microsoft OAuth، إعدادات كلمة المرور
|
||||
* **البريد الإلكتروني** - إعدادات SMTP، القوالب، التحقق
|
||||
* **التخزين** - تكوين S3، مسارات التخزين المحلية
|
||||
* **التكاملات** - Gmail، تقويم Google، خدمات Microsoft
|
||||
* **حدود التشغيل ومعدل التكنولوجيا** - حدود التنفيذ، API الحد من السرعة
|
||||
* **والمزيد من الأمور...**
|
||||
|
||||

|
||||
|
||||
<Warning>
|
||||
كل متغير موثق بوصف في لوحة الإدارة الخاصة بك في **الإعدادات → لوحة الإدارة → متغيرات التكوين**.
|
||||
بعض إعدادات البنية التحتية مثل اتصالات قاعدة البيانات (`PG_DATABASE_URL`)، عناوين الخوادم (`SERVER_URL`)، وأسرار التطبيقات (`APP_SECRET`) يمكن ضبطها فقط عبر ملف `.env`.
|
||||
|
||||
[مرجع تقني كامل →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts)
|
||||
</Warning>
|
||||
|
||||
## 2. إعداد بيئي فقط
|
||||
|
||||
```bash
|
||||
IS_CONFIG_VARIABLES_IN_DB_ENABLED=false
|
||||
```
|
||||
|
||||
**جميع الإعدادات تتم إدارتها عبر ملفات `.env`:**
|
||||
|
||||
1. قم بتعيين `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false` في ملف `.env` الخاص بك
|
||||
2. أضف جميع متغيرات الإعداد إلى ملف `.env` الخاص بك
|
||||
3. أعد تشغيل الحاويات لتصبح التغييرات نافذة
|
||||
4. ستظهر لوحة الإدارة القيم الحالية لكنها لن تتمكن من تعديلها
|
||||
|
||||
## Multi-Workspace Mode
|
||||
|
||||
By default, Twenty runs in **single-workspace mode** — ideal for most self-hosted deployments where you need one CRM instance for your organization.
|
||||
|
||||
### Single-Workspace Mode (Default)
|
||||
|
||||
```bash
|
||||
IS_MULTIWORKSPACE_ENABLED=false # default
|
||||
```
|
||||
|
||||
* One workspace per Twenty instance
|
||||
* First user automatically becomes admin with full privileges (`canImpersonate` and `canAccessFullAdminPanel`)
|
||||
* New signups are disabled after the first workspace is created
|
||||
* Simple URL structure: `https://your-domain.com`
|
||||
|
||||
### Enabling Multi-Workspace Mode
|
||||
|
||||
```bash
|
||||
IS_MULTIWORKSPACE_ENABLED=true
|
||||
DEFAULT_SUBDOMAIN=app # default value
|
||||
```
|
||||
|
||||
Enable multi-workspace mode for SaaS-like deployments where multiple independent teams need their own workspaces on the same Twenty instance.
|
||||
|
||||
**Key differences from single-workspace mode:**
|
||||
|
||||
* Multiple workspaces can be created on the same instance
|
||||
* Each workspace gets its own subdomain (e.g., `sales.your-domain.com`, `marketing.your-domain.com`)
|
||||
* Users sign up and log in at `{DEFAULT_SUBDOMAIN}.your-domain.com` (e.g., `app.your-domain.com`)
|
||||
* No automatic admin privileges — first user in each workspace is a regular user
|
||||
* Workspace-specific settings like subdomain and custom domain become available in workspace settings
|
||||
|
||||
<Warning>
|
||||
**Environment-only setting:** `IS_MULTIWORKSPACE_ENABLED` can only be configured via `.env` file and requires a restart. It cannot be changed through the admin panel.
|
||||
</Warning>
|
||||
|
||||
### DNS Configuration for Multi-Workspace
|
||||
|
||||
When using multi-workspace mode, configure your DNS with a wildcard record to allow dynamic subdomain creation:
|
||||
|
||||
```
|
||||
*.your-domain.com -> your-server-ip
|
||||
```
|
||||
|
||||
This enables automatic subdomain routing for new workspaces without manual DNS configuration.
|
||||
|
||||
### Restricting Workspace Creation
|
||||
|
||||
In multi-workspace mode, you may want to limit who can create new workspaces:
|
||||
|
||||
```bash
|
||||
IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
|
||||
```
|
||||
|
||||
When enabled, only users with `canAccessFullAdminPanel` can create additional workspaces. Users can still create their first workspace during initial signup.
|
||||
|
||||
## تكامل Gmail و Google Calendar
|
||||
|
||||
### إنشاء مشروع Google Cloud
|
||||
|
||||
1. اذهب إلى [وحدة تحكم السحابة من Google](https://console.cloud.google.com/)
|
||||
2. أنشئ مشروعًا جديدًا أو اختر أحد المشاريع الموجودة
|
||||
3. قم بتفعيل هذه الـ APIs:
|
||||
|
||||
* [Gmail API](https://console.cloud.google.com/apis/library/gmail.googleapis.com)
|
||||
* [Google Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com)
|
||||
* [People API](https://console.cloud.google.com/apis/library/people.googleapis.com)
|
||||
|
||||
### تكوين OAuth
|
||||
|
||||
1. اذهب إلى [بيانات اعتماد](https://console.cloud.google.com/apis/credentials)
|
||||
2. قم بإنشاء معرف عميل OAuth 2.0
|
||||
3. أضف هذه الـ URIs لإعادة التوجيه:
|
||||
* `https://{your-domain}/auth/google/redirect` (for SSO)
|
||||
* `https://{your-domain}/auth/google-apis/get-access-token` (for integrations)
|
||||
|
||||
### الإعداد في Twenty
|
||||
|
||||
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
|
||||
2. ابحث بسهولة **عن قسم Google Auth**
|
||||
3. حدد هذه المتغيرات:
|
||||
* `MESSAGING_PROVIDER_GMAIL_ENABLED=true`
|
||||
* `CALENDAR_PROVIDER_GOOGLE_ENABLED=true`
|
||||
* `AUTH_GOOGLE_CLIENT_ID={client-id}`
|
||||
* `AUTH_GOOGLE_CLIENT_SECRET={client-secret}`
|
||||
* `AUTH_GOOGLE_CALLBACK_URL=https://{your-domain}/auth/google/redirect`
|
||||
* `AUTH_GOOGLE_APIS_CALLBACK_URL=https://{your-domain}/auth/google-apis/get-access-token`
|
||||
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
**النطاقات المطلوبة** (يتم تكوينها تلقائيًا):
|
||||
[انظر الشيفرة المصدرية ذات الصلة](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/auth/utils/get-google-apis-oauth-scopes.ts#L4-L10)
|
||||
|
||||
* `https://www.googleapis.com/auth/calendar.events`
|
||||
* `https://www.googleapis.com/auth/gmail.readonly`
|
||||
* `https://www.googleapis.com/auth/profile.emails.read`
|
||||
|
||||
### إذا كان تطبيقك في وضع الاختبار
|
||||
|
||||
إذا كان تطبيقك في وضع الاختبار، ستحتاج إلى إضافة مستخدمين اختباريين إلى مشروعك.
|
||||
|
||||
تحت [شاشة موافقة OAuth](https://console.cloud.google.com/apis/credentials/consent)، أضف مستخدمي الاختبار إلى قسم "مستخدمو الاختبار".
|
||||
|
||||
## تكامل Microsoft 365
|
||||
|
||||
<Warning>
|
||||
يجب على المستخدمين الحصول على [ترخيص Microsoft 365](https://admin.microsoft.com/Adminportal/Home) ليتمكنوا من استخدام تقويم API ورسائل. لن يتمكنوا من مزامنة حسابهم في Twenty دون واحد منها.
|
||||
</Warning>
|
||||
|
||||
### إنشاء مشروع في Microsoft Azure
|
||||
|
||||
ستحتاج إلى إنشاء مشروع في [Microsoft Azure](https://portal.azure.com/#view/Microsoft_AAD_IAM/AppGalleryBladeV2) والحصول على بيانات الاعتماد.
|
||||
|
||||
### تمكين APIs
|
||||
|
||||
على وحدة تحكم Microsoft Azure، قم بتمكين الواجهات التالية في "أذونات":
|
||||
|
||||
* Microsoft Graph: Mail.ReadWrite
|
||||
* Microsoft Graph: Mail.Send
|
||||
* Microsoft Graph: Calendars.Read
|
||||
* Microsoft Graph: User.Read
|
||||
* Microsoft Graph: openid
|
||||
* Microsoft Graph: email
|
||||
* Microsoft Graph: profile
|
||||
* Microsoft Graph: offline_access
|
||||
|
||||
ملحوظة: "Mail.ReadWrite" و "Mail.Send" إلزاميان فقط إذا كنت ترغب في إرسال رسائل بريد إلكتروني باستخدام إجراءات سير العمل الخاصة بنا. يمكنك استخدام "Mail.Read" بدلاً من ذلك إذا كنت ترغب فقط في تلقي الرسائل الإلكترونية.
|
||||
|
||||
### URIs لإعادة التوجيه المصرح بها
|
||||
|
||||
ستحتاج إلى إضافة URIs التالية لإعادة التوجيه إلى مشروعك:
|
||||
|
||||
* `https://{your-domain}/auth/microsoft/redirect` if you want to use Microsoft SSO
|
||||
* `https://{your-domain}/auth/microsoft-apis/get-access-token`
|
||||
|
||||
### الإعداد في Twenty
|
||||
|
||||
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
|
||||
2. Find the **Microsoft Auth** section
|
||||
3. حدد هذه المتغيرات:
|
||||
* `MESSAGING_PROVIDER_MICROSOFT_ENABLED=true`
|
||||
* `CALENDAR_PROVIDER_MICROSOFT_ENABLED=true`
|
||||
* `AUTH_MICROSOFT_ENABLED=true`
|
||||
* `AUTH_MICROSOFT_CLIENT_ID={client-id}`
|
||||
* `AUTH_MICROSOFT_CLIENT_SECRET={client-secret}`
|
||||
* `AUTH_MICROSOFT_CALLBACK_URL=https://{your-domain}/auth/microsoft/redirect`
|
||||
* `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://{your-domain}/auth/microsoft-apis/get-access-token`
|
||||
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
### Configure scopes
|
||||
|
||||
[انظر الشيفرة المصدرية ذات الصلة](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/auth/utils/get-microsoft-apis-oauth-scopes.ts#L2-L9)
|
||||
|
||||
* 'openid'
|
||||
* 'البريد الإلكتروني'
|
||||
* 'profile'
|
||||
* 'offline_access'
|
||||
* 'Mail.ReadWrite'
|
||||
* 'Mail.Send'
|
||||
* 'Calendars.Read'
|
||||
|
||||
### إذا كان تطبيقك في وضع الاختبار
|
||||
|
||||
إذا كان تطبيقك في وضع الاختبار، ستحتاج إلى إضافة مستخدمين اختباريين إلى مشروعك.
|
||||
|
||||
أضف مستخدمي الاختبار إلى قسم "المستخدمون والمجموعات".
|
||||
|
||||
## Background Jobs for Calendar & Messaging
|
||||
|
||||
بعد إعداد تكامل Gmail، أو Google Calendar، أو Microsoft 365، تحتاج إلى بدء وظائف الخلفية التي تقوم بمزامنة البيانات.
|
||||
|
||||
سجل الوظائف المتكررة التالية في حاوية العمل الخاصة بك:
|
||||
|
||||
```bash
|
||||
# from your worker container
|
||||
yarn command:prod cron:messaging:messages-import
|
||||
yarn command:prod cron:messaging:message-list-fetch
|
||||
yarn command:prod cron:calendar:calendar-event-list-fetch
|
||||
yarn command:prod cron:calendar:calendar-events-import
|
||||
yarn command:prod cron:messaging:ongoing-stale
|
||||
yarn command:prod cron:calendar:ongoing-stale
|
||||
yarn command:prod cron:workflow:automated-cron-trigger
|
||||
```
|
||||
|
||||
## تكوين البريد الإلكتروني
|
||||
|
||||
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
|
||||
2. Find the **Email** section
|
||||
3. قم بضبط إعدادات SMTP الخاصة بك:
|
||||
|
||||
<ArticleTabs label1="جيميل" label2="أوفيس 365" label3="Smtp4dev">
|
||||
<ArticleTab>
|
||||
ستحتاج إلى توفير [كلمة مرور التطبيق](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'
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
تذكر أنه إذا كنت تشغل التحقق بعاملين، ستحتاج إلى توفير [كلمة مرور التطبيق](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'
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
**smtp4dev** هو خادم بريد إلكتروني مزيف للتطوير والاختبار.
|
||||
|
||||
* قم بتشغيل صورة smtp4dev: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev`
|
||||
* الوصول إلى واجهة المستخدم smtp4dev هنا: [http://localhost:8090](http://localhost:8090)
|
||||
* حدد المتغيرات التالية:
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=localhost
|
||||
* EMAIL_SMTP_PORT=2525
|
||||
</ArticleTab>
|
||||
</ArticleTabs>
|
||||
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: استكشاف الأخطاء وإصلاحها
|
||||
---
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
إذا واجهت أي مشكلة أثناء إعداد البيئة للتطوير، أو ترقية النسخة الخاصة بك، أو استضافتها ذاتيًا، إليك بعض الحلول للمشاكل الشائعة.
|
||||
|
||||
### استضافة ذاتية
|
||||
|
||||
#### التثبيت الأولي ينتج عنه فشل المصادقة على كلمة المرور للمستخدم "بوستجريس"
|
||||
|
||||
🚨 **هام: هذا الحل فقط للتثبيتات الجديدة** 🚨
|
||||
إذا كان لديك تطبيق Twenty موجود يحتوي على بيانات إنتاج، **لا تتبع هذه الخطوات لأنها ستحذف قاعدة البيانات الخاصة بك بشكل دائم!**
|
||||
|
||||
أثناء تثبيت Twenty لأول مرة، قد ترغب في تغيير كلمة المرور الافتراضية لقاعدة البيانات.
|
||||
كلمة المرور التي تعيّنها أثناء التثبيت الأول يتم تخزينها بشكل دائم في حجم قاعدة البيانات. إذا حاولت لاحقًا تغيير هذه الكلمة في التكوين بدون إزالة الحجم القديم، ستحصل على أخطاء المصادقة لأن قاعدة البيانات لا تزال تستخدم كلمة المرور الأصلية.
|
||||
|
||||
⚠️ تحذير: اتباع الخطوات التالية سيقوم بحذف جميع بيانات قاعدة البيانات بشكل دائم! ⚠️
|
||||
قم بالإجراء فقط إذا كان هذا تثبيتًا جديدًا بدون بيانات مهمة.
|
||||
|
||||
لتحديث `PG_DATABASE_PASSWORD` عليك القيام بما يلي:
|
||||
|
||||
```sh
|
||||
# تحديث PG_DATABASE_PASSWORD في .env
|
||||
إيقاف تشغيل docker باستخدام –volumes
|
||||
تشغيل docker مرة أخرى باستخدام -d
|
||||
```
|
||||
|
||||
#### تم العثور على فواصل الخط CR [نظام Windows]
|
||||
|
||||
هذا بسبب حروف فواصل الخط لنظام Windows وتكوين git. حاول تشغيل:
|
||||
|
||||
```
|
||||
git config --global core.autocrlf false
|
||||
```
|
||||
|
||||
ثم قم بحذف المستودع واستنساخه مرة أخرى.
|
||||
|
||||
#### Missing metadata schema
|
||||
|
||||
أثناء تثبيت Twenty، تحتاج إلى توفير قاعدة بيانات بوستجريس الخاصة بك بالمخططات والإضافات والمستخدمين الصحيحة.
|
||||
إذا نجح تشغيل هذا التخصيص، يجب أن تحتوي قاعدة البيانات لديك على المخططات `default` و`metadata`.
|
||||
إذا لم تكن كذلك، فتأكد من عدم وجود أكثر من مثيل بوستجريس واحد يعمل على الكمبيوتر الخاص بك.
|
||||
|
||||
#### لا يمكن العثور على الوحدة النمطية 'twenty-emails' أو إعلانات نوعها المقابلة.
|
||||
|
||||
عليك بناء حزمة `twenty-emails` قبل تشغيل تهيئة قاعدة البيانات باستخدام `npx nx run twenty-emails:build`.
|
||||
|
||||
#### Missing twenty-x package
|
||||
|
||||
تأكد من تشغيل yarn في الدليل الجذر ثم تشغيل `npx nx server:dev twenty-server`. إذا لم يعمل ذلك، حاول بناء الحزمة المفقودة يدوياً.
|
||||
|
||||
#### التحقق من العمليات عند الحفظ لا يعمل
|
||||
|
||||
هذا يجب أن يعمل تلقائيًا مع تثبيت إضافة eslint. إذا لم يعمل ذلك، حاول إضافة هذا إلى إعدادات vscode (ضمن نطاق حاوية التطوير):
|
||||
|
||||
```
|
||||
"editor.codeActionsOnSave": {
|
||||
|
||||
"source.fixAll.eslint": "explicit"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### أثناء تشغيل `npx nx start` أو `npx nx start twenty-front`، ظهرت خطأ نفاد الذاكرة
|
||||
|
||||
في `packages/twenty-front/.env` قم بإزالة تعليق على `VITE_DISABLE_TYPESCRIPT_CHECKER=true` و`VITE_DISABLE_ESLINT_CHECKER=true` لتعطيل فحوصات الخلفية مما يقلل من كمية الذاكرة المطلوبة.
|
||||
|
||||
**If it does not work:**
|
||||
Run only the services you need, instead of `npx nx start`. على سبيل المثال، إذا كنت تعمل على الخادم، قم بتشغيل `npx nx worker twenty-server` فقط
|
||||
|
||||
**If it does not work:**
|
||||
If you tried to run only `npx nx run twenty-server:start` on WSL and it's failing with the below memory error:
|
||||
|
||||
`FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`
|
||||
|
||||
الحل البديل هو تنفيذ الأمر التالي في الطرفية أو إضافته في ملف تعريف .bashrc ليتم الإعداد تلقائيًا:
|
||||
|
||||
`export NODE_OPTIONS="--max-old-space-size=8192"`
|
||||
|
||||
علامة --max-old-space-size=8192 تحدد حداً أقصى للذاكرة الخاصة بـ Node.js بحد أقصى 8GB؛ الاستخدام يتزايد بطلبات التطبيق.
|
||||
المرجع: https://stackoverflow.com/questions/56982005/where-do-i-set-node-options-max-old-space-size-2048
|
||||
|
||||
**If it does not work:**
|
||||
Investigate which processes are taking you most of your machine RAM. في Twenty، لاحظنا أن بعض إضافات VScode كانت تستهلك الكثير من الذاكرة لذا قمنا بتعطيلها مؤقتًا.
|
||||
|
||||
**If it does not work:**
|
||||
Restart your machine helps to clean up ghost processes.
|
||||
|
||||
#### أثناء تشغيل `npx nx start` تظهر سجلات غريبة [0] و [1]
|
||||
|
||||
هذا متوقع حيث أن الأمر `npx nx start` يقوم بتشغيل المزيد من الأوامر خلف الكواليس
|
||||
|
||||
#### لا يتم إرسال الرسائل الإلكترونية
|
||||
|
||||
غالبًا، يكون السبب هو أن "العامل" لا يعمل في الخلفية. حاول التشغيل
|
||||
|
||||
```
|
||||
npx nx worker twenty-server
|
||||
```
|
||||
|
||||
#### لا يمكن ربط حساب Microsoft 365 الخاص بي
|
||||
|
||||
غالبًا، يكون السبب في ذلك هو أن المسؤول الخاص بك لم يقم بتمكين رخصة Microsoft 365 لحسابك. تحقق من [https://admin.microsoft.com/](https://admin.microsoft.com/Adminportal/Home).
|
||||
|
||||
إذا تلقيت رمز الخطأ `AADSTS50020`، فهذا يعني أنه ربما تستخدم حساب Microsoft شخصي. هذا غير مدعوم حتى الآن. المزيد من المعلومات [هنا](https://learn.microsoft.com/fr-fr/troubleshoot/entra/entra-id/app-integration/error-code-aadsts50020-user-account-identity-provider-does-not-exist)
|
||||
|
||||
#### أثناء تشغيل `yarn` تظهر تحذيرات في الكونسول
|
||||
|
||||
التحذيرات تخبر عن سحب تبعيات إضافية ليست مذكورة صراحة في `package.json`، طالما لم تظهر أي أخطاء تكسر العمل، ينبغي أن يعمل كل شيء كما هو متوقع.
|
||||
|
||||
#### عند الوصول إلى صفحة تسجيل الدخول تظهر رسالة خطأ حول مستخدم غير مصرح له بمحاولة الوصول إلى مساحة العمل في السجلات
|
||||
|
||||
هذا متوقع لأن المستخدم غير مصرح له عندما يسجل الخروج لأن هويته لم يتم التحقق منها.
|
||||
|
||||
#### كيف يمكنك التأكد من عمل العامل الخاص بك؟
|
||||
|
||||
* اذهب إلى [webhook-test.com](https://webhook-test.com/) ونسخ **عنوان URL الخاص بك**.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/self-hosting/webhook-test.jpg" alt="اختبار الويب هوك" />
|
||||
</div>
|
||||
|
||||
* افتح تطبيق Twenty الخاص بك، انتقل إلى `/settings`، وفعل التبديل المتقدم في الجزء السفلي الأيسر من الشاشة.
|
||||
* إنشاء ويب هوك جديد.
|
||||
* Paste **Your Unique Webhook URL** in the **Endpoint Url** field in Twenty. Set the **Filters** to `Companies` and `Created`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/self-hosting/webhook-settings.jpg" alt="إعدادات الويب هوك" />
|
||||
</div>
|
||||
|
||||
* انتقل إلى `/objects/companies` وأنشئ سجلاً جديدًا للشركة.
|
||||
* ارجع إلى [webhook-test.com](https://webhook-test.com/) وتحقق مما إذا كانت هناك **طلب POST جديد** تم استلامه.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/self-hosting/webhook-test-result.jpg" alt="نتيجة اختبار الويب هوك" />
|
||||
</div>
|
||||
|
||||
* إذا تم استلام **طلب POST**، فهذا يعني أن العامل يعمل بنجاح. وإلا، ستحتاج إلى استكشاف الأخطاء وإصلاحها لعامل التشغيل الخاص بك.
|
||||
|
||||
#### لا يمكن تشغيل الواجهة الأمامية وتظهر رسالة الخطأ TS5042: لا يمكن مزج خيار 'المشروع' مع ملفات المصدر في سطر الأوامר
|
||||
|
||||
قم بتعليق مكون التحليل في `packages/twenty-ui/vite-config.ts` كما في المثال أدناه
|
||||
|
||||
```
|
||||
plugins: [
|
||||
react({ jsxImportSource: '@emotion/react' }),
|
||||
tsconfigPaths(),
|
||||
svgr(),
|
||||
dts(dtsConfig),
|
||||
// checker(checkersConfig),
|
||||
wyw({
|
||||
include: [
|
||||
'**/OverflowingTextWithTooltip.tsx',
|
||||
'**/Chip.tsx',
|
||||
'**/Tag.tsx',
|
||||
'**/Avatar.tsx',
|
||||
'**/AvatarChip.tsx',
|
||||
],
|
||||
babelOptions: {
|
||||
presets: ['@babel/preset-typescript', '@babel/preset-react'],
|
||||
},
|
||||
}),
|
||||
],
|
||||
```
|
||||
|
||||
#### لوحة الإدارة غير قابلة للوصول
|
||||
|
||||
قم بتشغيل `UPDATE core."user" SET "canAccessFullAdminPanel" = TRUE WHERE email = 'you@yourdomain.com';` في حاوية قاعدة البيانات للحصول على الوصول إلى لوحة الإدارة.
|
||||
|
||||
### Docker compose بنقرة واحدة
|
||||
|
||||
#### غير قادر على تسجيل الدخول
|
||||
|
||||
إذا كنت لا تستطيع تسجيل الدخول بعد الإعداد:
|
||||
|
||||
1. قم بتشغيل الأوامر التالية:
|
||||
```bash
|
||||
docker exec -it twenty-server-1 yarn
|
||||
docker exec -it twenty-server-1 npx nx database:reset --configuration=no-seed
|
||||
```
|
||||
2. إعادة تشغيل حاويات Docker:
|
||||
```bash
|
||||
docker compose down\ndocker compose up -d
|
||||
```
|
||||
|
||||
لاحظ أن الأمر database:reset سيقوم بمسح قاعدة البيانات الخاصة بك بالكامل وإعادة إنشائها من جديد.
|
||||
|
||||
#### مشاكل الاتصال خلف بروكسي عكسي
|
||||
|
||||
إذا كنت تستخدم Twenty خلف بروكسي عكسي وواجهت مشاكل في الاتصال:
|
||||
|
||||
1. **تأكد من SERVER_URL:**
|
||||
|
||||
تأكد من أن `SERVER_URL` في ملف `.env` يتطابق مع عنوان الوصول الخارجي الخاص بك، بما في ذلك `https` إذا كان SSL مفعلاً.
|
||||
|
||||
2. **التحقق من إعدادات البروكسي العكسي:**
|
||||
|
||||
* تأكد من أن البروكسي العكسي يقوم بتمرير الطلبات بشكل صحيح إلى خادم Twenty.
|
||||
* تأكد من أن رؤوس مثل `X-Forwarded-For` و`X-Forwarded-Proto` تم ضبطها بشكل صحيح.
|
||||
|
||||
3. **إعادة تشغيل الخدمات:**
|
||||
|
||||
بعد إجراء التغييرات، أعد تشغيل كل من البروكسي العكسي وحاويات Twenty.
|
||||
|
||||
#### خطأ عند تحميل صورة - تم رفض الإذن
|
||||
|
||||
تغيير ملكية مجلد البيانات على المضيف من الجذر إلى مستخدم ومجموعة آخرين يحل هذه المشكلة.
|
||||
|
||||
## الحصول على المساعدة
|
||||
|
||||
إذا واجهت مشكلات لم يتم تغطيتها في هذا الدليل:
|
||||
|
||||
* تفقد السجلات:
|
||||
|
||||
اعرض سجلات الحاوية للرسائل الخطأ:
|
||||
|
||||
```bash
|
||||
docker compose logs
|
||||
```
|
||||
|
||||
* الدعم المجتمعي:
|
||||
|
||||
تواصل مع [مجتمع Twenty](https://github.com/twentyhq/twenty/issues) أو [قنوات الدعم](https://discord.gg/cx5n4Jzs57) للحصول على المساعدة.
|
||||
@@ -0,0 +1,381 @@
|
||||
---
|
||||
title: دليل الترقية
|
||||
---
|
||||
|
||||
## إرشادات عامة
|
||||
|
||||
**Always make sure to back up your database before starting the upgrade process** by running `docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql`.
|
||||
|
||||
To restore backup, run `cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}`.
|
||||
|
||||
إذا كنت تستخدم Docker Compose، اتبع الخطوات التالية:
|
||||
|
||||
1. في الطرفية، على الجهاز الذي يعمل فيه Twenty، قم بإيقاف Twenty: `docker compose down`
|
||||
|
||||
2. قم بترقية الإصدار عن طريق تغيير قيمة `TAG` في ملف .env بجانب docker-compose. ( نوصي باستخدام إصدار `major.minor` مثل `v0.53` )
|
||||
|
||||
3. قم بإعادة تشغيل Twenty باستخدام `docker compose up -d`
|
||||
|
||||
إذا كنت ترغب في ترقية مثيلك بزيادة بعض الإصدارات، مثل الانتقال من v0.33.0 إلى v0.35.0، يجب أن تقوم بترقية مثيلك بشكل تسلسلي، في هذا المثال من v0.33.0 إلى v0.34.0، ثم من v0.34.0 إلى v0.35.0.
|
||||
|
||||
**تأكد من أن لديك نسخة احتياطية غير تالفة بعد كل إصدار تمت ترقيته.**
|
||||
|
||||
## خطوات الترقية الخاصة بالإصدار
|
||||
|
||||
## v1.0
|
||||
|
||||
مرحباً Twenty v1.0! 🎉
|
||||
|
||||
## v0.60
|
||||
|
||||
### تحسين الأداء
|
||||
|
||||
تم تحسين جميع التفاعلات مع واجهة برمجة التطبيقات للبيانات الوصفية للحصول على أداء أفضل، خاصة فيما يتعلق بمعالجة بيانات الكائن وإنشاء المساحات.
|
||||
|
||||
أعدنا تصميم استراتيجيتنا للتخزين المؤقت لإعطاء الأولوية للوصول عبر التخزين المؤقت على استعلامات قاعدة البيانات قدر الإمكان، مما أدى إلى تحسين كبير في أداء عمليات واجهة برمجة التطبيقات للبيانات الوصفية.
|
||||
|
||||
إذا واجهت أي مشاكل في وقت التشغيل بعد الترقية، قد تحتاج إلى مسح التخزين المؤقت لضمان تزامنه مع أحدث التغييرات. قم بتشغيل هذا الأمر في حاوية خادم twenty الخاص بك:
|
||||
|
||||
```bash
|
||||
yarn command:prod cache:flush
|
||||
```
|
||||
|
||||
### v0.55
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.55
|
||||
|
||||
لم تعد بحاجة إلى تشغيل أي أمر، الصورة الجديدة ستعتني بتشغيل جميع الترحيلات المطلوبة تلقائيًا.
|
||||
|
||||
### `User does not have permission` error
|
||||
|
||||
إذا واجهت أخطاء في الأذونات في معظم الطلبات بعد الترقية، فقد تحتاج إلى مسح التخزين المؤقت لإعادة حساب أحدث الأذونات.
|
||||
|
||||
في حاوية خادم `twenty` الخاص بك، قم بتشغيل:
|
||||
|
||||
```bash
|
||||
yarn command:prod cache:flush
|
||||
```
|
||||
|
||||
هذه المشكلة خاصة بهذا الإصدار من Twenty ولا يجب أن تكون ضرورية في الترقيات المستقبلية.
|
||||
|
||||
### v0.54
|
||||
|
||||
منذ الإصدار `0.53`، لا حاجة لأي إجراءات يدوية.
|
||||
|
||||
#### إيقاف تشغيل مخطط البيانات الوصفية
|
||||
|
||||
قمنا بدمج مخطط `metadata` مع مخطط `core` لتبسيط استرجاع البيانات من `TypeORM`.
|
||||
قمنا بدمج خطوة تنفيذ الأمر `migrate` مع الأمر `upgrade`. لا ننصح بتشغيل `migrate` يدويًا داخل أي من حاويات الخادم/العمل الخاصة بك.
|
||||
|
||||
### منذ v0.53
|
||||
|
||||
بدءًا من الإصدار `0.53`، تتم الترقية بشكل برمجي داخل `DockerFile`، مما يعني أنه من الآن فصاعدًا، لن تحتاج إلى تشغيل أي أوامر يدويًا بعد الآن.
|
||||
|
||||
تأكد من متابعة الترقية الخاصة بك تسلسليًا، دون تخطي أي إصدار رئيسي (على سبيل المثال `0.43.3` إلى `0.44.0` مسموح، ولكن `0.43.1` إلى `0.45.0` غير مسموح)، قد يؤدي بخلاف ذلك إلى عدم تزامن إصدار مساحة العمل مما قد يؤدي إلى خطأ في وقت التشغيل وفقدان الوظائف.
|
||||
|
||||
للتحقق مما إذا كانت مساحة العمل قد تمت ترقيتها بشكل صحيح ، يمكنك مراجعة نسختها في قاعدة البيانات في جدول `core.workspace`.
|
||||
|
||||
يجب أن تكون دائمًا في نطاق إصدار `major.minor` لحساب Twenty الحالي الخاص بك ، ويمكنك مشاهدة نسخة حسابك في لوحة المدير (في `/settings/admin-panel`، يمكن الوصول إليها إذا كانت خاصية `canAccessFullAdminPanel` الخاصة بالمستخدم مصفوفة إلى true في قاعدة البيانات) أو عن طريق تشغيل `echo $APP_VERSION` في حاوية `twenty-server` الخاصة بك.
|
||||
|
||||
لإصلاح إصدار مساحة العمل غير المتزامن ، سيتعين عليك الترقية من الإصدار المعني لـ Twenty باتباع دليل الترقية الخاص ذو الصلة تسلسليًا وهكذا حتى يصل إلى الإصدار المطلوب.
|
||||
|
||||
#### إزالة `auditLog`
|
||||
|
||||
لقد قمنا بإزالة كائن المعيار auditLog، مما يعني أن حجم النسخة الاحتياطية الخاصة بك قد يقل بشكل كبير بعد هذه الترقية.
|
||||
|
||||
### من v0.51 إلى v0.52
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.52
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
#### لدي مساحة عمل محظورة في الإصدار بين `0.52.0` و`0.52.6`
|
||||
|
||||
لسوء الحظ، تم إزالة `0.52.0` و`0.52.6` بالكامل من dockerHub.
|
||||
سيتعين عليك تحديث نسخة مساحة العمل يدويًا إلى `0.51.0` في قاعدة البيانات والترقية باستخدام إصدار twenty عند `0.52.11` باتباع دليل الترقية الخاص به أعلاه.
|
||||
|
||||
### من v0.50 إلى v0.51
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.51
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
### من v0.44.0 إلى v0.50.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.50.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
#### تغيير ملف docker-compose.yml
|
||||
|
||||
يتضمن هذا الإصدار تغييرًا في `docker-compose.yml` لمنح خدمة `worker` إمكانية الوصول إلى وحدة التخزين `server-local-data`.
|
||||
يرجى تحديث `docker-compose.yml` المحلي الخاص بك بـ [docker-compose.yml v0.50.0](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml)
|
||||
|
||||
### من v0.43.0 إلى v0.44.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.44.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
### من v0.42.0 إلى v0.43.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.43.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
في هذا الإصدار، قمنا أيضًا بالتحول إلى صورة postgres:16 في docker-compose.yml.
|
||||
|
||||
#### (الخيار 1) ترحيل قاعدة البيانات
|
||||
|
||||
احتفاظ بصورة postgres-spilo الحالية مقبول، ولكن سيتعين عليك تجميد الإصدار في docker-compose.yml ليكون 0.43.0.
|
||||
|
||||
#### (الخيار 2) ترحيل قاعدة البيانات
|
||||
|
||||
إذا كنت تريد ترحيل قاعدة بياناتك إلى الصورة الجديدة postgres:16، يرجى اتباع هذه الخطوات:
|
||||
|
||||
1. نسخ قاعدة البيانات الخاصة بك من حاوية 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 .
|
||||
```
|
||||
|
||||
تأكد من أن ملف النسخ الاحتياطي ليس فارغًا.
|
||||
|
||||
2. قم بترقية docker-compose.yml الخاص بك لاستخدام صورة postgres:16 كما هو في الملف [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml).
|
||||
|
||||
3. استعادة قاعدة البيانات إلى الحاوية الجديدة 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 إلى v0.42.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.42.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.42
|
||||
```
|
||||
|
||||
**متغيرات البيئة**
|
||||
|
||||
* تمت الإزالة: `FRONT_PORT`, `FRONT_PROTOCOL`, `FRONT_DOMAIN`, `PORT`
|
||||
* تمت الإضافة: `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 إلى v0.41.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.41.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.41
|
||||
```
|
||||
|
||||
**متغيرات البيئة**
|
||||
|
||||
* تمت الإزالة: `AUTH_MICROSOFT_TENANT_ID`
|
||||
|
||||
### من v0.35.0 إلى v0.40.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.40.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.40
|
||||
```
|
||||
|
||||
**متغيرات البيئة**
|
||||
|
||||
* تمت الإضافة: `IS_EMAIL_VERIFICATION_REQUIRED`, `EMAIL_VERIFICATION_TOKEN_EXPIRES_IN`, `WORKFLOW_EXEC_THROTTLE_LIMIT`, `WORKFLOW_EXEC_THROTTLE_TTL`
|
||||
|
||||
### من v0.34.0 إلى v0.35.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.35.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.35
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.35` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
**متغيرات البيئة**
|
||||
|
||||
* قمنا باستبدال `ENABLE_DB_MIGRATIONS` بـ `DISABLE_DB_MIGRATIONS` (القيمة الافتراضية الآن `false`, على الأرجح لن تحتاج إلى تعيين أي شيء)
|
||||
|
||||
### من v0.33.0 إلى v0.34.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.34.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.34
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.34` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
**متغيرات البيئة**
|
||||
|
||||
* تمت الإزالة: `FRONT_BASE_URL`
|
||||
* تمت الإضافة: `FRONT_DOMAIN`, `FRONT_PROTOCOL`, `FRONT_PORT`
|
||||
|
||||
لقد قمنا بتحديث الطريقة التي نتعامل بها مع عنوان URL الخاص بالواجهة الأمامية.
|
||||
يمكنك الآن تعيين عنوان URL الخاص بالواجهة الأمامية باستخدام متغيرات `FRONT_DOMAIN`, `FRONT_PROTOCOL` و`FRONT_PORT`.
|
||||
إذا لم يتم تعيين FRONT_DOMAIN، فسوف يتراجع عنوان URL للواجهة الأمامية إلى `SERVER_URL`.
|
||||
|
||||
### من v0.32.0 إلى v0.33.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.33.0
|
||||
|
||||
```
|
||||
yarn command:prod cache:flush
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.33
|
||||
```
|
||||
|
||||
أمر `yarn command:prod cache:flush` سيقوم بمسح ذاكرة تخزين Redis المؤقتة.
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.33` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
بدءًا من هذا الإصدار، أصبحت صورة twenty-postgres للقاعدة غير نشطة وتم استخدام twenty-postgres-spilo بدلاً منها.
|
||||
إذا كنت ترغب في الاستمرار باستخدام صورة twenty-postgres، فما عليك سوى استبدال `twentycrm/twenty-postgres:${TAG}` بـ `twentycrm/twenty-postgres` في docker-compose.yml.
|
||||
|
||||
### من v0.31.0 إلى v0.32.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.32.0
|
||||
|
||||
**ترقية المخطط والبيانات**
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.32
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.32` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
**متغيرات البيئة**
|
||||
|
||||
لقد قمنا بتحديث الطريقة التي نتعامل بها مع اتصال Redis.
|
||||
|
||||
* تمت الإزالة: `REDIS_HOST`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`
|
||||
* تمت الإضافة: `REDIS_URL`
|
||||
|
||||
قم بتحديث ملفك `.env` لاستخدام المتغير الجديد `REDIS_URL` بدلاً من معلمات اتصال Redis الفردية.
|
||||
|
||||
قمنا أيضًا بتبسيط الطريقة التي نتعامل بها مع رموز JWT.
|
||||
|
||||
* تمت الإزالة: `ACCESS_TOKEN_SECRET`, `LOGIN_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `FILE_TOKEN_SECRET`
|
||||
* تمت الإضافة: `APP_SECRET`
|
||||
|
||||
قم بتحديث ملفك `.env` لاستخدام المتغير الجديد `APP_SECRET` بدلاً من الأسرار الفردية للرموز (يمكنك استخدام نفس السر كما كان من قبل أو توليد سلسلة عشوائية جديدة)
|
||||
|
||||
**الحساب المتصل**
|
||||
|
||||
إذا كنت تستخدم حسابًا متصلًا لمزامنة رسائل بريدك الإلكتروني في جوجل والتقويمات، فستحتاج إلى تفعيل [People API](https://developers.google.com/people) في وحدة تحكم مشرف جوجل لديك.
|
||||
|
||||
### من v0.30.0 إلى v0.31.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.31.0
|
||||
|
||||
**ترقية المخطط والبيانات**:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.31
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.31` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
### من v0.24.0 إلى v0.30.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.30.0
|
||||
|
||||
**Breaking change**:
|
||||
To enhance performances, Twenty now requires redis cache to be configured. قمنا بتحديث [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml) لتعكس ذلك.
|
||||
تأكد من تحديث إعدادات التكوين الخاصة بك وتحديث المتغيرات البيئية الخاصة بك وفقًا لذلك:
|
||||
|
||||
```
|
||||
REDIS_HOST={your-redis-host}
|
||||
REDIS_PORT={your-redis-port}
|
||||
CACHE_STORAGE_TYPE=redis
|
||||
```
|
||||
|
||||
**ترقية المخطط والبيانات**:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.30
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.30` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
### من v0.23.0 إلى v0.24.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.24.0
|
||||
|
||||
قم بتشغيل الأوامر التالية:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.24
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على هيكل قاعدة البيانات (مخططات core وmetadata)
|
||||
أمر `yarn command:prod upgrade-0.24` يتولى ترقية البيانات إلى جميع المساحات.
|
||||
|
||||
### من v0.22.0 إلى v0.23.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.23.0
|
||||
|
||||
قم بتشغيل الأوامر التالية:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.23
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على قاعدة البيانات.
|
||||
أمر `yarn command:prod upgrade-0.23` يتولى ترقية البيانات، بما في ذلك نقل الأنشطة إلى المهام/الملاحظات.
|
||||
|
||||
### من v0.21.0 إلى v0.22.0
|
||||
|
||||
قم بترقية مثيل Twenty الخاص بك لاستخدام صورة v0.22.0
|
||||
|
||||
قم بتشغيل الأوامر التالية:
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod workspace:sync-metadata -f
|
||||
yarn command:prod upgrade-0.22
|
||||
```
|
||||
|
||||
أمر `yarn database:migrate:prod` سيقوم بتطبيق الترقيات على قاعدة البيانات.
|
||||
الأمر `yarn command:prod workspace:sync-metadata -f` سيزامن تعريف الكائنات القياسية مع جداول البيانات الوصفية ويطبق الترقيات المطلوبة على مساحات العمل الموجودة.
|
||||
الأمر `yarn command:prod upgrade-0.22` سيقوم بتطبيق تحويلات بيانات محددة للتكيف مع الخيارات الافتراضية الجديدة لتوثيق الطلبات في الكائنات.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Self-Host
|
||||
description: Deploy and manage Twenty on your own infrastructure.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/what-is-twenty/20.png" alt="الذكاء الاصطناعي" />
|
||||
</Frame>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty can be self-hosted on your own infrastructure, giving you full control over your data and deployment.
|
||||
|
||||
## Why Self-Host?
|
||||
|
||||
* **Data ownership**: Keep all CRM data on your own servers
|
||||
* **Compliance**: Meet regulatory requirements for data residency
|
||||
* **Customization**: Full access to modify and extend the platform
|
||||
|
||||
## البدء
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="Docker Compose" icon="docker" href="/l/ar/developers/self-host/capabilities/docker-compose">
|
||||
Quick setup with Docker
|
||||
</Card>
|
||||
|
||||
<Card title="Cloud Providers" icon="cloud" href="/l/ar/developers/self-host/capabilities/cloud-providers">
|
||||
Deploy on AWS, GCP, or Azure
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,40 +1,142 @@
|
||||
{
|
||||
"tabs": {
|
||||
"userGuide": {
|
||||
"label": "دليل المستخدم",
|
||||
"label": "User Guide",
|
||||
"groups": {
|
||||
"gettingStarted": {
|
||||
"label": "البدء"
|
||||
"discoverTwenty": {
|
||||
"label": "Discover Twenty",
|
||||
"groups": {
|
||||
"gettingStartedCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"gettingStartedHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataModel": {
|
||||
"label": "نموذج البيانات"
|
||||
"label": "نموذج البيانات",
|
||||
"groups": {
|
||||
"dataModelCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"dataModelHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"crmEssentials": {
|
||||
"label": "أساسيات CRM"
|
||||
"dataMigration": {
|
||||
"label": "Data Migration",
|
||||
"groups": {
|
||||
"dataMigrationCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"dataMigrationHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"views": {
|
||||
"label": "العروض"
|
||||
"calendarEmails": {
|
||||
"label": "Calendar & Emails",
|
||||
"groups": {
|
||||
"calendarEmailsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"calendarEmailsHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workflows": {
|
||||
"label": "سير العمل"
|
||||
"label": "سير العمل",
|
||||
"groups": {
|
||||
"workflowsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"workflowsHowTos": {
|
||||
"label": "How-Tos",
|
||||
"groups": {
|
||||
"crmAutomations": {
|
||||
"label": "CRM Automations"
|
||||
},
|
||||
"connectToOtherTools": {
|
||||
"label": "Connect to Other Tools"
|
||||
},
|
||||
"advancedConfigurations": {
|
||||
"label": "Advanced Configurations"
|
||||
},
|
||||
"needMoreHelp": {
|
||||
"label": "Need More Help"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"collaboration": {
|
||||
"label": "التعاون"
|
||||
"ai": {
|
||||
"label": "الذكاء الاصطناعي",
|
||||
"groups": {
|
||||
"aiCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"aiHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"integrationsApi": {
|
||||
"label": "التكاملات & API"
|
||||
"viewsPipelines": {
|
||||
"label": "Views & Pipelines",
|
||||
"groups": {
|
||||
"viewsPipelinesCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"viewsPipelinesHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"reporting": {
|
||||
"label": "التقارير"
|
||||
"dashboards": {
|
||||
"label": "لوحات القيادة",
|
||||
"groups": {
|
||||
"dashboardsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"dashboardsHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"permissionsAccess": {
|
||||
"label": "Permissions & Access",
|
||||
"groups": {
|
||||
"permissionsAccessCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"permissionsAccessHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"billing": {
|
||||
"label": "الفوترة",
|
||||
"groups": {
|
||||
"billingCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"billingHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"label": "الإعدادات"
|
||||
},
|
||||
"pricing": {
|
||||
"label": "التسعير"
|
||||
},
|
||||
"resources": {
|
||||
"label": "الموارد"
|
||||
"label": "\\ا\\ل\\إ\\ع\\د\\ا\\د\\ا\\ت",
|
||||
"groups": {
|
||||
"settingsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
},
|
||||
"settingsHowTos": {
|
||||
"label": "How-Tos"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -42,50 +144,60 @@
|
||||
"label": "المطورون",
|
||||
"groups": {
|
||||
"developersGroup": {
|
||||
"label": "المطورين"
|
||||
"label": "المطورون"
|
||||
},
|
||||
"devGettingStarted": {
|
||||
"label": "بدء العمل",
|
||||
"extend": {
|
||||
"label": "Extend",
|
||||
"groups": {
|
||||
"selfHosting": {
|
||||
"label": "الاستضافة الذاتية"
|
||||
},
|
||||
"apiAndWebhooks": {
|
||||
"label": "API و Webhooks"
|
||||
"extendCapabilities": {
|
||||
"label": "Capabilities"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contributing": {
|
||||
"label": "المساهمة",
|
||||
"selfHost": {
|
||||
"label": "Self-Host",
|
||||
"groups": {
|
||||
"frontendDevelopment": {
|
||||
"label": "تطوير الواجهة",
|
||||
"selfHostCapabilities": {
|
||||
"label": "Capabilities"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contribute": {
|
||||
"label": "Contribute",
|
||||
"groups": {
|
||||
"contributeCapabilities": {
|
||||
"label": "Capabilities",
|
||||
"groups": {
|
||||
"twentyUi": {
|
||||
"label": "Twenty UI",
|
||||
"frontendDevelopment": {
|
||||
"label": "تطوير الواجهة الأمامية",
|
||||
"groups": {
|
||||
"display": {
|
||||
"label": "عرض"
|
||||
},
|
||||
"feedback": {
|
||||
"label": "الملاحظات"
|
||||
},
|
||||
"input": {
|
||||
"label": "Input"
|
||||
},
|
||||
"navigation": {
|
||||
"label": "Navigation"
|
||||
"twentyUi": {
|
||||
"label": "Twenty UI",
|
||||
"groups": {
|
||||
"display": {
|
||||
"label": "عرض"
|
||||
},
|
||||
"feedback": {
|
||||
"label": "التغذية الراجعة"
|
||||
},
|
||||
"input": {
|
||||
"label": "إدخال"
|
||||
},
|
||||
"navigation": {
|
||||
"label": "Navigation"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"backendDevelopment": {
|
||||
"label": "تطوير الواجهة الخلفية"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backendDevelopment": {
|
||||
"label": "تطوير الخلفية"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: تلميح التطبيق
|
||||
image: /images/user-guide/tips/light-bulb.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/tips/light-bulb.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
رسالة مختصرة تعرض معلومات إضافية عند تفاعل المستخدم مع عنصر.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { AppTooltip } from "@/ui/display/tooltip/AppTooltip";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<p id="hoverText" style={{ display: "inline-block" }}>
|
||||
Customer Insights
|
||||
</p>
|
||||
<AppTooltip
|
||||
className
|
||||
anchorSelect="#hoverText"
|
||||
content="Explore customer behavior and preferences"
|
||||
delayHide={0}
|
||||
offset={6}
|
||||
noArrow={false}
|
||||
isOpen={true}
|
||||
place="bottom"
|
||||
positionStrategy="absolute"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
|
||||
| اختيار الربط | محدد CSS | Selector for the tooltip anchor (the element that triggers the tooltip) |
|
||||
| المحتوى | نص | The content you want to display within the tooltip |
|
||||
| تأخير الإخفاء | رقم | The delay in seconds before hiding the tooltip after the cursor leaves the anchor |
|
||||
| الإزاحة | رقم | The offset in pixels for positioning the tooltip |
|
||||
| بدون سهم | قيمة منطقية | إذا كانت القيمة `صحيح`, سيتم إخفاء السهم في المربط التنبيهي |
|
||||
| مفتوح | قيمة منطقية | إذا كانت القيمة `صحيح`, يكون المربط التنبيهي مفتوحًا افتراضيًا |
|
||||
| المكان | `PlacesType` string from `react-tooltip` | يحدد موضع المربط التنبيهي. Values include `bottom`, `left`, `right`, `top`, `top-start`, `top-end`, `right-start`, `right-end`, `bottom-start`, `bottom-end`, `left-start`, and `left-end` |
|
||||
| استراتيجية الوضعية | نص `PositionStrategy` من `react-tooltip` | استراتيجية وضعية للمربط التنبيهي. Has two values: `absolute` and `fixed` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Overflowing Text with Tooltip
|
||||
|
||||
يعالج النص الزائد ويعرض مربط تنبيهي عند فيضان النص.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```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.';
|
||||
|
||||
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------- | ------ | ------------------------------------------------------------ |
|
||||
| نص | string | The content you want to display in the overflowing text area |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: علامة صحيح
|
||||
image: /images/user-guide/tasks/tasks_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/tasks/tasks_header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
يمثل إجراءً ناجحًا أو مكتملًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Checkmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <Checkmark />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
يمتد `React.ComponentPropsWithoutRef<'div'>` و يقبل جميع خصائص عنصر `div` العادي.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## علامة صحيح متحركة
|
||||
|
||||
يمثل رمز علامة صحيح مع ميزة الإضافة للحركة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { AnimatedCheckmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AnimatedCheckmark
|
||||
isAnimating={true}
|
||||
color="green"
|
||||
duration={0.5}
|
||||
size={30}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | ------------------------------------- | ----------------- |
|
||||
| isAnimating | قيمة منطقية | يتحكم فيما إذا كانت علامة صحيح متحركة | خاطئ |
|
||||
| اللون | string | Color of the checkmark | |
|
||||
| المدة | رقم | مدة الحركة بالثواني | 0.5 ثانية |
|
||||
| الحجم | رقم | The size of the checkmark | 28 بكسل |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: رقاقة
|
||||
image: /images/user-guide/github/github-header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/github/github-header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
عنصر مرئي يمكن استخدامه كحاوية قابلة للنقر أو غير قابلة للنقر، مع علامة وعناصر اختيارية يسار ويمين، وخيارات تصميم متنوعة لعرض العلامات والبطاقات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Clickable Chip"
|
||||
clickable={true}
|
||||
variant="highlighted"
|
||||
accent="text-primary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
|
||||
| linkToEntity | نص | الرابط إلى الكيان |
|
||||
| معرف الكيان | نص | المعرف الفريد للكيان |
|
||||
| الاسم | نص | اسم الكيان |
|
||||
| رابط الصورة | نص | s picture", |
|
||||
| نوع الصورة الرمزية | نوع الصورة الرمزية | نوع الصورة الرمزية التي تريد عرضها. لديه خياران: `مستدير` و `مربع` |
|
||||
| التنوع | تعداد EntityChipVariant | تنوع الرقاقة الكيانية التي ترغب في عرضها. لديه خياران: `عادي` و `شفاف` |
|
||||
| الأيقونة اليسرى | مكون رمز | مكون React يمثل رمزًا. يظهر على الجانب الأيسر من الرقاقة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الأمثلة
|
||||
|
||||
### رقاقة شفافة معطلة
|
||||
|
||||
```jsx
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Transparent Disabled Chip"
|
||||
clickable={false}
|
||||
variant="rounded"
|
||||
accent="text-secondary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
### رقاقة معطلة مع تلميح
|
||||
|
||||
```jsx
|
||||
import { Chip } from "twenty-ui/components";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Disabled chip that triggers a tooltip when overflowing."
|
||||
clickable={false}
|
||||
variant="regular"
|
||||
accent="text-primary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## رقاقة كيان
|
||||
|
||||
عنصر يشبه الرقاقة لعرض معلومات عن كيان.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```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 (
|
||||
<Router>
|
||||
<Chip
|
||||
linkToEntity="/entity-link"
|
||||
entityId="entityTest"
|
||||
name="Entity name"
|
||||
pictureUrl=""
|
||||
avatarType="rounded"
|
||||
variant="regular"
|
||||
LeftIcon={IconTwentyStar}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
|
||||
| linkToEntity | نص | الرابط إلى الكيان |
|
||||
| معرف الكيان | نص | المعرف الفريد للكيان |
|
||||
| الاسم | نص | اسم الكيان |
|
||||
| رابط الصورة | نص | s picture", |
|
||||
| نوع الصورة الرمزية | نوع الصورة الرمزية | نوع الصورة الرمزية التي تريد عرضها. لديه خياران: `مستدير` و `مربع` |
|
||||
| التنوع | تعداد EntityChipVariant | تنوع الرقاقة الكيانية التي ترغب في عرضها. لديه خياران: `عادي` و `شفاف` |
|
||||
| الأيقونة اليسرى | مكون رمز | مكون React يمثل رمزًا. يظهر على الجانب الأيسر من الرقاقة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: الأيقونات
|
||||
image: /images/user-guide/objects/objects.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/objects/objects.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
قائمة بالأيقونات المستخدمة في جميع أنحاء تطبيقنا.
|
||||
|
||||
## Tabler Icons
|
||||
|
||||
نستخدم أيقونات Tabler لـ React في جميع أنحاء التطبيق.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="التثبيت">
|
||||
<br />
|
||||
|
||||
```
|
||||
yarn add @tabler/icons-react
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
يمكنك استيراد كل أيقونة كمكون. إليك مثال:
|
||||
|
||||
<br />
|
||||
|
||||
```jsx
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconArrowLeft color="red" size={48} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
| الإزاحة | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ------ | -------------------------------- | ----------------- |
|
||||
| الحجم | رقم | ارتفاع وعرض الأيقونة بالبكسل | 24 |
|
||||
| اللون | string | لون الأيقونات | اللون الحالي |
|
||||
| الخط العريض | رقم | عرض الخط العريض للأيقونة بالبكسل | 2 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## أيقونات مخصصة
|
||||
|
||||
بالإضافة إلى أيقونات Tabler، يستخدم التطبيق أيضًا بعض الأيقونات المخصصة.
|
||||
|
||||
### أيقونة دفتر العناوين
|
||||
|
||||
يعرض أيقونة دفتر العناوين.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconAddressBook } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconAddressBook size={24} stroke={2} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----- | -------------------------------- | ----------------- |
|
||||
| الحجم | رقم | ارتفاع وعرض الأيقونة بالبكسل | 24 |
|
||||
| الخط العريض | رقم | عرض الخط العريض للأيقونة بالبكسل | 2 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Soon Pill
|
||||
image: /images/user-guide/kanban-views/kanban.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/kanban-views/kanban.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
شارة صغيرة أو "كبسولة" للإشارة إلى أن شيئًا ما قادم قريبًا.
|
||||
|
||||
```jsx
|
||||
import { SoonPill } from "@/ui/display/pill/components/SoonPill";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <SoonPill />;
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: علامة
|
||||
image: /images/user-guide/table-views/table.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/table-views/table.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
Component to visually categorize or label content.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Tag } from "@/ui/display/tag/components/Tag";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Tag
|
||||
className
|
||||
color="red"
|
||||
text="Urgent"
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ----- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| اللون | نص | لون العلامة. الخيارات تشمل: `أخضر`, `تركواز`, `سماوي`, `أزرق`, `أرجواني`, `وردي`, `أحمر`, `برتقالي`, `أصفر`, `رمادي`. |
|
||||
| نص | نص | محتوى العلامة |
|
||||
| عند_النقر | دالة | دالة اختيارية تُستدعى عند نقر المستخدم على العلامة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -4,31 +4,28 @@ image: /images/user-guide/api/api.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/api/api.png" alt="Header" />
|
||||
<img src="/images/user-guide/api/api.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
يستخدم محرر نصوص غني يعتمد على الكتل من [BlockNote](https://www.blocknotejs.org/) للسماح للمستخدمين بتحرير وعرض كتل المحتوى.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { useBlockNote } from "@blocknote/react";
|
||||
import { BlockEditor } from "@/ui/input/editor/components/BlockEditor";
|
||||
|
||||
```jsx
|
||||
import { useBlockNote } from "@blocknote/react";
|
||||
import { BlockEditor } from "@/ui/input/editor/components/BlockEditor";
|
||||
export const MyComponent = () => {
|
||||
const BlockNoteEditor = useBlockNote();
|
||||
|
||||
export const MyComponent = () => {
|
||||
const BlockNoteEditor = useBlockNote();
|
||||
return <BlockEditor editor={BlockNoteEditor} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
return <BlockEditor editor={BlockNoteEditor} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------- | ----------------- | ------------------------ |
|
||||
| محرر | `BlockNoteEditor` | مثيل أو تكوين محرر الكتل |
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------- | ----------------- | ------------------------ |
|
||||
| محرر | `BlockNoteEditor` | مثيل أو تكوين محرر الكتل |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -0,0 +1,439 @@
|
||||
---
|
||||
title: الأزرار
|
||||
image: /images/user-guide/views/filter.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/views/filter.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
قائمة الأزرار ومجموعات الأزرار المستخدمة في التطبيق.
|
||||
|
||||
## زر
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Title"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="default"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | --------------------- | -------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون رمز اختياري يُعرض داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| عرض كامل | قيمة منطقية | يُحدد إذا كان الزر يجب أن يمتد ليغطي العرض الكامل للحاوية الخاصة به |
|
||||
| التنوع | string | النمط المرئي للزر. Options include `primary`, `secondary`, and `tertiary` |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `right`, and `middle` |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `default`، `blue`، `danger` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة الأزرار
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
import { ButtonGroup } from "@/ui/input/button/components/ButtonGroup";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ButtonGroup variant="primary" size="large" accent="blue" className>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 1"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 2"
|
||||
fullWidth={false}
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
position="left"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 3"
|
||||
fullWidth={false}
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
position="right"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | --------- | ------------------------------------------------------------------------------------------ |
|
||||
| التنوع | string | النمط المرئي للأزرار داخل المجموعة. Options include `primary`, `secondary`, and `tertiary` |
|
||||
| الحجم | string | حجم الأزرار داخل المجموعة. Has two options: `medium` and `small` |
|
||||
| accent | نص | لون تمييز الأزرار داخل المجموعة. Options include `default`, `blue` and `danger` |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر عائم
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
title="Title"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `middle`, `right` |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة الأزرار العائمة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { FloatingButtonGroup } from "@/ui/input/button/components/FloatingButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButtonGroup size="small">
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconClipboardText}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconCheckbox}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
/>
|
||||
</FloatingButtonGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ------- | --------- | -------------------------------------------------------- | ----------------- |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` | صغير |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر رمز عائم
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingIconButton } from "@/ui/input/button/components/FloatingIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingIconButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
isActive={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | -------------------------------------------------------------------------------------- |
|
||||
| className | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | نص | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `right`, and `middle` |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة أزرار الرموز العائمة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingIconButtonGroup } from "@/ui/input/button/components/FloatingIconButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const iconButtons = [
|
||||
{
|
||||
Icon: IconClipboardText,
|
||||
onClick: () => console.log("Button 1 clicked"),
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => console.log("Button 2 clicked"),
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<FloatingIconButtonGroup
|
||||
className
|
||||
size="small"
|
||||
iconButtons={iconButtons} />
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| أزرار الرموز | array | مجموعة من الكائنات، يمثل كل منها زر رمز في المجموعة. يجب أن يشمل كل كائن مكون الرمز الذي تريد عرضه في الزر، الوظيفة التي ترغب في استدعائها عند نقر المستخدم على الزر، وما إذا كان الزر ينبغي أن يكون نشطًا أم لا. |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Light Button
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { LightButton } from "@/ui/input/button/components/LightButton";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <LightButton
|
||||
className
|
||||
icon={null}
|
||||
title="Title"
|
||||
accent="secondary"
|
||||
active={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={()=>console.log('click')}
|
||||
/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
| الإزاحة | النوع | الوصف |
|
||||
| --------- | ----------------- | -------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ReactNode` | الرمز الذي تريد عرضه في الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. لون الزر المميز تشمل الخيارات: `ثانوي` و `ثالثي` |
|
||||
| نشط | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر أيقونة خفيف
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { LightIconButton } from "@/ui/input/button/components/LightIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<LightIconButton
|
||||
className
|
||||
testId="test1"
|
||||
Icon={IconSearch}
|
||||
title="Title"
|
||||
size="small"
|
||||
accent="secondary"
|
||||
active={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| --------- | --------------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| testId | string | معرف اختبار للزر |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نصي للزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| accent | string | لون الزر المميز تشمل الخيارات: `ثانوي` و `ثالثي` |
|
||||
| نشط | قيمة منطقية | يحدد ما إذا كان الزر في حالة نشطة |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطلاً |
|
||||
| التركيز | قيمة منطقية | يشير إلى ما إذا كان الزر لديه تركيز |
|
||||
| عند النقر | function | وظيفة رد اتصال تتفعّل عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الزر الرئيسي
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MainButton } from "@/ui/input/button/components/MainButton";
|
||||
import { IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<MainButton
|
||||
title="Checkbox"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
soon={false}
|
||||
Icon={IconCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| -------------- | -------------------------------- | -------------------------------------------------------------- |
|
||||
| العنوان | string | محتوى نصي للزر |
|
||||
| عرض كامل | قيمة منطقية | يحدد ما إذا كان الزر يجب أن يمتد على كامل عرض الحاوية |
|
||||
| التنوع | string | النمط البصري للزر. تشمل الخيارات `أساسي` و `ثانوي` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| خصائص زر React | `React.ComponentProps<'button'>` | كل خصائص زر HTML القياسية مدعومة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر أيقونة دائري
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RoundedIconButton } from "@/ui/input/button/components/RoundedIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RoundedIconButton
|
||||
Icon={IconSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| -------------- | ----------------------------------------------- | ----- |
|
||||
| أيقونة | `React.ComponentType` | |
|
||||
| خصائص زر React | `React.ButtonHTMLAttributes<HTMLButtonElement>` | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: مربع اختيار
|
||||
image: /images/user-guide/tasks/tasks_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/tasks/tasks_header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
يُستخدم عندما يحتاج المستخدم إلى اختيار قيم متعددة من بين عدة خيارات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Checkbox } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={true}
|
||||
indeterminate={false}
|
||||
onChange={() => console.log("onChange function fired")}
|
||||
onCheckedChange={() => console.log("onCheckedChange function fired")}
|
||||
variant="primary"
|
||||
size="small"
|
||||
shape="squared"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------- | ------------------------------------------------------------------------------------------- |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار محددًا |
|
||||
| indeterminate | قيمة منطقية | Indicates whether the checkbox is in an indeterminate state (neither checked nor unchecked) |
|
||||
| عند التغيير | دالة | الدالة التي ترغب في تفعيلها عند تغيير حالة مربع الاختيار |
|
||||
| عند تغيير الحالة المحددة | دالة | The callback function you want to trigger when the `checked` state changes |
|
||||
| نموذج | نص | النمط البصري للصندوق. تتضمن الخيارات: 'أساسي'، 'ثانوي'، و 'ثالثي' |
|
||||
| الحجم | نص | حجم مربع الاختيار. Has two options: `small` and `large` |
|
||||
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: طريقة عرض الألوان
|
||||
image: /images/user-guide/fields/field.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/fields/field.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
## بطاقة مخطط الألوان
|
||||
|
||||
يمثل مخططات ألوان مختلفة ومخصص بشكل خاص للمواضيع الفاتحة والداكنة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { ColorSchemeCard } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ColorSchemeCard
|
||||
variant="Dark"
|
||||
selected={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ------------ | --------------------------------------- | ---------------------------------------------------------------------- | ----------------- |
|
||||
| التنوع | string | نوع مخطط الألوان. تشمل الخيارات `داكنة`, `فاتحة`, و `النظام` | فاتح |
|
||||
| المحدد | قيمة منطقية | إذا كان `صحيح`, يتم عرض علامة الاختيار للدلالة على مخطط الألوان المحدد | |
|
||||
| خصائص إضافية | `React.ComponentPropsWithoutRef<'div'>` | خصائص عنصر `div` العادي في HTML | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## منتقي مخطط الألوان
|
||||
|
||||
يتيح للمستخدمين اختيار بين مخططات الألوان المختلفة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { ColorSchemePicker } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ColorSchemePicker
|
||||
value="Dark"
|
||||
onChange
|
||||
/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ----------- | ------------------- | ---------------------------------------------------------------------------- |
|
||||
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
|
||||
| عند التغيير | function | The callback function you want to trigger when a user selects a color scheme |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: منتقى الأيقونات
|
||||
image: /images/user-guide/github/github-header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/github/github-header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
منتقى الأيقونات المعتمد على القائمة المنسدلة الذي يتيح للمستخدمين اختيار أيقونة من قائمة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import React, { useState } from "react";
|
||||
import { IconPicker } from "@/ui/input/components/IconPicker";
|
||||
|
||||
export const MyComponent = () => {
|
||||
|
||||
const [selectedIcon, setSelectedIcon] = useState("");
|
||||
const handleIconChange = ({ iconKey, Icon }) => {
|
||||
console.log("Selected Icon:", iconKey);
|
||||
setSelectedIcon(iconKey);
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<IconPicker
|
||||
disabled={false}
|
||||
onChange={handleIconChange}
|
||||
selectedIconKey={selectedIcon}
|
||||
variant="primary"
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ----------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| معطل | قيمة منطقية | يقوم بتعطيل منتقى الأيقونات إذا تم تعيينه إلى `true` |
|
||||
| عند التغيير | دالة | الدالة الارتجاعية التي تُفعل عندما يختار المستخدم أيقونة. يستقبل كائنًا يحتوي على الخصائص `iconKey` و `Icon` |
|
||||
| مفتاح الأيقونة المختارة | نص | مفتاح الأيقونة المختارة في البداية |
|
||||
| النقر بالخارج | دالة | الدالة الارتجاعية التي تُفعل عندما ينقر المستخدم خارج القائمة المنسدلة |
|
||||
| عند الإغلاق | دالة | الدالة الارتجاعية التي تُفعل عند إغلاق القائمة المنسدلة |
|
||||
| عند الفتح | دالة | الدالة الارتجاعية التي تُفعل عند فتح القائمة المنسدلة |
|
||||
| التنوع | نص | The visual style variant of the clickable icon. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "\x062A\x062F\x062E\x064A\x0644 \x0627\x0644\x0635\x0648\x0631\x0629"
|
||||
image: /images/user-guide/objects/objects.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/objects/objects.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
4A4F33452D 44445245332A2E2F454A46 28452F 482532274429 35483129.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
27332A4A31272F { 2A2F2E4A44 274435483129 } 4546 "@/ui/input/components/ImageInput";
|
||||
|
||||
27352F31 45434852464A 27442E2735 = () => {
|
||||
39482F 2A2F2E44 274435483129/>
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------ | ----------- | ------------------------------------------------------------------------------------------------- |
|
||||
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
|
||||
| onUpload | دالة | The function called when a user uploads a new image. It receives the `File` object as a parameter |
|
||||
| onRemove | دالة | The function called when the user clicks on the remove button |
|
||||
| onAbort | دالة | The function called when a user clicks on the abort button during image upload |
|
||||
| isUploading | قيمة منطقية | Indicates whether an image is currently being uploaded |
|
||||
| errorMessage | نص | An optional error message to display below the image input |
|
||||
| معطل | قيمة منطقية | If `true`, the entire input is disabled, and the buttons are not clickable |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: راديو
|
||||
image: /images/user-guide/create-workspace/workspace-cover.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/create-workspace/workspace-cover.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
تستخدم عندما يمكن للمستخدمين اختيار خيار واحد فقط من سلسلة من الخيارات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Radio } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
|
||||
const handleRadioChange = (event) => {
|
||||
console.log("Radio button changed:", event.target.checked);
|
||||
};
|
||||
|
||||
const handleCheckedChange = (checked) => {
|
||||
console.log("Checked state changed:", checked);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Radio
|
||||
checked={true}
|
||||
value="Option 1"
|
||||
onChange={handleRadioChange}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
size="large"
|
||||
disabled={false}
|
||||
labelPosition="right"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------------- | ------------------------------------------------------------------------ |
|
||||
| النمط | خصائص `React.CSS` | أنماط إضافية مضمنة للمكون |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية لتصميم إضافي |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان زر الراديو محددًا |
|
||||
| القيمة | نص | التسمية أو النص المرتبط بزر الراديو |
|
||||
| عند التغيير | دالة | The function called when the selected radio button is changed |
|
||||
| عند تغيير الحالة المحددة | دالة | The function called when the `checked` state of the radio button changes |
|
||||
| الحجم | نص | حجم زر الراديو. Options include: `large` and `small` |
|
||||
| معطل | قيمة منطقية | If `true`, the radio button is disabled and not clickable |
|
||||
| موضع التسمية | نص | موضع نص التسمية بالنسبة لزر الراديو. Has two options: `left` and `right` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة الراديو
|
||||
|
||||
يجمع أزرار الراديو ذات الصلة معًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { Radio, RadioGroup } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState("Option 1");
|
||||
|
||||
const handleChange = (event) => {
|
||||
setSelectedValue(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<RadioGroup value={selectedValue} onChange={handleChange}>
|
||||
<Radio value="Option 1" />
|
||||
<Radio value="Option 2" />
|
||||
<Radio value="Option 3" />
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------- | ----------------- | ---------------------------------------------------------------------------------- |
|
||||
| القيمة | string | قيمة زر الراديو المحدد حاليًا |
|
||||
| عند التغيير | دالة | The callback function triggered when the radio button is changed |
|
||||
| onValueChange | دالة | The callback function triggered when the selected value in the group changes. |
|
||||
| الأبناء | `React.ReactNode` | Allows you to pass React components (such as Radio) as children to the Radio Group |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: اختيار
|
||||
image: /images/user-guide/what-is-twenty/20.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/what-is-twenty/20.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
يتيح للمستخدمين اختيار قيمة من قائمة من الخيارات المحددة مسبقًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
|
||||
export const MyComponent = () => {
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<Select
|
||||
className
|
||||
disabled={false}
|
||||
label="Select an option"
|
||||
options={[
|
||||
{ value: 'option1', label: 'Option A', Icon: IconTwentyStar },
|
||||
{ value: 'option2', label: 'Option B', Icon: IconTwentyStar },
|
||||
]}
|
||||
value="option1"
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
|
||||
| معطل | قيمة منطقية | عند ضبطها على `true`، يتم تعطيل تفاعل المستخدم مع المكون |
|
||||
| التسمية | نص | The label to describe the purpose of the `Select` component |
|
||||
| عند التغيير | دالة | The function called when the selected values change |
|
||||
| خيارات | مصفوفة | تمثل الخيارات المتاحة في مكون `الاختيار`. إنها مصفوفة من الكائنات حيث يحتوي كل كائن على `قيمة` (معرف فريد)، `تسمية` (معرف فريد)، و`أيقونة` اختيارية |
|
||||
| القيمة | نص | تمثل القيمة المحددة حاليًا. يجب أن تطابق إحدى خصائص `القيمة` في مصفوفة `الخيارات` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -4,7 +4,7 @@ image: /images/user-guide/notes/notes_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/notes/notes_header.png" alt="Header" />
|
||||
<img src="/images/user-guide/notes/notes_header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
## إدخال نص
|
||||
@@ -12,59 +12,53 @@ image: /images/user-guide/notes/notes_header.png
|
||||
يسمح للمستخدمين بإدخال وتحرير النص.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { TextInput } from "@/ui/input/components/TextInput";
|
||||
|
||||
<Tab title="Usage">
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("Input changed:", text);
|
||||
};
|
||||
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { TextInput } from "@/ui/input/components/TextInput";
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("Key pressed:", event.key);
|
||||
};
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("Input changed:", text);
|
||||
};
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<TextInput
|
||||
className
|
||||
label="Username"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="Invalid username"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("Key pressed:", event.key);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<TextInput
|
||||
className
|
||||
label="Username"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="Invalid username"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| التسمية | string | يمثل التسمية للإدخال. |
|
||||
| عند التغيير | function | الدالة التي تُستدعى عند تغيير قيمة الإدخال. |
|
||||
| عرض كامل | قيمة منطقية | يشير إلى ما إذا كان الإدخال يجب أن يشغل 100% من العرض. |
|
||||
| تعطيل الإختصارات | قيمة منطقية | يشير إلى ما إذا كانت الاختصارات ممكنة للإدخال. |
|
||||
| خطأ | string | يمثل رسالة الخطأ التي سيتم عرضها. عند توفرها، تضيف رمز خطأ على الجانب الأيمن من الإدخال. |
|
||||
| onKeyDown | function | يتم الاستدعاء عندما يتم الضغط على مفتاح عند التركيز على حقل الإدخال. يتلقى `React.KeyboardEvent` كمعلمة |
|
||||
| أيقونة يمين | مكون رمز | مكون أيقونة اختياري معروض على الجانب الأيمن من الإدخال. |
|
||||
|
||||
يقبل المكون أيضًا دعم خصائص HTML أخرى لعناصر الإدخال.
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري للتنسيق الإضافي. |
|
||||
| التسمية | نص | يمثل التسمية للإدخال. |
|
||||
| onChange | وظيفة | الدالة التي تُستدعى عند تغيير قيمة الإدخال. |
|
||||
| عرض كامل | قيمة منطقية | يشير إلى ما إذا كان الإدخال يجب أن يشغل 100% من العرض. |
|
||||
| تعطيل الإختصارات | قيمة منطقية | يشير إلى ما إذا كانت الاختصارات ممكنة للإدخال. |
|
||||
| خطأ | string | يمثل رسالة الخطأ التي سيتم عرضها. عند توفرها، تضيف رمز خطأ على الجانب الأيمن من الإدخال. |
|
||||
| onKeyDown | دالة | يتم الاستدعاء عندما يتم الضغط على مفتاح عند التركيز على حقل الإدخال. يتلقى `React.KeyboardEvent` كمعلمة |
|
||||
| أيقونة يمين | مكون الأيقونة | مكون أيقونة اختياري معروض على الجانب الأيمن من الإدخال. |
|
||||
|
||||
يقبل المكون أيضًا دعم خصائص HTML أخرى لعناصر الإدخال.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## إدخال نص بالحجم التلقائي
|
||||
@@ -72,46 +66,40 @@ export const MyComponent = () => {
|
||||
مكون إدخال نصي يعدل ارتفاعه تلقائيًا بناءً على المحتوى.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { AutosizeTextInput } from "@/ui/input/components/AutosizeTextInput";
|
||||
|
||||
<Tab title="Usage">
|
||||
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { AutosizeTextInput } from "@/ui/input/components/AutosizeTextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("onValidate function fired")}
|
||||
minRows={1}
|
||||
placeholder="Write a comment"
|
||||
onFocus={() => console.log("onFocus function fired")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="Task: "
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | -------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| onValidate | function | الدالة التي ترغب في تفعيلها عند تصديق المستخدم الإدخال. |
|
||||
| الحد الأدنى للأسطر | رقم | عدد الأسطر الأدنى للمساحة النصية. |
|
||||
| نص توضيحي | string | النص التوضيحي الذي ترغب في عرضه عند كون المساحة النصية فارغة. |
|
||||
| onFocus | function | الدالة التي ترغب في تفعيلها عند تركيز المساحة النصية. |
|
||||
| التنوع | string | البديل للإدخال. تشمل الخيارات: `افتراضي`، `أيقونة`، و`زر`. |
|
||||
| عنوان الزر | string | العنوان للزر (فقط للبديل الزر). |
|
||||
| القيمة | string | القيمة الأولية للمساحة النصية. |
|
||||
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("onValidate function fired")}
|
||||
minRows={1}
|
||||
placeholder="Write a comment"
|
||||
onFocus={() => console.log("onFocus function fired")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="Task: "
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ------------------ | ----- | ------------------------------------------------------------- |
|
||||
| onValidate | دالة | الدالة التي ترغب في تفعيلها عند تصديق المستخدم الإدخال. |
|
||||
| الحد الأدنى للأسطر | رقم | عدد الأسطر الأدنى للمساحة النصية. |
|
||||
| النص التوضيحي | نص | النص التوضيحي الذي ترغب في عرضه عند كون المساحة النصية فارغة. |
|
||||
| onFocus | دالة | الدالة التي ترغب في تفعيلها عند تركيز المساحة النصية. |
|
||||
| البديل | نص | البديل للإدخال. تشمل الخيارات: `افتراضي`، `أيقونة`، و`زر`. |
|
||||
| عنوان الزر | نص | العنوان للزر (فقط للبديل الزر). |
|
||||
| القيمة | نص | القيمة الأولية للمساحة النصية. |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مساحة نصية
|
||||
@@ -119,35 +107,31 @@ export const MyComponent = () => {
|
||||
تتيح لك إنشاء إدخالات نصية متعددة الأسطر.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { TextArea } from "@/ui/input/components/TextArea";
|
||||
|
||||
```jsx
|
||||
import { TextArea } from "@/ui/input/components/TextArea";
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('On change function fired')}
|
||||
placeholder="Enter text here"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('On change function fired')}
|
||||
placeholder="Enter text here"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ----------- | ----------------------------------------------------------- |
|
||||
| معطل | قيمة منطقية | يشير إلى ما إذا كانت المساحة النصية معطلة. |
|
||||
| الحد الأدنى للأسطر | رقم | العدد الأدنى للأسطر الظاهرة للمساحة النصية. |
|
||||
| عند التغيير | function | دالة الاستدعاء تُشغّل عند تغيّر محتوى منطقة النص |
|
||||
| نص توضيحي | string | النص المُوضّح عندما تكون منطقة النص فارغة |
|
||||
| القيمة | string | القيمة الحالية لمنطقة النص |
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ------------------ | ----------- | ------------------------------------------------ |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كانت المساحة النصية معطلة. |
|
||||
| الحد الأدنى للأسطر | رقم | العدد الأدنى للأسطر الظاهرة للمساحة النصية. |
|
||||
| onChange | وظيفة | دالة الاستدعاء تُشغّل عند تغيّر محتوى منطقة النص |
|
||||
| نص توضيحي | نص | النص المُوضّح عندما تكون منطقة النص فارغة |
|
||||
| القيمة | نص | القيمة الحالية لمنطقة النص |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: تبديل
|
||||
image: /images/user-guide/table-views/table.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/table-views/table.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { Toggle } from "twenty-ui/input";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Toggle
|
||||
value = {true}
|
||||
onChange = {()=>console.log('On Change event')}
|
||||
color="green"
|
||||
toggleSize = "medium"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| القيمة | قيمة منطقية | The current state of the toggle | `خاطئ` |
|
||||
| عند التغيير | دالة | Callback function triggered when the toggle state changes | |
|
||||
| اللون | string | لون التبديل عند كونه | لون أزرق |
|
||||
| حجم التبديل | نص | حجم التبديل الذي يؤثر على كل من الطول والوزن. لديها خياران: `صغير` و`متوسط` | متوسط |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: نظرة عامة
|
||||
description: مكتبة المكونات لتطبيق Twenty CRM
|
||||
---
|
||||
|
||||
import { CardTitle } from "/snippets/card-title.mdx"
|
||||
|
||||
## مكونات
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card href="/l/ar/twenty-ui/display" img="/images/user-guide/views/filter.png">
|
||||
<CardTitle>Display</CardTitle>
|
||||
Display components for showing information visually
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/twenty-ui/progress-bar" img="/images/user-guide/fields/field.png">
|
||||
<CardTitle>Feedback</CardTitle>
|
||||
Feedback components for user notifications
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/twenty-ui/input" img="/images/user-guide/tips/light-bulb.png">
|
||||
<CardTitle>Input</CardTitle>
|
||||
Input components for user interaction
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/twenty-ui/navigation" img="/images/user-guide/tasks/tasks_header.png">
|
||||
<CardTitle>Navigation</CardTitle>
|
||||
Navigation components for user interface
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Navigation
|
||||
image: /images/user-guide/tasks/tasks_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/tasks/tasks_header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: Breadcrumb
|
||||
image: /images/user-guide/fields/field.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/fields/field.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
Renders a breadcrumb navigation bar.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Breadcrumb } from "@/ui/navigation/bread-crumb/components/Breadcrumb";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const breadcrumbLinks = [
|
||||
{ children: "الصفحة الرئيسية", href: "/" },
|
||||
{ children: "الفئة", href: "/category" },
|
||||
{ children: "الفئة الفرعية", href: "/category/subcategory" },
|
||||
{ children: "الصفحة الحالية" },
|
||||
];
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Breadcrumb className links={breadcrumbLinks} />
|
||||
</BrowserRouter>
|
||||
)
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| روابط | مصفوفة | An array of objects, each representing a breadcrumb link. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -4,7 +4,7 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/what-is-twenty/20.png" alt="Header" />
|
||||
<img src="/images/user-guide/what-is-twenty/20.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
## رابط الاتصال
|
||||
@@ -12,44 +12,40 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
مكون رابط منمق لعرض معلومات الاتصال.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { ContactLink } from 'twenty-ui/navigation';
|
||||
|
||||
import { ContactLink } from 'twenty-ui/navigation';
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('Contact link clicked!', event);
|
||||
};
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('Contact link clicked!', event);
|
||||
};
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ContactLink
|
||||
className
|
||||
href="mailto:example@example.com"
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
example@example.com
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------------ | ----------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند_النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
|
||||
</Tab>
|
||||
return (
|
||||
<Router>
|
||||
<ContactLink
|
||||
className
|
||||
href="mailto:example@example.com"
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
example@example.com
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري للتنسيق الإضافي. |
|
||||
| رابط | نص | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## رابط خام
|
||||
@@ -57,39 +53,36 @@ export const MyComponent = () => {
|
||||
مكون رابط منمق لعرض الروابط.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RawLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
```jsx
|
||||
import { RawLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
return (
|
||||
<Router>
|
||||
<RawLink className href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RawLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RawLink className href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RawLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | function | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| اسم الصنف | string | اسم اختياري لتنسيقات إضافية |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## رابط مستدير
|
||||
@@ -97,38 +90,34 @@ export const MyComponent = () => {
|
||||
رابط مستدير مثبت مع مكون Chip للروابط.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RoundedLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
```jsx
|
||||
import { RoundedLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
return (
|
||||
<Router>
|
||||
<RoundedLink href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RoundedLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RoundedLink href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RoundedLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| عند النقر | function | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## رابط التواصل الاجتماعي
|
||||
@@ -136,33 +125,30 @@ export const MyComponent = () => {
|
||||
روابط اجتماعية منمقة، مع دعم لأنواع متعددة من الروابط الاجتماعية، مثل العناوين الإلكترونية، LinkedIn، وX (أو Twitter).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Usage">
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { SocialLink } from "twenty-ui/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
```jsx
|
||||
import { SocialLink } from "twenty-ui/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<SocialLink
|
||||
type="twitter"
|
||||
href="https://twitter.com/twentycrm"
|
||||
></SocialLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<SocialLink
|
||||
type="twitter"
|
||||
href="https://twitter.com/twentycrm"
|
||||
></SocialLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Props">
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------------ | ----------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| النوع | string | نوع الروابط الاجتماعية. تشمل الخيارات: `url`, `LinkedIn`, و`Twitter` |
|
||||
| عند_النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | -------------------------------------------------------------------- |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| النوع | string | نوع الروابط الاجتماعية. تشمل الخيارات: `url`, `LinkedIn`, و`Twitter` |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
---
|
||||
title: عنصر قائمة
|
||||
image: /images/user-guide/kanban-views/kanban.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/kanban-views/kanban.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
عنصر قائمة متعدد الاستخدامات مصمم للاستخدام في قائمة أو قائمة تنقل.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItem } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
|
||||
const handleButtonClick = (event) => {
|
||||
console.log("Icon button clicked!", event);
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
text="Menu item text"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={true}
|
||||
testId="menu-item-1"
|
||||
onClick={handleMenuItemClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------------- | ------------- | ----------------------------------------------------------------------------------------- |
|
||||
| أيقونة اليسار | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | Specifies the accent color of the menu item. تشمل الخيارات: `افتراضي`, `خطر`, `موضع مؤقت` |
|
||||
| نص | نص | المحتوى النصي لعنصر القائمة |
|
||||
| أزرار الأيقونة | array | مجموعة من الكائنات التي تمثل أيقونات إضافية مرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | Controls the visibility of the tooltip associated with the menu item |
|
||||
| معرف الفحص | نص | السمة data-testid لأغراض الاختبار |
|
||||
| عند النقر | function | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الأشكال
|
||||
|
||||
تتضمن الأشكال المختلفة لمكون عنصر القائمة ما يلي:
|
||||
|
||||
### أمر
|
||||
|
||||
عنصر قائمة على نمط الأوامر داخل القائمة للإشارة إلى اختصارات لوحة المفاتيح.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemCommand } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleCommandClick = () => {
|
||||
console.log("تم النقر على الأمر!");
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
LeftIcon={IconBell}
|
||||
text="الخيار الأول"
|
||||
firstHotKey="⌘"
|
||||
secondHotKey="1"
|
||||
isSelected={true}
|
||||
onClick={handleCommandClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | -------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
|
||||
| secondHotKey | string | The second keyboard shortcut associated with the command |
|
||||
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
|
||||
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### قابلة للسحب
|
||||
|
||||
مكون عنصر قائمة قابل للسحب مصمم ليتم استخدامه في قائمة أو قائمة حيث يمكن سحب العناصر، ويتم تنفيذ إجراءات إضافية عبر أزرار الأيقونات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItemDraggable } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("تم النقر على عنصر القائمة!", event);
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemDraggable
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={false}
|
||||
onClick={handleMenuItemClick}
|
||||
text="عنصر القائمة القابل للسحب"
|
||||
isDragDisabled={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | -------------------------------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | لون العنصر لهجة القائمة. It can either be `default`, `placeholder`, and `danger` |
|
||||
| أزرار الأيقونات | array | مصفوفة الكائنات التي تمثل أزرار الأيقونات الإضافية المرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | Controls the visibility of the tooltip associated with the menu item |
|
||||
| عند_النقر | دالة | وظيفة استدعاء ليتم تشغيلها عند النقر فوق الرابط |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| isDragDisabled | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### التحديد المتعدد
|
||||
|
||||
يوفر طريقة لتنفيذ وظيفة التحديد المتعدد مع مربع اختيار مصاحب.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemMultiSelect } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
|
||||
return (
|
||||
<MenuItemMultiSelect
|
||||
LeftIcon={IconBell}
|
||||
text="الخيار الأول"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | string | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| onSelectChange | دالة | وظيفة استدعاء يتم تشغيلها عند تغيير حالة مربع الاختيار |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Multi Select Avatar
|
||||
|
||||
عنصر قائمة متعدد الخيارات مع صورة رمزية، ومربع اختيار للتحديد، ومحتوى نصي.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MenuItemMultiSelectAvatar } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const imageUrl =
|
||||
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMACwgICggHCwoJCg0MCw0RHBIRDw8RIhkaFBwpJCsqKCQnJy0yQDctMD0wJyc4TDk9Q0VISUgrNk9VTkZUQEdIRf/bAEMBDA0NEQ8RIRISIUUuJy5FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRf/dAAQAAv/aAAwDAQACEQMRAD8Ava1q728otYY98joSCTgZrnbXWdTtrhrfVZXWLafmcAEkdgR/hVltQku9Q8+OIEBcGOT+ID0PY1ka1KH2u8ToqnPLbmIqG7u6LtbQ7RXBRec4Uck9eKXcPWsKDWVnhWSL5kYcFelSf2m3901POh8jP//QoyIAnTuKpXsY82NsksUyWPU5q/L9z8RVK++/F/uCsVsaEURwgA4HtT9x9TUcf3KfUGh//9k=";
|
||||
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="الصورة الرمزبية" />}
|
||||
text="الخيار الأول"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ----------- | -------------------------------------------------------------------- |
|
||||
| الصورة الرمزبية | `ReactNode` | الصورة الرمزبية أو الأيقونة لعرضها على الجانب الأيسر من عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| onSelectChange | دالة | وظيفة استدعاء يتم تشغيلها عند تغيير حالة مربع الاختيار |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### التنقل
|
||||
|
||||
A menu item featuring an optional left icon, textual content, and a right-chevron icon.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemNavigate } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleNavigation = () => {
|
||||
console.log("التنقل إلى صفحة أخرى");
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemNavigate
|
||||
LeftIcon={IconBell}
|
||||
text="الخيار الأول"
|
||||
onClick={handleNavigation}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| عند_النقر | دالة | وظيفة الاستدعاء يتم تنشيطها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### اختيار
|
||||
|
||||
عنصر قائمة يمكن تحديده، مع شكل محتوى متاح (أيقونة ونص) ومُؤشر (أيقونة تحقق) لحالة الاختيار.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemSelect } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("تم اختيار عنصر القائمة");
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconBell}
|
||||
text="الخيار الأول"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ---------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلا |
|
||||
| معطل | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Select Avatar
|
||||
|
||||
A selectable menu item with an avatar, featuring optional left content (avatar and text) and an indicator (check icon) for the selected state.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MenuItemSelectAvatar } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const imageUrl =
|
||||
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMACwgICggHCwoJCg0MCw0RHBIRDw8RIhkaFBwpJCsqKCQnJy0yQDctMD0wJyc4TDk9Q0VISUgrNk9VTkZUQEdIRf/bAEMBDA0NEQ8RIRISIUUuJy5FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRf/dAAQAAv/aAAwDAQACEQMRAD8Ava1q728otYY98joSCTgZrnbXWdTtrhrfVZXWLafmcAEkdgR/hVltQku9Q8+OIEBcGOT+ID0PY1ka1KH2u8ToqnPLbmIqG7u6LtbQ7RXBRec4Uck9eKXcPWsKDWVnhWSL5kYcFelSf2m3901POh8jP//QoyIAnTuKpXsY82NsksUyWPU5q/L9z8RVK++/F/uCsVsaEURwgA4HtT9x9TUcf3KfUGh//9k=";
|
||||
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
testId="menu-item-test"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| -------------- | ------------ | ---------------------------------------------------------------------------- |
|
||||
| الصورة الرمزية | `مكون React` | الصورة الرمزية أو الأيقونة التي سيتم عرضها على الجانب الأيسر من عنصر القائمة |
|
||||
| نص | نص | محتوى النص في عنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| testId | نص | سمة data-testid لأغراض الاختبار |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### اختيار اللون
|
||||
|
||||
A selectable menu item with a color sample for scenarios where you want users to choose a color from a menu.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
استيراد {MenuItemSelectColor} من "twenty-ui/display";
|
||||
|
||||
تصدير المكون الخاص بي = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("تم اختيار عنصر القائمة");
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemSelectColor
|
||||
color="أخضر"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={true}
|
||||
variant="افتراضي"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اللون | نص | لون الثيم المعروض كعينة في عنصر القائمة. الخيارات تشمل: `أخضر`, `تركواز`, `سماوي`, `أزرق`, `أرجواني`, `وردي`, `أحمر`, `برتقالي`, `أصفر`, `رمادي`. |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| البديل | نص | The variant of the color sample. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### تبديل
|
||||
|
||||
عنصر قائمة مع مفتاح تبديل مرتبط للسماح للمستخدمين بتمكين أو تعطيل ميزة معينة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
استيراد {IconBell} من '@tabler/icons-react';
|
||||
|
||||
استيراد {MenuItemToggle} من 'twenty-ui/display';
|
||||
|
||||
تصدير المكون الخاص بي = () => {
|
||||
|
||||
return (
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBell}
|
||||
text="الخيار الأول"
|
||||
toggled={true}
|
||||
toggleSize="صغير"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تُعرض قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص في عنصر القائمة |
|
||||
| مبدل | قيمة منطقية | يشير إلى ما إذا كان مفتاح التبديل في حالة "تشغيل" أو "إيقاف" |
|
||||
| onToggleChange | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل |
|
||||
| حجم التبديل | نص | حجم مفتاح التبديل. يمكن أن يكون إما \ |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: شريط الخطوات
|
||||
image: /images/user-guide/api/api.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/api/api.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
يعرض التقدم من خلال سلسلة من الخطوات المرقمة عن طريق تمييز الخطوة النشطة. يولد حاوية تحتوي على خطوات، يتم تمثيل كل منها بواسطة مكون 'Step'.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { StepBar } from "@/ui/navigation/step-bar/components/StepBar";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<StepBar activeStep={2}>
|
||||
<StepBar.Step>الخطوة 1</StepBar.Step>
|
||||
<StepBar.Step>الخطوة 2</StepBar.Step>
|
||||
<StepBar.Step>الخطوة 3</StepBar.Step>
|
||||
</StepBar>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ---------- | ----- | ----------------------------------------------------------------- |
|
||||
| activeStep | رقم | مؤشر للخطوة النشطة حاليًا. هذا يحدد أي خطوة يجب إبرازها بشكل مرئي |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: التغذية الراجعة
|
||||
image: /images/user-guide/emails/emails_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/emails/emails_header.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
يشير إلى تقدم أو عد تنازلي ويتحرك من اليمين إلى اليسار.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { ProgressBar } from "twenty-ui/feedback";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ProgressBar
|
||||
duration={6000}
|
||||
delay={0}
|
||||
easing="easeInOut"
|
||||
barHeight={10}
|
||||
barColor="#4bb543"
|
||||
autoStart={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ---------------- | ----------- | ------------------------------------------------------------------------------- | ----------------- |
|
||||
| المدة | رقم | إجمالي مدة الرسوم المتحركة لشريط التقدم بالميلي ثانية | 3 |
|
||||
| التأخير | رقم | The delay in starting the progress bar animation in milliseconds | 0 |
|
||||
| التخفيف | نص | وظيفة التخفيف للرسوم المتحركة لشريط التقدم | easeInOut |
|
||||
| ارتفاع الشريط | رقم | ارتفاع الشريط بالبكسل | 24 |
|
||||
| لون الشريط | نص | لون الشريط | gray80 |
|
||||
| التشغيل التلقائي | قيمة منطقية | إذا كان `true`، فإن الرسوم المتحركة لشريط التقدم تبدأ تلقائيًا عند تحميل المكون | `صحيح` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## شريط التقدم الدائري
|
||||
|
||||
يشير إلى تقدم المهمة، ويستخدم غالباً في شاشات التحميل أو الأماكن التي ترغب فيها في إبلاغ العمليات الجارية إلى المستخدم.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { CircularProgressBar } from "@/ui/feedback/progress-bar/components/CircularProgressBar";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <CircularProgressBar size={80} barWidth={6} barColor="green" />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ---------- | ----- | ----------------------- | ----------------- |
|
||||
| الحجم | رقم | حجم شريط التقدم الدائري | 50 |
|
||||
| عرض الشريط | رقم | عرض خط شريط التقدم | 5 |
|
||||
| لون الشريط | نص | لون شريط التقدم | currentColor |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: AI Agents
|
||||
description: Integrate AI capabilities directly into your automation workflows.
|
||||
---
|
||||
|
||||
<Note>
|
||||
This feature is in development and will be available in beta soon.
|
||||
</Note>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Integrate AI capabilities directly into your automation workflows for intelligent data processing and decision-making.
|
||||
|
||||
## Capabilities
|
||||
|
||||
| Feature | الوصف |
|
||||
| ------------------- | ------------------------------------------------ |
|
||||
| **AI actions** | Add AI-powered steps to any workflow |
|
||||
| **Data enrichment** | Automatically enhance records with external data |
|
||||
| **Classification** | Categorize records based on content analysis |
|
||||
| **Summarization** | Generate summaries from text fields |
|
||||
| **Custom prompts** | Define exactly how AI processes your data |
|
||||
|
||||
## Use Cases
|
||||
|
||||
* **Lead scoring**: Automatically score and prioritize inbound leads
|
||||
* **Data cleanup**: Standardize company names and contact information
|
||||
* **Email drafts**: Generate follow-up emails based on meeting notes
|
||||
* **Record routing**: Assign records to the right team member based on content
|
||||
|
||||
## Related
|
||||
|
||||
* [Workflows Overview](/l/ar/user-guide/workflows/overview) — automation basics
|
||||
* [AI Permissions](/l/ar/user-guide/ai/capabilities/permissions-access-control) — access control for AI agents
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: AI Chatbot
|
||||
description: An intelligent assistant that helps you interact with your CRM data using natural language.
|
||||
---
|
||||
|
||||
<Note>
|
||||
This feature is in development and will be available in beta soon.
|
||||
</Note>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
An intelligent assistant that helps you interact with your CRM data using natural language.
|
||||
|
||||
## Capabilities
|
||||
|
||||
| Feature | الوصف |
|
||||
| ---------------------------- | ------------------------------------------------------------------------- |
|
||||
| **Natural language queries** | Ask questions in plain English instead of building filters |
|
||||
| **Full data access** | Query records, relationships, and metrics across your workspace |
|
||||
| **Page context** | Reference "this company" or "this opportunity" based on your current view |
|
||||
| **Conversational** | Follow-up questions maintain context from previous queries |
|
||||
|
||||
## Example Interactions
|
||||
|
||||
### Finding Records
|
||||
|
||||
* "Show me all opportunities over $50,000"
|
||||
* "Find contacts I haven't emailed in 2 weeks"
|
||||
* "List companies in the healthcare industry"
|
||||
|
||||
### Getting Insights
|
||||
|
||||
* "What's my total pipeline value?"
|
||||
* "How many deals closed last month?"
|
||||
* "Which stage has the most stuck opportunities?"
|
||||
|
||||
### Using Page Context
|
||||
|
||||
* "Summarize my interactions with this person" (on a contact page)
|
||||
* "What opportunities are linked to this company?" (on a company page)
|
||||
* "When was this deal last updated?" (on an opportunity page)
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: الأذونات والتحكم في الوصول
|
||||
description: تحكّم بما يمكن لوكلاء الذكاء الاصطناعي الوصول إليه وتعديله في مساحة عملك.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يحترم وكلاء الذكاء الاصطناعي هيكل الأذونات الحالي لديك. وهذا مهم بشكل خاص للفرق التي تريد التحكّم بدقة في ما يمكن لعمليات الذكاء الاصطناعي المؤتمتة الوصول إليه أو تعديله في مساحة عملها.
|
||||
|
||||
## تعيين دور لوكيل ذكاء اصطناعي
|
||||
|
||||
1. اذهب إلى **الإعدادات → الأدوار**
|
||||
2. انقر على الدور الذي ترغب في تعيينه
|
||||
3. افتح علامة التبويب **التعيين**
|
||||
4. ضمن **وكلاء الذكاء الاصطناعي**، انقر **+ تعيين لوكيل ذكاء اصطناعي**
|
||||
5. اختر وكيل الذكاء الاصطناعي من القائمة
|
||||
6. أكد التعيين
|
||||
|
||||
## لماذا نعيّن أدوارًا لوكلاء الذكاء الاصطناعي؟
|
||||
|
||||
| الفائدة | الوصف |
|
||||
| ------------------- | --------------------------------------------------------------------- |
|
||||
| **الأمان** | قيِّد ما يمكن لوكلاء الذكاء الاصطناعي الوصول إليه أو تعديله من بيانات |
|
||||
| **الامتثال** | ضمان أن يعالج الذكاء الاصطناعي فقط البيانات التي يحتاجها |
|
||||
| **التحكم** | منع الإجراءات غير المقصودة الناتجة عن أتمتة الذكاء الاصطناعي |
|
||||
| **إمكانية التدقيق** | تتبُّع الإجراءات التي نفّذها كل وكيل |
|
||||
|
||||
<Note>
|
||||
بالنسبة لوكلاء الذكاء الاصطناعي الذين يعملون ضمن سير العمل، يضمن تعيين الدور ألا يتمكّن الوكيل من الوصول إلى البيانات أو تعديلها خارج نطاقه المقصود — حتى إذا كانت لسير العمل أذونات أوسع.
|
||||
</Note>
|
||||
|
||||
## ذات صلة
|
||||
|
||||
* [الأذونات](/l/ar/user-guide/permissions-access/capabilities/permissions) — معلومات مفصلة حول إنشاء الأدوار وإدارتها
|
||||
* [وكلاء الذكاء الاصطناعي](/l/ar/user-guide/ai/capabilities/ai-agents) — قدرات الذكاء الاصطناعي ضمن سير العمل
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: AI FAQ
|
||||
description: Frequently asked questions about AI features in Twenty.
|
||||
---
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="When will AI features be available?">
|
||||
AI features are currently in development and will be released in beta soon. Stay tuned for updates!
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What AI capabilities are planned?">
|
||||
We're building two main AI capabilities:
|
||||
|
||||
1. **AI Chatbot**: A context-aware assistant that can access your Twenty data and help you with queries
|
||||
2. **AI Agents in Workflows**: Intelligent automation that can process data, make decisions, and execute tasks within your workflows
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Will AI agents have access to all my data?">
|
||||
AI agents will operate under the permission system. You can assign specific roles to AI agents under **Settings → Roles**, giving you full control over what data they can access and what actions they can perform.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How will AI credits work?">
|
||||
AI actions will consume workflow credits based on the complexity of the task and the AI model used. More details will be available when the features launch.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I use my own AI models?">
|
||||
Initially, Twenty will use built-in AI models. Support for custom or external AI models may be added in future releases based on user feedback.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: الذكاء الاصطناعي
|
||||
description: AI-powered features coming soon to Twenty.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/workflows/robot.png" alt="الذكاء الاصطناعي" />
|
||||
</Frame>
|
||||
|
||||
## ما القادم
|
||||
|
||||
Twenty is building AI capabilities to help your team work smarter. We're focusing on two major areas:
|
||||
|
||||
### 1. AI Chatbot
|
||||
|
||||
A conversational assistant that understands your context and has access to all your Twenty data.
|
||||
|
||||
**Key capabilities:**
|
||||
|
||||
* **Full data access**: Query any record, relationship, or metric in your workspace
|
||||
* **Page context awareness**: Reference "this company" or "this opportunity" based on where you are in Twenty
|
||||
* **Natural language**: Ask questions and get answers without navigating menus
|
||||
|
||||
**Example prompts:**
|
||||
|
||||
* "What opportunities are closing this month?"
|
||||
* "Which deals have been in Negotiation for more than 30 days?"
|
||||
* "Summarize my interactions with this person"
|
||||
|
||||
### ٢. AI Agents in Workflows
|
||||
|
||||
Extend your workflows with AI-powered actions and autonomous agents.
|
||||
|
||||
**Key capabilities:**
|
||||
|
||||
* **AI actions**: Use AI to enrich data, classify records, generate summaries, and more
|
||||
* **Autonomous agents**: Let agents execute multi-step tasks within a workflow
|
||||
* **Custom prompts**: Define exactly how AI should process your data
|
||||
|
||||
**حالات الاستخدام:**
|
||||
|
||||
* Automatically categorize inbound leads
|
||||
* Enrich company data from public sources
|
||||
* Generate follow-up email drafts based on meeting notes
|
||||
* Score opportunities based on engagement patterns
|
||||
|
||||
## Permissions and Access Control
|
||||
|
||||
AI agents will be managed through the existing permissions system:
|
||||
|
||||
1. اذهب إلى **الإعدادات → الأدوار**
|
||||
2. Configure which data each AI agent can access
|
||||
3. Set read/write permissions per object
|
||||
|
||||
This ensures AI agents respect your data governance policies and only access what they need.
|
||||
|
||||
## ابق على إطلاع
|
||||
|
||||
We'll update this section as AI features become available. In the meantime:
|
||||
|
||||
* Follow our [GitHub](https://github.com/twentyhq/twenty) for development updates
|
||||
* Join our [Discord](https://discord.gg/twenty) to share feedback and feature requests
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: خطط التسعير
|
||||
description: تعرّف على خطط تسعير Twenty وكيفية التبديل بينها.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
توفر Twenty تسعيرًا مرنًا ليناسب الفرق بمختلف أحجامها، سواء كنت تفضّل الاستضافة السحابية أو الاستضافة الذاتية.
|
||||
|
||||
## الخطط السحابية
|
||||
|
||||
### Pro (سحابي)
|
||||
|
||||
للفرق الجاهزة للتوسّع:
|
||||
|
||||
* جميع ميزات إدارة علاقات العملاء (CRM) الأساسية
|
||||
* مزامنة البريد الإلكتروني والتقويم
|
||||
* عمليات سير العمل والأتمتة
|
||||
* دعم قياسي
|
||||
|
||||
<Note>
|
||||
الميزات المتميزة (SSO وأذونات على مستوى الصف) غير مشمولة في خطة Pro.
|
||||
</Note>
|
||||
|
||||
### المؤسسة (سحابي)
|
||||
|
||||
للفرق الأكبر ذات الاحتياجات المتقدّمة:
|
||||
|
||||
* كل ما في Pro
|
||||
* **ميزات متميزة**: تكامل SSO وأذونات على مستوى الصف
|
||||
* دعم متميز
|
||||
|
||||
## خطط الاستضافة الذاتية
|
||||
|
||||
### مجاني (استضافة ذاتية)
|
||||
|
||||
استضف Twenty على بُنيتك التحتية الخاصة دون أي تكلفة:
|
||||
|
||||
* تشمل جميع ميزات Pro
|
||||
* دعم المجتمع عبر Discord
|
||||
* تحكّم كامل في بياناتك
|
||||
|
||||
### المؤسسة (استضافة ذاتية)
|
||||
|
||||
للفرق التي تحتاج إلى ميزات متميزة أثناء الاستضافة الذاتية:
|
||||
|
||||
* جميع ميزات Pro
|
||||
* **ميزات متميزة**: تكامل SSO وأذونات على مستوى الصف
|
||||
* دعم فريق Twenty
|
||||
* لا يُشترط نشر الشيفرة المخصّصة كمفتوح المصدر قبل التوزيع
|
||||
|
||||
## الميزات المتميزة
|
||||
|
||||
الميزات المتميزة متاحة فقط في خطط المؤسسة (السحابي أو الاستضافة الذاتية):
|
||||
|
||||
* **تكامل SSO**: تسجيل دخول أحادي مع موفّر الهوية لديك
|
||||
* **أذونات على مستوى الصف**: تحكّم دقيق في الوصول على مستوى السجل
|
||||
|
||||
## التبديل بين الخطط
|
||||
|
||||
### الترقية إلى المؤسسة
|
||||
|
||||
1. اذهب إلى **الإعدادات → الفواتير**
|
||||
2. انقر **التبديل إلى المؤسسة**
|
||||
3. أكِّد الترقية
|
||||
|
||||
### الرجوع إلى Pro
|
||||
|
||||
تواصل مع الدعم لتخفيض خطتك.
|
||||
|
||||
### التبديل إلى الفوترة السنوية
|
||||
|
||||
1. اذهب إلى **الإعدادات → الفواتير**
|
||||
2. انقر **التبديل إلى السنوي**
|
||||
3. وفّر مع الفوترة السنوية
|
||||
|
||||
### التبديل إلى الفوترة الشهرية
|
||||
|
||||
تواصل مع الدعم للعودة إلى الفوترة الشهرية.
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: رصيد سير العمل
|
||||
description: Understanding workflow credits, consumption, and how to purchase more.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Credits power your workflow automations in Twenty. Every workflow action consumes credits based on its complexity.
|
||||
|
||||
## Credit Allocation
|
||||
|
||||
Credits are based on your billing cycle, not your plan:
|
||||
|
||||
| Billing Cycle | Credits |
|
||||
| ------------- | --------------- |
|
||||
| شهري | 5 million/month |
|
||||
| سنوي | 50 million/year |
|
||||
|
||||
<Note>
|
||||
The 5 million monthly credits are designed to empower you to run automations without worrying about costs. For most workflows using standard actions, this is more than enough. You'll only need additional credits when running advanced code nodes or AI-powered features.
|
||||
</Note>
|
||||
|
||||
## Credit Consumption
|
||||
|
||||
Different actions consume different amounts of credits:
|
||||
|
||||
| Action Type | استخدام الاعتمادات |
|
||||
| ------------------------------------------------------- | ----------------------- |
|
||||
| **Basic operations** (search, update, create records) | Minimal |
|
||||
| **Complex operations** (code nodes, external API calls) | More credits |
|
||||
| **طلبات الذكاء الاصطناعي** (قريبًا) | Variable based on usage |
|
||||
|
||||
يتم خصم الأرصدة فورًا عند تنفيذ سير العمل.
|
||||
|
||||
## Monitoring Usage
|
||||
|
||||
Track your credit consumption:
|
||||
|
||||
1. اذهب إلى **الإعدادات → الفواتير**
|
||||
2. View your current usage and remaining credits
|
||||
3. Monitor trends to plan for additional credits if needed
|
||||
|
||||
## شراء رصيد إضافي
|
||||
|
||||
Need more credits?
|
||||
|
||||
1. اذهب إلى **الإعدادات → الفواتير**
|
||||
2. Click on the option to purchase additional credit packs
|
||||
3. Select the amount you need
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Billing FAQ
|
||||
description: Frequently asked questions about Twenty pricing and billing.
|
||||
---
|
||||
|
||||
## التسعير
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل يمكنني استخدام Twenty مجانًا أثناء الاستضافة الذاتية؟">
|
||||
نعم، يمكنك استخدام Twenty مجانًا أثناء الاستضافة الذاتية. You will get access to everything included in the Pro (Cloud) plan, except the support from our core-team. الدعم متاح عبر مجتمعنا في Discord.
|
||||
|
||||
If you want to self-host and need the Premium features (SSO and row-level permissions), you can choose the paid Organization (Self-Hosted) license. This also includes support from the Twenty team and removes the requirement to publish custom code as open-source before distributing.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What are the Premium features?">
|
||||
Premium features are only available on the Organization plans (Cloud or Self-Hosted):
|
||||
|
||||
* **SSO integration**: Single Sign-On with your identity provider
|
||||
* **Row-level permissions**: Fine-grained access control at the record level
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل تقدمون مقاعد مجانية للمستخدمين العارضين فقط؟">
|
||||
نحن لا نقدم مقاعد مجانية. التسعير يكون لكل مستخدم وكل مستخدم يحتاج إلى ترخيص للوصول إلى Twenty.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى خطة المؤسسة؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `التبديل إلى المؤسسة`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى الخطة الاحترافية؟">
|
||||
يرجى التواصل مع فريقنا مباشرة عبر الدعم، لا يوجد حاليًا طريقة سهلة للقيام بذلك عبر واجهة المستخدم.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى سنوي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `التبديل إلى السنوي`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى شهري؟">
|
||||
يرجى التواصل مع فريقنا مباشرة عبر الدعم، لا يوجد حاليًا طريقة سهلة للقيام بذلك عبر واجهة المستخدم.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني رؤية استخدام الرصيد الخاص بي؟">
|
||||
ستجد ذلك ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How many workflow credits do we have?">
|
||||
The number of credits depends on your billing cycle, not your plan:
|
||||
|
||||
* **Monthly subscriptions**: 5 million credits per month
|
||||
* **Yearly subscriptions**: 50 million credits per year
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="كيف يعمل استهلاك رصيد سير العمل؟">
|
||||
يستهلك كل إجراء سير عمل الرصيد بناءً على تعقيده.
|
||||
|
||||
* **العمليات الداخلية الأساسية** (مثل البحث والتحديث وإنشاء السجلات) تستهلك عدد قليل من الأرصدة
|
||||
* **More complex operations** like code nodes and requests to external services consume more credits
|
||||
* **طلبات الذكاء الاصطناعي** (قريبًا!) ستستهلك أيضًا المزيد من الأرصدة بناءً على الاستخدام
|
||||
|
||||
يتم خصم الأرصدة فورًا عند تنفيذ سير العمل. يمكنك متابعة استخدامك في **الإعدادات → الفوترة** لمتابعة الاستهلاك والأرصدة المتبقية.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني شراء المزيد من أرصدة سير العمل؟">
|
||||
تستطيع شراء أرصدة إضافية ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## الفوترة
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="أين يمكنني إدارة تفاصيل الفوترة؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تحديث طريقة الدفع الخاصة بي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. ستتمكن من إضافة طريقة دفع جديدة هناك.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تحديث معلومات الفوترة الخاصة بي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. ستتمكن من تعديل معلومات الفوترة هناك.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني الوصول إلى فواتيري؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. سترى جميع فواتيرك في أسفل الشاشة.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: الفوترة
|
||||
description: Understand Twenty pricing and manage your subscription.
|
||||
image: /images/user-guide/setup/pricing.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/setup/pricing.png" alt="الفوترة" />
|
||||
</Frame>
|
||||
|
||||
Twenty offers flexible pricing plans to fit your team's needs. Manage your subscription, track workflow credits, and access invoices all from **Settings → Billing**.
|
||||
|
||||
## What's in this section
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="Pricing Plans" icon="tags" href="/l/ar/user-guide/billing/capabilities/pricing-plans">
|
||||
Learn about Twenty's pricing plans and what's included.
|
||||
</Card>
|
||||
|
||||
<Card title="FAQ" icon="circle-question" href="/l/ar/user-guide/billing/how-tos/billing-faq">
|
||||
Frequently asked questions about pricing and billing.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## At a glance
|
||||
|
||||
| الخطة | Key Features |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Free (Self-Hosted)** | All Pro features, community support |
|
||||
| **Pro (Cloud)** | Everything apart from the Premium features (SSO and row-level permissions), standard support |
|
||||
| **Organization (Cloud)** | All from Pro + the Premium features (SSO and row-level permissions), priority support |
|
||||
| **Organization (Self-Hosted)** | All from Pro + the Premium features (SSO, row-level permissions), Twenty team support, not required to publish your custom code as open-source before distributing |
|
||||
|
||||
## Quick answers
|
||||
|
||||
**Where do I manage billing?**
|
||||
Go to **Settings → Billing** to view your plan, update payment methods, and access invoices.
|
||||
|
||||
**Can I use Twenty for free?**
|
||||
Yes! Self-host Twenty and get all Pro features at no cost.
|
||||
|
||||
**How do I upgrade?**
|
||||
Go to **Settings → Billing** and click **Switch to Organization** or **Switch to Yearly**.
|
||||
|
||||
For more questions, see the [Billing FAQ](/l/ar/user-guide/billing/how-tos/billing-faq).
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: تقويم
|
||||
description: Understanding calendar integration features in Twenty.
|
||||
---
|
||||
|
||||
**Note**: To connect your calendar and configure sync settings, visit [Email & Calendar Setup](/l/ar/user-guide/calendar-emails/overview).
|
||||
|
||||
## How Calendar Integration Works
|
||||
|
||||
Twenty automatically syncs your calendar events and links them to the relevant CRM records, giving you a complete view of your meeting history with contacts and companies.
|
||||
|
||||
## علامة تبويب التقويم
|
||||
|
||||
Next to the Emails tab on records, you'll find a `Calendar` tab that contains the history of meetings scheduled with the record.
|
||||
|
||||
### Available For
|
||||
|
||||
* **الأشخاص**: عرض جميع الاجتماعات المجدولة مع جهة اتصال محددة
|
||||
* **الشركات**: راجع جميع الاجتماعات المتعلقة بشركة وموظفيها
|
||||
* **الفرص**: الوصول إلى تاريخ الاجتماع المتعلق بالشركة المرتبطة بهذه الفرصة
|
||||
|
||||
### عرض تاريخ الاجتماعات
|
||||
|
||||
1. **انتقل إلى سجل**: انتقل إلى أي سجل لشخص، شركة، أو فرصة
|
||||
2. **اختر علامة تبويب التقويم**: انقر على علامة التبويب `التقويم` بجانب علامة البريد الإلكتروني
|
||||
3. **تصفح تاريخ الاجتماعات**: عرض جميع الاجتماعات المجدولة وتفاصيلها
|
||||
4. **الوصول إلى سياق الاجتماع**: شاهد المشاركين في الاجتماع، الأوقات، والمعلومات ذات الصلة
|
||||
|
||||
## Visibility Settings
|
||||
|
||||
Calendar data follows the same visibility settings as emails, ensuring consistent privacy controls across both communication channels.
|
||||
|
||||
## What Gets Synced
|
||||
|
||||
* **External Meetings**: All meetings with contacts outside your organization
|
||||
* **Automatic Linking**: Meetings connect to existing People and Company records based on attendee email addresses
|
||||
* **Meeting Details**: Subject, time, duration, and participants
|
||||
* **Updates**: New calendar events sync automatically
|
||||
|
||||
## ما لا يتم مزامنته
|
||||
|
||||
* **Internal Meetings**: Meetings with only colleagues (same domain) remain private
|
||||
* **Private Events**: Events marked as private in your calendar
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Mailbox
|
||||
description: Understanding email integration features in Twenty.
|
||||
---
|
||||
|
||||
**ملاحظة**: لربط حسابات البريد الإلكتروني الخاصة بك وتكوين إعدادات المزامنة، قم بزيارة [إعدادات البريد الإلكتروني والتقويم](/l/ar/user-guide/calendar-emails/overview).
|
||||
|
||||
## كيف تعمل تكاملات البريد الإلكتروني
|
||||
|
||||
يربط Twenty تلقائيًا رسائل البريد الإلكتروني الواردة من صناديق البريد المتصلة بالسجلات ذات الصلة في إدارة علاقات العملاء (CRM)، مما يحافظ على تاريخ جميع الاتصالات في مكان واحد.
|
||||
|
||||
### Objects Where Emails Can Be Found
|
||||
|
||||
تظهر المحادثات البريدية في ثلاثة أشياء رئيسية:
|
||||
|
||||
* **الأشخاص**: عرض جميع رسائل البريد الإلكتروني التي تم تبادلها مع جهة اتصال محددة
|
||||
* **الشركات**: راجع جميع رسائل البريد الإلكتروني المتعلقة بالشركة وموظفيها
|
||||
* **الفرص**: الوصول إلى سلاسل البريد الإلكتروني المتعلقة بالشركة المرتبطة بهذه الفرصة. لم يتم عرض سلاسل البريد الإلكتروني من الأفراد في الفرصة حتى الآن.
|
||||
|
||||
### عرض سلاسل البريد الإلكتروني
|
||||
|
||||
1. **انتقل إلى سجل**: انتقل إلى أي سجل لشخص، شركة، أو فرصة
|
||||
2. **اختر علامة تبويب البريد الإلكتروني**: انقر على علامة التبويب `البريد الإلكتروني` لعرض الرسائل المتزامنة
|
||||
3. **Open an Email Thread**: Click on any email to open and read the full conversation
|
||||
4. **تصفح التاريخ**: انتقل عبر السجل الكامل للبريد الإلكتروني مع تلك الجهة
|
||||
|
||||
<img src="/images/user-guide/emails/show-inbox.png" style={{width:'100%'}} />
|
||||
|
||||
## ما ستراه
|
||||
|
||||
### عرض سلسلة البريد الإلكتروني
|
||||
|
||||
عند فتح سلسلة بريد إلكتروني، يمكنك:
|
||||
|
||||
* **قراءة المحادثات بالكامل**: شاهد تبادلات البريد الإلكتروني الكاملة
|
||||
* **عرض المشاركين**: شاهد جميع الأشخاص المشاركين في سلسلة البريد الإلكتروني
|
||||
* **تحقق من الأوقات المرسلة**: تعرف على الأوقات التي تم فيها إرسال كل بريد إلكتروني بالضبط
|
||||
* **الوصول إلى السياق**: افهم التاريخ الكامل للاتصالات
|
||||
|
||||
### رؤية البريد الإلكتروني
|
||||
|
||||
بناءً على إعدادات صندوق البريد الخاص بك، قد ترى:
|
||||
|
||||
* **المحتوى الكامل**: نص البريد الإلكتروني الكامل والتفاصيل
|
||||
* **الموضوع + البيانات الوصفية**: سطر الموضوع، المرسل، المتلقي، والتوقيت
|
||||
* **البيانات الوصفية فقط**: معلومات أساسية بدون محتوى البريد الإلكتروني
|
||||
|
||||
## سلوك مزامنة البريد الإلكتروني
|
||||
|
||||
### What Gets Synced
|
||||
|
||||
* **البريد الخارجي**: كافة رسائل البريد الإلكتروني مع جهات الاتصال خارج مؤسستك
|
||||
* **ربط تلقائي**: رسائل البريد الإلكتروني تتصل بسجلات الأشخاص والشركات الحالية
|
||||
* **عناوين متعددة**: رسائل البريد الواردة من أي عنوان ترتبط بسجل جهة الاتصال نفسه
|
||||
* **التحديثات**: تظهر رسائل البريد الإلكتروني الجديدة خلال 5 دقائق
|
||||
|
||||
### ما لا يتم مزامنته
|
||||
|
||||
* **البريد الداخلي**: تبقى رسائل البريد الإلكتروني بين الزملاء (نفس النطاق) خاصة
|
||||
* **البريد الجماعي**: يتم استبعاد قوائم التوزيع والرسائل البريدية الجماعية
|
||||
* **المجلدات المستبعدة**: يتم استبعاد المجلدات التي اخترت عدم مزامنتها (تم تكوينها تحت الإعدادات → الحسابات → البريد الإلكتروني)
|
||||
|
||||
### مزامنة المجلدات الانتقائية (ميزة مختبر)
|
||||
|
||||
تحكم بما تم مزامنته من مجلدات البريد الإلكتروني مع Twenty:
|
||||
|
||||
1. تفعيل "مجلد الرسائل" في الإعدادات → الإصدارات → مختبر
|
||||
2. تكوين المجلدات تحت الإعدادات → الحسابات → البريد الإلكتروني
|
||||
3. اختر مجلدات معينة لتضمينها أو استبعادها (البريد الوارد، المرسل، الأرشيف، المجلدات المخصصة)
|
||||
|
||||
## استكشاف مشكلات مزامنة البريد الإلكتروني
|
||||
|
||||
### مشكلات المزامنة الشائعة
|
||||
|
||||
* **تأخير المزامنة**: تظهر رسائل البريد الإلكتروني خلال 5 دقائق، ولكن قد تستغرق عمليات الاستيراد الأولية وقتًا أطول
|
||||
* **غياب رسائل البريد الإلكتروني**: تحقق مما إذا:
|
||||
* المجلدات مستبعدة في إعدادات مجلد الرسائل
|
||||
* تم تعطيل إنشاء جهة الاتصال تلقائيًا (البريد الإلكتروني يحتاج إلى سجلات Twenty موجودة)
|
||||
* البريد الإلكتروني من زملاء العمل (نفس النطاق) أو قوائم المجموعات
|
||||
* صندوق البريد لا يزال يكمل المزامنة الأولية
|
||||
|
||||
### قيود البريد الإلكتروني
|
||||
|
||||
* **المجلدات النظامية**: قد لا تكون بعض مجلدات البريد الإلكتروني متاحة للمزامنة
|
||||
* **الأسماء المستعارة**: يمكن توصيل حسابات البريد الفعلية فقط (وليس الأسماء المستعارة البريدية)
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Can I Book Meetings from Twenty?
|
||||
description: Information about booking meetings directly from Twenty.
|
||||
---
|
||||
|
||||
## Current Status
|
||||
|
||||
**No, Twenty does not currently support booking meetings directly from the platform.**
|
||||
|
||||
Twenty's calendar integration is designed to **sync and display** your existing calendar events, not to create new ones. All meeting scheduling should be done through your native calendar application (Google Calendar, Microsoft Outlook, etc.).
|
||||
|
||||
## What You Can Do
|
||||
|
||||
* **View meeting history** on People, Companies, and Opportunities records
|
||||
* **See upcoming meetings** with contacts in your CRM
|
||||
* **Track meeting context** alongside email communications
|
||||
* **Auto-create contacts** from meeting participants
|
||||
|
||||
## How to Schedule Meetings
|
||||
|
||||
1. Use your native calendar app (Google Calendar, Outlook, etc.)
|
||||
2. Create the meeting as you normally would
|
||||
3. The meeting will automatically sync to Twenty within 5 minutes
|
||||
4. View the meeting on the relevant CRM records
|
||||
|
||||
## Future Plans
|
||||
|
||||
Meeting creation from within Twenty is on our roadmap. Join our [GitHub discussions](https://github.com/twentyhq/twenty/discussions) to share your use case and help prioritize this feature.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Can I Send Emails from Twenty?
|
||||
description: Information about sending emails directly from Twenty.
|
||||
---
|
||||
|
||||
## Current Status
|
||||
|
||||
Twenty's email integration is designed to **sync and display** your email history. Emails cannot be composed or sent directly from Twenty's interface.
|
||||
|
||||
When you view an email thread on a record page and click **Reply**, you'll be redirected to the original thread in your mailbox (Gmail, Outlook, etc.). This is where you compose and send your reply.
|
||||
|
||||
## What You Can Do Today
|
||||
|
||||
* **View email history** on People, Companies, and Opportunities records
|
||||
* **Read full email threads** with contacts in your CRM
|
||||
* **Track communication context** alongside calendar events
|
||||
* **Auto-create contacts** from email interactions
|
||||
* **Reply via redirect** — click Reply to jump to your mailbox
|
||||
|
||||
## Sending Emails via Workflows
|
||||
|
||||
While you can't send emails manually from Twenty, you **can send emails automatically using Workflows**. This is useful for:
|
||||
|
||||
* Automated follow-ups
|
||||
* Notifications to contacts
|
||||
* Triggered communications based on record changes
|
||||
|
||||
Emails sent via workflows go through your connected mailbox account.
|
||||
|
||||
→ Learn about the [Send Email action](/l/ar/user-guide/workflows/capabilities/workflow-actions#send-email)
|
||||
|
||||
## Email Sequences and Newsletters
|
||||
|
||||
For email sequences and newsletters, we recommend using workflows to connect Twenty to a dedicated email marketing tool.
|
||||
|
||||
<Warning>
|
||||
Mass emails should not be sent directly from your mailbox to protect your domain reputation. Use a dedicated tool for bulk communications.
|
||||
</Warning>
|
||||
|
||||
→ See [How to send emails from workflows](/l/ar/user-guide/workflows/capabilities/send-emails-from-workflows) for setup instructions
|
||||
|
||||
## Future Plans
|
||||
|
||||
Native email composition from within Twenty is on our roadmap. Join our [GitHub discussions](https://github.com/twentyhq/twenty/discussions) to share your use case and help prioritize this feature.
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Can I Track Email Activity on All Objects?
|
||||
description: Understanding email activity tracking across different objects.
|
||||
---
|
||||
|
||||
## Supported Objects
|
||||
|
||||
Email activity is currently available on **three standard objects**:
|
||||
|
||||
| كائن | What You See |
|
||||
| ----------- | ---------------------------------------------------------------- |
|
||||
| **People** | All emails exchanged with that specific contact |
|
||||
| **الشركات** | All emails with anyone from that company (based on email domain) |
|
||||
| **الفرص** | Emails related to the company linked to the opportunity |
|
||||
|
||||
## Why Only These Objects?
|
||||
|
||||
People, Companies, and Opportunities are the core relationship objects where email context adds the most value. Email threads are automatically linked based on:
|
||||
|
||||
* **Email address** → matched to People records
|
||||
* **Email domain** → matched to Company records
|
||||
* **Company relation** → linked to Opportunities
|
||||
|
||||
## كائنات مخصصة
|
||||
|
||||
**Email tracking is not available on custom objects** at this time.
|
||||
|
||||
If you need email context on a custom object, consider:
|
||||
|
||||
* Using a relation field to link your custom object to People or Companies
|
||||
* Viewing email history on the linked People/Company record
|
||||
|
||||
## Future Plans
|
||||
|
||||
Extending email visibility to custom objects is being considered. Share your use case on our [GitHub discussions](https://github.com/twentyhq/twenty/discussions) to help prioritize this feature.
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Connect Several Mailboxes per User
|
||||
description: Connect multiple email accounts for a single user.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty supports **unlimited email accounts per user**. This is useful if you manage multiple inboxes, such as:
|
||||
|
||||
* Personal work email + shared team inbox
|
||||
* Multiple client-facing email addresses
|
||||
* Different email accounts for different roles
|
||||
|
||||
## How to Add Multiple Mailboxes
|
||||
|
||||
1. اذهب إلى **الإعدادات → الحسابات**
|
||||
2. انقر على **إضافة حساب**
|
||||
3. Connect your additional Google or Microsoft account
|
||||
4. Configure sync settings for this mailbox
|
||||
5. Repeat for each mailbox you want to connect
|
||||
|
||||
## Managing Multiple Accounts
|
||||
|
||||
Each connected mailbox has its own settings:
|
||||
|
||||
* **Email visibility**: Choose what teammates can see
|
||||
* **Contact auto-creation**: Enable/disable per mailbox
|
||||
* **Folder selection**: Choose which folders to sync (Lab feature)
|
||||
|
||||
## How Emails Appear
|
||||
|
||||
Emails from all your connected mailboxes are synced to Twenty and appear on:
|
||||
|
||||
* **People records**: Based on the contact's email address
|
||||
* **Company records**: Based on the email domain
|
||||
* **Opportunities**: Based on the linked company
|
||||
|
||||
<Note>Each email shows which mailbox it was sent from/received to, so you can track which account was used for each communication.</Note>
|
||||
|
||||
## Important Notes
|
||||
|
||||
<Warning>Only true mailboxes can be connected. Email aliases that forward to another mailbox cannot be connected separately—they'll sync through the main mailbox.</Warning>
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: I Don't See Emails on Records
|
||||
description: Troubleshooting missing emails on records.
|
||||
---
|
||||
|
||||
## Common Reasons
|
||||
|
||||
### 1. Initial Sync Still in Progress
|
||||
|
||||
Email sync takes time, especially for large mailboxes.
|
||||
|
||||
* **Calendar sync**: Completes in minutes
|
||||
* **Email sync**: Can take several hours for large mailboxes
|
||||
|
||||
**Solution**: Wait up to a few hours for the initial import to complete.
|
||||
|
||||
### ٢. Contact Doesn't Exist in Twenty
|
||||
|
||||
Emails only appear on existing People records. If the contact wasn't created yet:
|
||||
|
||||
* Enable **Contact Auto-Creation** in your mailbox settings
|
||||
* Or manually create the Person record first
|
||||
|
||||
**Solution**: Go to **Settings → Accounts**, select your mailbox, and enable contact auto-creation.
|
||||
|
||||
### 3. Internal Emails Are Excluded
|
||||
|
||||
Emails between colleagues (same email domain) are never synced to maintain privacy.
|
||||
|
||||
**Solution**: This is expected behavior. Only external emails are synced.
|
||||
|
||||
### 4. Email Is from a Group or Distribution List
|
||||
|
||||
Group emails and distribution lists are excluded from sync.
|
||||
|
||||
**Solution**: This is expected behavior.
|
||||
|
||||
### 5. Folder Not Selected for Sync
|
||||
|
||||
If you're using the Message Folder feature, some folders might be excluded.
|
||||
|
||||
**Solution**: Go to **Settings → Accounts**, select your mailbox, and check folder sync settings.
|
||||
|
||||
### 6. Wrong Email Address on Record
|
||||
|
||||
The Person record might have a different email address than the one used in the email.
|
||||
|
||||
**Solution**: Add the correct email address to the Person record.
|
||||
|
||||
## Still Not Working?
|
||||
|
||||
1. Try disconnecting and reconnecting your mailbox
|
||||
2. Contact support if issues persist
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: تقييد رسائل البريد الإلكتروني المُستوردة
|
||||
description: تحكّم في أي رسائل بريد إلكتروني تُستورد إلى Twenty.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
افتراضيًا، يقوم Twenty بمزامنة جميع رسائل البريد الإلكتروني الخارجية من صندوق البريد المتصل لديك. يمكنك تقييد ما يتم استيراده باستخدام **اختيار المجلدات** و**إعدادات الرؤية**.
|
||||
|
||||
## الطريقة 1: اختيار المجلدات (مُوصى بها)
|
||||
|
||||
تحكم بما تم مزامنته من مجلدات البريد الإلكتروني مع Twenty:
|
||||
|
||||
1. اذهب إلى **الإعدادات → الإصدارات → المختبر**
|
||||
2. فعِّل **مجلد الرسائل**
|
||||
3. العودة إلى **الإعدادات → الحسابات**
|
||||
4. اختر حساب البريد الإلكتروني المتصل لديك
|
||||
5. اختر المجلدات التي تريد مزامنتها:
|
||||
|
||||
| مجلد | الوصف |
|
||||
| --------------------- | --------------------------------- |
|
||||
| **صندوق الوارد** | الرسائل الواردة الأساسية |
|
||||
| **المرسلة** | رسائل البريد الصادرة التي أرسلتها |
|
||||
| **الأرشيف** | الرسائل المؤرشفة |
|
||||
| **المجلدات المخصّصة** | أي مجلدات محددة تريدها |
|
||||
|
||||
6. استبعِد المجلدات التي لا تريد مزامنتها (الرسائل غير المرغوب فيها، المهملات، المجلدات الشخصية)
|
||||
|
||||
<Note>هذا يمنحك تحكّمًا دقيقًا في الرسائل التي تظهر في CRM الخاص بك دون مزامنة كل شيء.</Note>
|
||||
|
||||
## الطريقة 2: إعدادات الإنشاء التلقائي لجهات الاتصال
|
||||
|
||||
تحكّم في وقت إنشاء جهات الاتصال من رسائل البريد الإلكتروني:
|
||||
|
||||
1. اذهب إلى **الإعدادات → الحسابات**
|
||||
2. اختر صندوق البريد المتصل لديك
|
||||
3. اختر خيارًا:
|
||||
* **معطَّلة**: لن يتم إنشاء جهات اتصال، لكن ستُزامَن الرسائل مع جهات الاتصال الموجودة
|
||||
* **المرسلة والمستلمة**: يتم إنشاء جهات اتصال من جميع رسائل البريد الإلكتروني الخارجية
|
||||
* **المرسلة فقط**: يتم إنشاء جهات اتصال من الرسائل التي ترسلها فقط
|
||||
|
||||
## ما يتم استبعاده دائمًا
|
||||
|
||||
لا تتم مزامنة هذه الرسائل أبدًا، بغضّ النظر عن الإعدادات:
|
||||
|
||||
* **الرسائل الداخلية**: رسائل البريد الإلكتروني بين الزملاء (نفس النطاق)
|
||||
* **الرسائل الجماعية**: قوائم التوزيع والرسائل الجماعية
|
||||
* **الرسائل غير المرغوب فيها/المهملات**: عادةً ما تُستبعَد مجلدات النظام
|
||||
|
||||
## ملاحظة مهمة
|
||||
|
||||
<Note>نحن لا نوفر عنوان بريد إلكتروني للنسخ الكربوني CC لأجل المزامنة الانتقائية. استخدم ميزة اختيار المجلدات أعلاه لتحقيق نفس مستوى التحكّم.</Note>
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: Calendar & Emails
|
||||
description: Connect your email and calendar accounts to Twenty.
|
||||
image: /images/user-guide/emails/emails_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/emails/emails_header.png" alt="Calendar & Emails" />
|
||||
</Frame>
|
||||
|
||||
## Connection Options
|
||||
|
||||
### حساب Google (Gmail وتقويم Google)
|
||||
|
||||
1. اذهب إلى **الإعدادات → الحسابات**
|
||||
2. انقر على **إضافة حساب**
|
||||
3. Select **Continue with Google**
|
||||
4. السماح لـ Twenty بالوصول إلى Gmail وتقويم Google الخاص بك
|
||||
5. Configure email sync settings (visibility, auto-creation) → click **Next**
|
||||
6. Configure calendar sync settings (visibility, auto-creation) → click **Add Account**
|
||||
7. ستبدأ رسائل البريد الإلكتروني وفعاليات التقويم بالمزامنة تلقائيًا
|
||||
|
||||
### حساب Microsoft (Outlook وتقويم Microsoft)
|
||||
|
||||
1. اذهب إلى **الإعدادات → الحسابات**
|
||||
2. انقر على **إضافة حساب**
|
||||
3. Select **Continue with Microsoft**
|
||||
4. السماح لـ Twenty بالوصول إلى Outlook وتقويم Microsoft الخاص بك
|
||||
5. Configure email sync settings (visibility, auto-creation) → click **Next**
|
||||
6. Configure calendar sync settings (visibility, auto-creation) → click **Add Account**
|
||||
7. ستبدأ رسائل البريد الإلكتروني وفعاليات التقويم بالمزامنة تلقائيًا
|
||||
|
||||
### إعداد SMTP/CalDAV (مزودون آخرون)
|
||||
|
||||
بالنسبة لمزودي البريد الإلكتروني والتقويم الآخرين:
|
||||
|
||||
1. انتقل إلى **الإعدادات → الإصدارات → المعمل** لتفعيل الميزة
|
||||
2. العودة إلى **الإعدادات → الحسابات**
|
||||
3. قم بتكوين إعدادات SMTP للبريد الإلكتروني
|
||||
4. قم بتكوين إعدادات CalDAV للتقويم
|
||||
5. اختبر الاتصال
|
||||
|
||||
### صناديق بريد متعددة
|
||||
|
||||
* **حسابات غير محدودة**: ربط حسابات بريد إلكتروني متعددة لكل مستخدم
|
||||
* **إدارة الحسابات**: التبديل بين صناديق بريد مختلفة
|
||||
* **إعدادات المزامنة**: تكوين إعدادات مختلفة لكل صندوق بريد
|
||||
|
||||
<Warning>
|
||||
لا يمكن ربط إلا صناديق البريد الحقيقية (مثل support@domain.com بصندوق الوارد الخاص بها). لا يمكن ربط الأسماء المستعارة للبريد الإلكتروني التي توجه إلى صندوق بريد آخر بـ Twenty.
|
||||
</Warning>
|
||||
|
||||
## تكوين البريد الإلكتروني
|
||||
|
||||
### ظهور الرسائل
|
||||
|
||||
اختر مستويات مختلفة من الظهور لرسائل بريدك الإلكتروني:
|
||||
|
||||
* **البيانات الوصفية فقط**: مشاركة المعلومات الأساسية فقط (المرسل، المستلم، التاريخ، الوقت)
|
||||
* **العنوان والبيانات الوصفية**: مشاركة سطر العنوان مع البيانات الوصفية
|
||||
* **محتوى البريد الإلكتروني الكامل**: مشاركة محتوى البريد الإلكتروني بالكامل بما في ذلك المرفقات
|
||||
|
||||
### الإنشاء التلقائي للاتصالات
|
||||
|
||||
* **معطل**: لا يتم إنشاء جهات اتصال تلقائيًا
|
||||
* **للرسائل المرسلة والمستلمة**: إنشاء جهات اتصال لجميع التفاعلات البريدية الخارجية
|
||||
* **للرسائل المرسلة فقط**: إنشاء جهات اتصال فقط لرسائل البريد التي ترسلها
|
||||
* **ملاحظة**: لا تتم مزامنة رسائل البريد الداخلية (نفس النطاق) للحفاظ على الخصوصية
|
||||
|
||||
<Note>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.</Note>
|
||||
|
||||
### التحكم بالرسائل التي تتم مزامنتها من خلال اختيار مجلد الرسائل (ميزة معمل)
|
||||
|
||||
تحكم بما تم مزامنته من مجلدات البريد الإلكتروني مع Twenty:
|
||||
|
||||
1. انتقل إلى **الإعدادات → الإصدارات → المعمل** وفعل **مجلد الرسائل**
|
||||
2. العودة إلى **الإعدادات → الحسابات** وحدد حساب البريد الإلكتروني المتصل.
|
||||
3. اختر المجلدات التي تريد مزامنتها:
|
||||
* **البريد الوارد**: رسائل البريد الأولية الواردة
|
||||
* **بريد مرسل**: رسائل البريد الصادرة التي قمت بإرسالها
|
||||
* **مجلدات مخصصة**: أي مجلدات محددة تريد تضمينها
|
||||
* **استبعاد المجلدات**: تخطي المجلدات مثل الرسائل غير المرغوب فيها، المهملات، أو المجلدات الشخصية
|
||||
|
||||
هذا يمنحك التحكم الدقيق في الرسائل التي تظهر في CRM الخاص بك دون مزامنة كل شيء.
|
||||
|
||||
**ما الذي يتم مزامنته:**
|
||||
|
||||
* **رسائل البريد الخارجية**: جميع رسائل البريد الإلكتروني مع جهات اتصال خارجية من المجلدات المحددة
|
||||
* **الرسائل الداخلية**: لا يتم مزامنتها (تبقى رسائل البريد من نفس النطاق خاصة)
|
||||
* **المرفقات**: ستأتي في النصف الأول من 2026
|
||||
|
||||
**ملاحظة**: لا نقدم عنوان بريد إلكتروني لنسخة كربونية للمزامنة الانتقائية. بدلاً من ذلك، استخدم ميزة مجلد الرسائل المذكورة أعلاه لتحقيق نفس مستوى التحكم حول أي الرسائل يتم مزامنتها مع Twenty.
|
||||
|
||||
## تكوين التقويم
|
||||
|
||||
### ظهور الفعاليات
|
||||
|
||||
اختر ما سيكون مرئيًا للمستخدمين الآخرين في مساحة العمل الخاصة بك:
|
||||
|
||||
* **كل شيء**: ستتم مشاركة تفاصيل الحدث كاملة مع فريقك
|
||||
* **البيانات الوصفية**: ستتم مشاركة فقط التاريخ والمشاركين مع فريقك
|
||||
|
||||
### الإنشاء التلقائي للاتصالات للاجتماعات
|
||||
|
||||
* **نعم**: إنشاء جهات اتصال تلقائيًا للمشاركين في الاجتماعات الذين ليسوا في CRM الخاص بك
|
||||
* **لا**: ربط الاجتماعات فقط بجهات الاتصال الموجودة
|
||||
|
||||
<Note>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.</Note>
|
||||
|
||||
### التحكم في الأحداث التي تتم مزامنتها
|
||||
|
||||
* **استيراد الاجتماعات**: استيراد فعاليات التقويم تلقائيًا
|
||||
* **ربط الاتصال**: ربط الاجتماعات بسجلات الأشخاص والشركات
|
||||
|
||||
**ما الذي يتم مزامنته:**
|
||||
|
||||
* **الاجتماعات**: فعاليات التقويم مع المشاركين الخارجيين
|
||||
* **ربط الاتصال**: يتم ربط الأحداث تلقائيًا بسجلات إدارة علاقات العملاء
|
||||
* **فعاليات الفريق**: ظهور تقويم مشترك
|
||||
|
||||
## تردد المزامنة
|
||||
|
||||
**التحديثات كل 5 دقائق**: تتم مزامنة البيانات البريدية والتقويمية تلقائيًا كل 5 دقائق بعد الاستيراد الأولي.
|
||||
|
||||
<Note>
|
||||
**Initial sync timing**: Calendar sync completes quickly (usually within minutes), while email sync takes longer for large mailboxes—up to a few hours depending on volume. Don't worry if you see contacts from calendar events appearing before your email contacts; this is normal behavior.
|
||||
</Note>
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
* [Mailbox capabilities](/l/ar/user-guide/calendar-emails/capabilities/mailbox)
|
||||
* [Troubleshoot missing emails](/l/ar/user-guide/calendar-emails/how-tos/i-dont-see-emails-on-records)
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: لوحات القيادة
|
||||
description: Create and organize dashboards with tabs to visualize your CRM data.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Dashboards in Twenty are organized in a hierarchy: **Dashboards → Tabs → Widgets**. Each dashboard can contain multiple tabs, and each tab contains widgets (charts, numbers, iFrames).
|
||||
|
||||
## Creating a Dashboard
|
||||
|
||||
1. Go to **Dashboards** in the navigation
|
||||
2. Click **+ New Dashboard**
|
||||
3. Give your dashboard a name
|
||||
4. Start adding tabs and widgets
|
||||
|
||||
## Working with Tabs
|
||||
|
||||
Tabs help you organize your dashboard into logical sections.
|
||||
|
||||
### Creating Tabs
|
||||
|
||||
1. In edit mode, click **+ Add Tab**
|
||||
2. Name your tab (e.g., "Pipeline Overview", "Team Performance")
|
||||
3. Add widgets to the tab
|
||||
|
||||
### Duplicating Tabs
|
||||
|
||||
1. Click on the tab you want to duplicate
|
||||
2. Click the **Duplicate** button in the side panel
|
||||
|
||||
## Dashboard Layout
|
||||
|
||||
### Arranging Widgets
|
||||
|
||||
* Drag and drop to position
|
||||
* Resize for emphasis
|
||||
* Group related charts together
|
||||
|
||||
### Duplicating a Dashboard
|
||||
|
||||
1. Exit edit mode (view mode only)
|
||||
2. Open the command bar with **Cmd + K** (or **Ctrl + K** on Windows)
|
||||
3. Select **Duplicate dashboard**
|
||||
|
||||
### أفضل الممارسات
|
||||
|
||||
* **Logical flow**: Arrange from overview to detail
|
||||
* **Visual hierarchy**: Larger charts for key metrics
|
||||
* **Consistent styling**: Use matching colors and fonts
|
||||
|
||||
## Visibility & Access
|
||||
|
||||
### Dashboard Visibility
|
||||
|
||||
Dashboards are visible to everyone who has access to your Twenty workspace. There is no private dashboard option at the moment.
|
||||
|
||||
### المفضلات
|
||||
|
||||
You can add dashboards to your favorites for quick access. This is a personal setting—your favorites are not visible to other users.
|
||||
|
||||
To add a dashboard to favorites, open the dashboard and click the star icon.
|
||||
|
||||
### Timezone Behavior
|
||||
|
||||
Dashboards currently display data based on the timezone of the user viewing them. This means the same dashboard may show different metrics for team members in different regions (e.g., APAC vs. US).
|
||||
|
||||
<Note>
|
||||
**Coming soon**: We will add the ability to set a specific timezone for a dashboard, so all users see consistent data regardless of their location.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**Coming soon**: Dashboard-level filters will allow you to apply filters across all widgets at once, making it faster to explore your data.
|
||||
</Note>
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: الأدوات
|
||||
description: Explore the widget types and visualization options in Twenty.
|
||||
---
|
||||
|
||||
## Available Widgets
|
||||
|
||||
Twenty provides various widget types to visualize your CRM data.
|
||||
|
||||
### Bar Charts
|
||||
|
||||
Display data as horizontal or vertical bars.
|
||||
|
||||
**Best for:**
|
||||
|
||||
* Comparing values across categories
|
||||
* Showing rankings
|
||||
* Tracking metrics by time period
|
||||
|
||||
**Example uses:**
|
||||
|
||||
* Deals by stage
|
||||
* Revenue by sales rep
|
||||
* Contacts added per month
|
||||
|
||||
<Note>
|
||||
**Display limits**: Bar charts can show a maximum of 100 bars (horizontal) or 50 bars (vertical). If you see the warning "Undisplayed data: max X bars per chart", add filters to narrow down your data or change the grouping (e.g., group by week instead of days).
|
||||
</Note>
|
||||
|
||||
### Pie Charts
|
||||
|
||||
Show proportions of a whole.
|
||||
|
||||
**Best for:**
|
||||
|
||||
* Showing composition or distribution
|
||||
* Comparing parts to whole
|
||||
* Highlighting major segments
|
||||
|
||||
**Example uses:**
|
||||
|
||||
* Deal distribution by source
|
||||
* Contact breakdown by industry
|
||||
* Pipeline composition by owner
|
||||
|
||||
### Line Charts
|
||||
|
||||
Display trends over time.
|
||||
|
||||
**Best for:**
|
||||
|
||||
* Tracking changes over time
|
||||
* Identifying trends
|
||||
* Comparing multiple metrics
|
||||
|
||||
**Example uses:**
|
||||
|
||||
* Monthly deal count trend
|
||||
* Revenue growth over quarters
|
||||
* Activity levels over time
|
||||
|
||||
### Number Metrics
|
||||
|
||||
Display single key values prominently.
|
||||
|
||||
**Best for:**
|
||||
|
||||
* Highlighting KPIs
|
||||
* Showing totals or averages
|
||||
* Quick status checks
|
||||
|
||||
**Example uses:**
|
||||
|
||||
* Total pipeline value
|
||||
* Number of open opportunities
|
||||
* Conversion rate
|
||||
|
||||
**Advanced options:**
|
||||
|
||||
* **Ratio**: For Select fields, calculate ratios between values. Go to **Data on display** → select your field → enable the **Ratio** option.
|
||||
* **Prefix & Suffix**: Add custom text before or after the number (e.g., "$" prefix or "%" suffix) for better readability.
|
||||
|
||||
### iFrames
|
||||
|
||||
Embed external tools and content directly in your dashboard.
|
||||
|
||||
**Best for:**
|
||||
|
||||
* Displaying external reports or dashboards
|
||||
* Integrating third-party sales tools
|
||||
* Showing live content from other systems
|
||||
|
||||
**Example uses:**
|
||||
|
||||
* Metrics from your Support tool
|
||||
* Metrics from your dialer
|
||||
* Live content from your Sales sequence tool
|
||||
|
||||
<Note>
|
||||
**Coming soon**: Gauge charts and tables are not yet available but are on our roadmap.
|
||||
</Note>
|
||||
|
||||
## Configuring Widgets
|
||||
|
||||
### Data Source
|
||||
|
||||
1. Select the object to visualize (Opportunities, People, etc.)
|
||||
2. Choose the metric to display (count, sum, average)
|
||||
3. Apply filters to focus on specific data
|
||||
|
||||
### Grouping
|
||||
|
||||
Group data by:
|
||||
|
||||
* Fields (stage, owner, industry)
|
||||
* Time periods (day, week, month, quarter)
|
||||
* Custom segments
|
||||
|
||||
### Styling
|
||||
|
||||
Customize your charts with:
|
||||
|
||||
* Colors and themes
|
||||
* Labels and legends
|
||||
* Size and positioning
|
||||
|
||||
### Duplicating Widgets
|
||||
|
||||
1. Click on the widget
|
||||
2. Open **Options**
|
||||
3. Click **Duplicate widget**
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Dashboards FAQ
|
||||
description: Frequently asked questions about dashboards in Twenty.
|
||||
---
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I create private dashboards?">
|
||||
No, dashboards are currently visible to everyone with access to your Twenty workspace. Private dashboards are not yet available.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why do my metrics look different from my colleague's?">
|
||||
Dashboards currently display data based on the viewer's timezone. If you're in different regions (e.g., APAC vs. US), you may see slightly different numbers for the same dashboard. We're working on adding a timezone setting per dashboard to ensure consistent data across teams.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I export my dashboard?">
|
||||
Exporting dashboards is not available at the moment. This feature is on our roadmap.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I share dashboards with external users?">
|
||||
No, sharing dashboards with users outside your Twenty workspace (non-Twenty users) is not currently supported.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I add a dashboard to my favorites?">
|
||||
Open the dashboard you want to favorite, then click the star icon. Favorites are personal—they won't affect other users.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What's the difference between tabs and widgets?">
|
||||
* **Tabs** organize your dashboard into sections (like pages within the dashboard)
|
||||
* **Widgets** are the individual visualizations (charts, numbers, iFrames) within each tab
|
||||
|
||||
Structure: Dashboard → Tabs → Widgets
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why do I see 'Undisplayed data' warning on my bar chart?">
|
||||
Bar charts have display limits: 100 bars for horizontal charts, 50 for vertical. If your data exceeds this, add filters to narrow down the results or change the grouping (e.g., group by week instead of day).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I filter an entire dashboard at once?">
|
||||
Dashboard-level filters are not available yet, but this feature is on our roadmap. Currently, you need to apply filters to each widget individually.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Are gauge charts or tables available?">
|
||||
ليس بعد. Gauge charts and tables are on our roadmap and will be added in a future release.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I duplicate a dashboard?">
|
||||
1. Make sure you're in view mode (not editing)
|
||||
2. Open the command bar with **Cmd + K** (or **Ctrl + K** on Windows)
|
||||
3. Select **Duplicate dashboard**
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How often does dashboard data refresh?">
|
||||
Widgets update automatically as your CRM data changes:
|
||||
|
||||
* Real-time updates for most metrics
|
||||
* Use the refresh button for a manual update if needed
|
||||
* Historical data is preserved for trend analysis
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: لوحات القيادة
|
||||
description: Learn the basics of reporting and dashboards in Twenty.
|
||||
image: /images/user-guide/reporting/pie-chart.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/reporting/pie-chart.png" alt="لوحات القيادة" />
|
||||
</Frame>
|
||||
|
||||
## Understanding Dashboards
|
||||
|
||||
Dashboards in Twenty provide a visual way to track your key performance metrics and gain insights from your CRM data.
|
||||
|
||||
<img src="/images/user-guide/dashboard/dashboards_v2.png" alt="لوحات القيادة" />
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### لوحات القيادة
|
||||
|
||||
A dashboard is a collection of tabs that display your CRM data at a glance. You can create multiple dashboards for different purposes:
|
||||
|
||||
* Sales performance
|
||||
* Team activity
|
||||
* Pipeline health
|
||||
* Custom metrics
|
||||
|
||||
### علامات التبويب
|
||||
|
||||
Tabs allow you to organize your dashboard into sections. Each tab contains one or more widgets.
|
||||
|
||||
### الأدوات
|
||||
|
||||
Widgets are individual visualizations that display specific data. Types include:
|
||||
|
||||
* Bar charts
|
||||
* Pie charts
|
||||
* Line charts
|
||||
* Number metrics
|
||||
* iFrames
|
||||
|
||||
<Note>
|
||||
**Current limitations**:
|
||||
|
||||
* Exporting dashboards and sharing with external users (non-Twenty users) are not available at the moment.
|
||||
* Gauge charts and tables are not yet available.
|
||||
</Note>
|
||||
|
||||
## البدء
|
||||
|
||||
### Creating Your First Dashboard
|
||||
|
||||
1. Navigate to the **Dashboards** section
|
||||
2. Click **+ New Dashboard**
|
||||
3. Give your dashboard a name
|
||||
4. Add tabs to organize your content
|
||||
5. Add widgets to display your data
|
||||
6. حفظ
|
||||
|
||||
### Adding Widgets
|
||||
|
||||
1. Open a tab on your dashboard
|
||||
2. Click **+ Add Widget**
|
||||
3. Select the widget type
|
||||
4. Choose the data source (object)
|
||||
5. Configure the widget settings
|
||||
6. Save and view your widget
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
* **Start simple**: Begin with a few key metrics and add more over time
|
||||
* **Focus on actionable data**: Display metrics that drive decisions
|
||||
* **Regular review**: Check your dashboards regularly to spot trends
|
||||
* **Share with team**: Make dashboards visible to relevant team members
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
* [Widgets and visualizations](/l/ar/user-guide/dashboards/capabilities/widgets)
|
||||
* [Dashboards FAQ](/l/ar/user-guide/dashboards/how-tos/dashboards-faq)
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Error Handling & Validation
|
||||
description: Review and fix import errors directly in the UI before confirming.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## Pre-Import Validation
|
||||
|
||||
After uploading your file and mapping fields, Twenty validates your data **before** importing. This allows you to catch and fix errors without affecting your existing data.
|
||||
|
||||
## كيف يعمل
|
||||
|
||||
1. **Upload** your CSV file
|
||||
2. **Map** your columns to Twenty fields
|
||||
3. **Review** the potential errors highlighted in yellow
|
||||
4. **Fix errors** directly in the UI
|
||||
5. **Confirm** the import
|
||||
|
||||
<VimeoEmbed videoId="1145273857" title="Video demonstration" />
|
||||
|
||||
## Error Display
|
||||
|
||||
Rows with issues are highlighted in **yellow**. You can:
|
||||
|
||||
* **Edit the cell directly** to fix the error
|
||||
* **Remove the row** to skip it entirely
|
||||
|
||||
This inline editing saves time—no need to go back to your spreadsheet, fix errors, and re-upload.
|
||||
|
||||
## Common Error Types
|
||||
|
||||
### Duplicate Values
|
||||
|
||||
**Cause**: A unique field (email, domain) already exists in Twenty or appears twice in your file.
|
||||
|
||||
**Fix**:
|
||||
|
||||
* Edit the duplicate value in the import UI
|
||||
* Remove one of the duplicate rows
|
||||
|
||||
See [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints) for more details on how uniqueness is enforced.
|
||||
|
||||
### Invalid Format
|
||||
|
||||
**Cause**: Data doesn't match the expected format (e.g., invalid email, wrong date format).
|
||||
|
||||
**Fix**: Edit the cell to use the correct format.
|
||||
|
||||
See [Field Mapping](/l/ar/user-guide/data-migration/capabilities/field-mapping) for the expected format of each field type.
|
||||
|
||||
### Missing Required Fields
|
||||
|
||||
**Cause**: A required field is empty.
|
||||
|
||||
**Fix**: Enter a value in the required field or remove the row.
|
||||
|
||||
### Relation Not Found
|
||||
|
||||
**Cause**: The referenced record doesn't exist (e.g., a Company domain that wasn't imported).
|
||||
|
||||
**Fix**:
|
||||
|
||||
* Import the parent records first
|
||||
* Or correct the reference value
|
||||
|
||||
See [Import Relations](/l/ar/user-guide/data-migration/capabilities/import-relations) for the correct import order and how to link records.
|
||||
|
||||
## Tips for Fewer Errors
|
||||
|
||||
1. **Download the template** to see expected format prior to importing your file
|
||||
2. **Clean your data** in the spreadsheet first
|
||||
3. **Import files in correct order** to import relations (Companies → People → Opportunities)
|
||||
4. **Test with small batches** before full import
|
||||
5. **Check for duplicates** before uploading
|
||||
6. **Limit the size of your file to 10,000 records** per file
|
||||
@@ -0,0 +1,198 @@
|
||||
---
|
||||
title: Field Mapping
|
||||
description: How field mapping works during data import.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## How Field Mapping Works
|
||||
|
||||
When you upload a file, Twenty analyzes your columns and attempts to match them to existing fields.
|
||||
|
||||
### Automatic Mapping
|
||||
|
||||
Twenty tries to match columns based on:
|
||||
|
||||
* Column header names (exact or similar matches)
|
||||
* Data type detection (dates, numbers, emails)
|
||||
* Common field patterns
|
||||
|
||||
<Note>**Quick tip:** Export a few rows from the object you want to import. The exported file will have the exact column names Twenty expects, making automatic mapping seamless during import.</Note>
|
||||
|
||||
### Manual Mapping Options
|
||||
|
||||
For each column, you can:
|
||||
|
||||
* **Map to a field**: Select the matching Twenty field from a dropdown
|
||||
* **Do not map**: Skip the column entirely (data won't be imported)
|
||||
|
||||
<Note>**Fields must exist before import.** The import creates records, not fields. Create custom fields under **Settings → Data Model** before importing.</Note>
|
||||
|
||||
## Field Type Compatibility
|
||||
|
||||
All field types available in the Data Model are supported for import.
|
||||
|
||||
You can also import `id` values to either assign a specific ID to new records or update existing ones.
|
||||
|
||||
<img src="/images/user-guide/fields/all-field-types.png" style={{width:'100%'}} />
|
||||
|
||||
## Data Format Requirements
|
||||
|
||||
<Note>**Some fields have special syntax.** We recommend downloading the sample file before preparing your import to see the expected syntax for each field type.</Note>
|
||||
|
||||
### Address Fields
|
||||
|
||||
Address is a nested field with multiple columns. Some can be left empty.
|
||||
|
||||
* **Address / Address 1**: Street address line 1
|
||||
* **Address / Address 2**: Street address line 2
|
||||
* **Address / City**: City name
|
||||
* **Address / State**: State or province
|
||||
* **Address / Country**: Country name
|
||||
* **Address / Post Code**: Postal/ZIP code
|
||||
|
||||
### Array Fields
|
||||
|
||||
Use the following format:
|
||||
|
||||
```
|
||||
["value1","value2"]
|
||||
```
|
||||
|
||||
### Boolean Fields
|
||||
|
||||
Use `TRUE` or `FALSE` (uppercase) - not `true` or `false`
|
||||
|
||||
### Currency Fields
|
||||
|
||||
Currency is a nested field with two columns that **both must be filled**:
|
||||
|
||||
* **Amount / Amount**: The numeric value (e.g., `1234.56`)
|
||||
* **Amount / Currency**: The currency code (e.g., `USD`, `EUR`)
|
||||
|
||||
### Date Fields
|
||||
|
||||
Supported formats:
|
||||
|
||||
* `YYYY-MM-DD` (recommended)
|
||||
* `MM/DD/YYYY`
|
||||
* `DD/MM/YYYY`
|
||||
* ISO 8601 format
|
||||
|
||||
### Domain Fields
|
||||
|
||||
* It is recommended to use the format `https://domain.com` to avoid creating duplicates, as this is the format used for Companies created by the mailbox and calendar synchronizations
|
||||
* A `Domain Label` and `Domain URL` can be filled: best practice is to fill `domain.com` in the label and `https://domain.com` in the url
|
||||
* Domains must be unique within the Companies object
|
||||
* **Domains must be unique within the file to import**
|
||||
|
||||
### Email Fields
|
||||
|
||||
* Must be valid email format
|
||||
* Emails must be unique within the People object
|
||||
* **Emails must be unique within the file to import**
|
||||
* For additional emails: use **Emails / Primary Email** for the main email, and **Emails / Additional Emails** with this format:
|
||||
|
||||
```
|
||||
["jane@twenty.com","jane.doe@twenty.com"]
|
||||
```
|
||||
|
||||
### Id Fields
|
||||
|
||||
Specifying an `id` during import is optional. Twenty auto-generates one if not provided.
|
||||
|
||||
Use cases for mapping an `id` column:
|
||||
|
||||
* **Set a specific ID**: Choose the UUID for newly created records
|
||||
* **Update existing records**: Match against existing records to update them instead of creating duplicates. In that case, it is recommended to not map the other unique fields: mapping only one unique field ensures a smoother import.
|
||||
|
||||
<Note>If you provide an `id`, it must be in UUID format (e.g., `c776ee49-f608-4a77-8cc8-6fe96ae1e43f`).</Note>
|
||||
|
||||
### JSON Fields
|
||||
|
||||
Use valid JSON format:
|
||||
|
||||
```
|
||||
{"key":"value","key2":"value2"}
|
||||
```
|
||||
|
||||
### Links Fields
|
||||
|
||||
Similar to Domain fields:
|
||||
|
||||
* Fill both the label and URL columns: **Links / Link URL** and **Links / Link Label**
|
||||
* Use full URL format: `https://example.com`
|
||||
* For secondary links, use **Links / Secondary Links** column with this format:
|
||||
|
||||
```
|
||||
[{"url":"https://twenty.com","label":"Twenty"}]
|
||||
```
|
||||
|
||||
### Multi-Select Fields
|
||||
|
||||
Use the **API names** (not the display labels) in the following format:
|
||||
|
||||
```
|
||||
["VALUE1","VALUE2"]
|
||||
```
|
||||
|
||||
See [here](#finding-api-names-for-select-fields) where to find the API names.
|
||||
|
||||
<Note>New select options will not be created automatically by the import. They must be added under **Settings → Data Model** before importing.</Note>
|
||||
|
||||
<Warning>
|
||||
**Import overwrites, it does not add.**
|
||||
|
||||
If a record already has `VALUE2` and `VALUE3` selected, and you import `["VALUE1"]`, the record will only have `VALUE1` after import. The previous selections are replaced, not merged.
|
||||
</Warning>
|
||||
|
||||
### Number Fields
|
||||
|
||||
* Numbers only
|
||||
* Decimals use period: `1234.56`
|
||||
* No thousands separators
|
||||
|
||||
### Phone Fields
|
||||
|
||||
Phone is a nested field with multiple columns that **must be filled**
|
||||
|
||||
* **Phones / Primary Phone Number**: The phone number (e.g., `4159095555`)
|
||||
* **Phones / Primary Phone Country Code**: Country code (e.g., `US`)
|
||||
* **Phones / Primary Phone Calling Code**: Dialing code (e.g., `+1`)
|
||||
|
||||
### Rating Fields
|
||||
|
||||
Use the API name format: `RATING_1`, `RATING_2`, `RATING_3`, `RATING_4`, `RATING_5`
|
||||
|
||||
### حقول العلاقات
|
||||
|
||||
Please see our dedicated article: [Import Relations Between Objects](/l/ar/user-guide/data-migration/capabilities/import-relations)
|
||||
|
||||
### حقول الاختيار
|
||||
|
||||
Use the **API name** of the option (not the display label):
|
||||
|
||||
```
|
||||
VALUE1
|
||||
```
|
||||
|
||||
See [here](#finding-api-names-for-select-fields) where to find the API names.
|
||||
<Note>New select options will not be created automatically by the import. They must be added under **Settings → Data Model** before importing.</Note>
|
||||
|
||||
### Text Fields
|
||||
|
||||
* No special formatting required
|
||||
* Leading/trailing spaces are trimmed
|
||||
|
||||
## Finding API Names
|
||||
|
||||
For Select, Multi-Select, and Array fields with predefined options, you must use the **API names**, not the display labels.
|
||||
|
||||
### How to Find API Names
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object and field
|
||||
3. Enable **Advanced mode** (toggle at the bottom right of the settings page)
|
||||
4. View the API name for each option
|
||||
|
||||
<VimeoEmbed videoId="1144910809" title="Video demonstration" />
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: تنسيقات الملفات المدعومة
|
||||
description: تنسيقات الملفات المدعومة لاستيراد البيانات في Twenty.
|
||||
---
|
||||
|
||||
## التنسيقات المدعومة
|
||||
|
||||
يدعم Twenty ثلاث تنسيقات ملفات للاستيراد:
|
||||
|
||||
| التنسيق | الامتداد | الملاحظات |
|
||||
| ---------------- | -------- | ----------------------- |
|
||||
| **CSV** | .csv | موصى به، الأكثر توافقاً |
|
||||
| **Excel** | .xlsx | تنسيق Excel الحديث |
|
||||
| **Excel (قديم)** | .xls | تنسيق Excel الأقدم |
|
||||
|
||||
## متطلبات الملف
|
||||
|
||||
| متطلب | القيمة |
|
||||
| ----------------------- | ------------------------------------------ |
|
||||
| **الترميز** | يُوصى باستخدام UTF-8 |
|
||||
| **الحد الأقصى للسجلات** | 10,000 سجل لكل ملف |
|
||||
| **الهيكل** | يجب أن يحتوي الصف الأول على عناوين الأعمدة |
|
||||
| **المحتوى** | نوع كائن واحد لكل ملف |
|
||||
|
||||
## أفضل الممارسات لملفات CSV
|
||||
|
||||
* **المحدد**: استخدم الفاصلة (`,`) أو الفاصلة المنقوطة (`;`)
|
||||
* **محدد النص**: استخدم علامات الاقتباس المزدوجة (`\"`) للنص الذي يحتوي على فواصل
|
||||
* **نهايات الأسطر**: Windows (CRLF) أو Unix (LF) كلاهما مدعومان
|
||||
* **القيم الفارغة**: اترك الخلايا فارغة، لا تستخدم "NULL" أو "N/A"
|
||||
|
||||
## أفضل الممارسات لبرنامج Excel
|
||||
|
||||
عند التصدير من Excel:
|
||||
|
||||
* أزل الصيغ (صدّر القيم فقط)
|
||||
* احذف الصفوف الفارغة في النهاية
|
||||
* تأكد من عدم وجود خلايا مدمجة
|
||||
* استخدم الورقة الأولى فقط
|
||||
|
||||
## مجموعات البيانات الكبيرة
|
||||
|
||||
بالنسبة لمجموعات البيانات التي تزيد عن 10,000 سجل:
|
||||
|
||||
* قسّمها إلى عدة ملفات
|
||||
* أو استخدم [الاستيراد عبر API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api) لسجلات غير محدودة
|
||||
|
||||
<Note>بالنسبة لعمليات الترحيل الكبيرة جداً (100,000+ سجل)، تكون واجهة API أسرع بكثير وأكثر موثوقية من عمليات استيراد CSV.</Note>
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Import Relations Between Objects
|
||||
description: Import relationships between records via CSV.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty supports importing relationships between objects during CSV import. This allows you to link records (e.g., attach People to Companies) as part of your data migration.
|
||||
|
||||
<Note>**Currently supported for import**: One-to-many relations pointing to a single object type on each side (e.g., People → Companies). Relations pointing to multiple object types are not yet supported in import/export.</Note>
|
||||
|
||||
## How Relations Work in Twenty
|
||||
|
||||
### One to Many / Many to One
|
||||
|
||||
Twenty supports standard relations where one record links to many others:
|
||||
|
||||
* **One Company → Many People**: A company can have multiple employees, but each person belongs to one company
|
||||
* **One Company → Many Opportunities**: A company can have multiple deals, but each opportunity belongs to one company
|
||||
|
||||
### Relations That Can Point to Multiple Object Types
|
||||
|
||||
Some relations can connect to different types of objects. This works in two ways:
|
||||
|
||||
**Pattern 1: Many records linking to one record each from different object types**
|
||||
|
||||
Several Notes, Tasks, or Activities can each be attached to multiple object types at once:
|
||||
|
||||
* **Notes** can be linked to one Person, one Company, and one Opportunity simultaneously
|
||||
* **Tasks** can be linked to one Person, one Company, and one Opportunity simultaneously
|
||||
|
||||
Here, the Notes/Tasks are on the "many" side. Each links to one record per object type.
|
||||
|
||||
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
|
||||
|
||||
**Pattern 2: One record receiving links from many records of different object types**
|
||||
|
||||
A Project can receive links from multiple records across different object types:
|
||||
|
||||
* **A Project** can have many People linked to it, many Companies linked to it, and many Notes attached to it
|
||||
|
||||
Here, the Project is on the "one" side. Multiple records from different objects can all link to the same Project.
|
||||
|
||||
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
|
||||
|
||||
<Warning>
|
||||
**Import/Export limitation**: Relations that point to multiple object types (like Notes → People/Companies/Opportunities) are **not yet supported** in CSV import or export.
|
||||
|
||||
* **Import**: Only one-to-many relations pointing to a single object type on each side can be imported
|
||||
* **Export**: Columns for relations pointing to multiple object types are currently left empty
|
||||
|
||||
This is on our roadmap.
|
||||
</Warning>
|
||||
|
||||
### What's Not Supported Today
|
||||
|
||||
**Many to Many relations** are not yet available. For example, you cannot currently create a relation where:
|
||||
|
||||
* Many People are linked to many Projects
|
||||
|
||||
<Note>Many to Many relations are planned for H1 2026.</Note>
|
||||
|
||||
## Linking Records During Import
|
||||
|
||||
<Note>**Reminder**: Only one-to-many relations pointing to a single object type can be imported (e.g., People → Companies). Relations pointing to multiple object types (e.g., Notes → People/Companies/Opportunities) are not yet supported.</Note>
|
||||
|
||||
### Step 1: Identify the "One" and "Many" Sides
|
||||
|
||||
First, determine which object is on the "one" side and which is on the "many" side of the relationship.
|
||||
|
||||
**Example**:
|
||||
|
||||
* **Company** is the "one" side (one company has many employees)
|
||||
* **People** is the "many" side (each person belongs to one company)
|
||||
|
||||
### Step 2: Ensure the "One" Side Records Exist
|
||||
|
||||
Before importing the "many" side, the "one" side records must already exist in Twenty.
|
||||
|
||||
* Import or create the "one" side records first (e.g., Companies)
|
||||
* Validate their unique identifier. This can be:
|
||||
* The `id` (Twenty's UUID)
|
||||
* A field set as unique (e.g., `domain` for Companies, or an external ID from your previous system)
|
||||
|
||||
<Warning>The import will fail if a reference is made to a record that does not exist.</Warning>
|
||||
|
||||
### Step 3: Prepare Your CSV File
|
||||
|
||||
Add a column in your "many" side CSV file that references the "one" side record.
|
||||
|
||||
**Example**: For a People CSV file linking to Companies:
|
||||
|
||||
```
|
||||
firstName,lastName,email,companyDomain
|
||||
John,Smith,john@acme.com,https://acme.com
|
||||
Jane,Doe,jane@widgets.co,https://widgets.co
|
||||
```
|
||||
|
||||
**Important**:
|
||||
|
||||
* The value must **exactly match** the unique field on the Company record
|
||||
* For domains, use the **Domain URL** (e.g., `https://acme.com`), not the Domain Label
|
||||
* Map only **one** unique identifier per relation: this leads to a smoother import
|
||||
|
||||
### Step 4: Ensure the Relation Field Exists
|
||||
|
||||
Before uploading your file, make sure the relation field exists between your objects.
|
||||
|
||||
If it doesn't exist:
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select your object (e.g., People)
|
||||
3. Create a relation field pointing to the target object (e.g., Company)
|
||||
|
||||
### Step 5: Upload and Map the Relation
|
||||
|
||||
1. Upload your CSV file via the import UI
|
||||
2. In the field mapping step, find your relation column (e.g., `companyDomain`)
|
||||
3. Map it to the relation field (e.g., Company)
|
||||
4. Twenty will automatically link each record to the matching parent
|
||||
|
||||
### Available Unique Fields for Relations
|
||||
|
||||
| كائن | Unique Fields Available |
|
||||
| ------------------------------------- | --------------------------------------- |
|
||||
| **الشركات** | `id`, `domain`, any custom unique field |
|
||||
| **People** | `id`, `email`, any custom unique field |
|
||||
| **أعضاء مساحة العمل** | `id`, `email` (not name) |
|
||||
| **Other standard and custom objects** | `id`, any field marked as unique |
|
||||
|
||||
<Note>**Linking to Workspace Members**: When the relation points to Workspace Members (your team logging into Twenty), reference them by their **email address**, not their name.</Note>
|
||||
|
||||
<Note>We recommend using `domain` for Companies and `email` for People, as these are human-readable and easy to maintain in spreadsheets.</Note>
|
||||
|
||||
<Note>**Reminder**: Soft-deleted records (visible under Command Menu → See deleted records) count toward uniqueness criteria. If you import a record with the same unique value as a deleted record, the deleted record will be restored. See [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints) for more details.</Note>
|
||||
|
||||
## Import Order Rule
|
||||
|
||||
<Warning>
|
||||
**Always import the "one" side first!**
|
||||
|
||||
1. **Companies** first (no dependencies)
|
||||
2. **People** second (linked to Companies)
|
||||
3. **Opportunities** third (linked to Companies/People)
|
||||
4. **Custom objects** following their dependencies
|
||||
|
||||
The parent record must exist before you can reference it.
|
||||
</Warning>
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Uniqueness Constraints
|
||||
description: How Twenty enforces data uniqueness during import.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty enforces uniqueness on certain fields to prevent duplicate records and ensure data integrity. Understanding these constraints is essential for successful imports.
|
||||
|
||||
## Default Unique Fields
|
||||
|
||||
| كائن | Unique Fields |
|
||||
| ---------------- | ---------------------- |
|
||||
| **People** | `id`, `email` |
|
||||
| **الشركات** | `id`, `domain` |
|
||||
| **كائنات مخصصة** | `id` only (by default) |
|
||||
|
||||
<Note>The `id` field is Twenty's internal identifier, auto-generated for each record. It uses UUID format (e.g., `c776ee49-f608-4a77-8cc8-6fe96ae1e43f`).</Note>
|
||||
|
||||
## Custom Unique Fields
|
||||
|
||||
You can define additional unique fields under **Settings → Data Model**:
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object
|
||||
3. Click on a field
|
||||
4. Enable **Unique** in field settings
|
||||
|
||||
### Use Cases for Custom Unique Fields
|
||||
|
||||
* **External IDs**: Store IDs from other systems (Salesforce ID, HubSpot ID)
|
||||
* **Business identifiers**: Employee numbers, customer codes
|
||||
* **Alternative contact info**: LinkedIn profile, phone number
|
||||
|
||||
<Note>The field name `id` is reserved for Twenty's internal ID. Use a different name like `externalId` or `legacyId` for external identifiers.</Note>
|
||||
|
||||
## Import Behavior
|
||||
|
||||
### Creating New Records
|
||||
|
||||
If a unique field value doesn't exist, a new record is created.
|
||||
|
||||
### Updating Existing Records
|
||||
|
||||
If a unique field value matches an existing record, that record is **updated** with the new data.
|
||||
<Warning>To **update existing records**, it is recommended to **only match one unique field**.</Warning>
|
||||
|
||||
### Soft-Deleted Records
|
||||
|
||||
<Warning>
|
||||
**Deleted records count toward uniqueness.**
|
||||
|
||||
Soft-deleted records (visible under Command Menu → See deleted records) are included in uniqueness checks. If you import a record with the same unique value as a deleted record, the deleted record will be **restored** with the new data.
|
||||
</Warning>
|
||||
|
||||
## Duplicate Detection During Import
|
||||
|
||||
During the validation phase:
|
||||
|
||||
* Duplicates within your file are highlighted in yellow
|
||||
* You can edit or remove duplicate rows from the UI before starting the import
|
||||
|
||||
<VimeoEmbed videoId="1145246326" title="Video demonstration" />
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
1. **Remove duplicates** from your file before importing
|
||||
2. **Check for existing records** in Twenty before importing
|
||||
3. **Use external IDs** when migrating from other systems
|
||||
4. **Include unique fields** if you want to update existing records
|
||||
@@ -0,0 +1,209 @@
|
||||
---
|
||||
title: Export Your Data
|
||||
description: Complete step-by-step guide to exporting data from Twenty.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Export your workspace data to CSV for backups, reporting, or migration.
|
||||
|
||||
**حالات الاستخدام:**
|
||||
|
||||
* **Regular backups** — keep copies of your data
|
||||
* **External reporting** — analyze data in Excel, Google Sheets, or BI tools
|
||||
* **Migration** — move data to another system
|
||||
* **Bulk updates** — export, edit, and re-import to update records
|
||||
|
||||
## What You Need to Know
|
||||
|
||||
### Export Limits
|
||||
|
||||
* **Maximum 20,000 records** per export
|
||||
* Only **visible columns** are exported
|
||||
* Only **filtered records** are exported (based on your current view)
|
||||
|
||||
<Note>For larger exports (20,000+ records), use filters to export in batches or use the [API](/l/ar/developers/extend/capabilities/apis).</Note>
|
||||
|
||||
### الصلاحيات
|
||||
|
||||
You need the **"Export CSV"** permission to export data. Contact your workspace admin if you don't have this option.
|
||||
|
||||
## Step 1: Navigate to the Object
|
||||
|
||||
Go to the object you want to export:
|
||||
|
||||
* **People** — for contacts
|
||||
* **Companies** — for organizations
|
||||
* **Opportunities** — for deals
|
||||
* **Custom objects** — any object you've created
|
||||
|
||||
## Step 2: Configure Your View
|
||||
|
||||
**Important:** The export includes only what's visible in your current view.
|
||||
|
||||
### Add/Remove Columns
|
||||
|
||||
1. Click **Options → Fields** (or the **+** at the end of columns)
|
||||
2. Check the fields you want to export
|
||||
3. Uncheck fields you don't need
|
||||
|
||||
### Filter Records (Optional)
|
||||
|
||||
If you only need a subset of data:
|
||||
|
||||
1. Click **Filter**
|
||||
2. Add filter conditions (e.g., "Created date > January 1, 2024")
|
||||
3. Only matching records will be exported
|
||||
|
||||
### Sort Records (Optional)
|
||||
|
||||
1. Click a column header to sort
|
||||
2. The export will follow your sort order
|
||||
|
||||
<Note>**Create a dedicated export view.** Save a view specifically configured for exports so you don't need to reconfigure each time.</Note>
|
||||
|
||||
## Step 3: Export the Data
|
||||
|
||||
1. Click the **⋮** icon on the top right of the table
|
||||
2. Select **Export view**
|
||||
3. Choose where to save the CSV file
|
||||
4. Wait for the download to complete
|
||||
|
||||
## What Gets Exported
|
||||
|
||||
| Included | Not Included |
|
||||
| -------------------------------- | ---------------------- |
|
||||
| All visible columns | Hidden columns |
|
||||
| Records matching current filters | Filtered-out records |
|
||||
| Custom field values | Fields not in the view |
|
||||
| Record IDs | File attachments |
|
||||
| Relation IDs | Images |
|
||||
|
||||
### حقول العلاقات
|
||||
|
||||
Relation IDs are only exported on the **"many" side** of a relationship:
|
||||
|
||||
* **People export** includes a `companyId` column (People → Company relation)
|
||||
* **Companies export** does NOT include `peopleIds` (Companies is the "one" side)
|
||||
|
||||
This means you can use the People export to re-import and maintain the Company link, but you'll need to re-import People after Companies to recreate the relationships.
|
||||
|
||||
## Exporting for Specific Purposes
|
||||
|
||||
### For Backups
|
||||
|
||||
1. Create a view with **all fields** visible
|
||||
2. Remove all filters to include all records
|
||||
3. Export each object type separately
|
||||
4. Store exports in a secure location
|
||||
5. Set a recurring reminder (weekly/monthly)
|
||||
|
||||
### For External Reporting
|
||||
|
||||
1. Include only the fields you need for analysis
|
||||
2. Apply filters to focus on relevant data
|
||||
3. Consider sorting by the field you'll analyze
|
||||
|
||||
### For Bulk Updates
|
||||
|
||||
1. Export the records you want to update
|
||||
2. Include the unique identifier (`email`, `domain`, or `id`)
|
||||
3. Edit the exported file
|
||||
4. Re-import to update records
|
||||
See: [How to Update Existing Records](/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import)
|
||||
|
||||
### For Migration
|
||||
|
||||
If you're exporting to migrate to another system:
|
||||
|
||||
1. **Export each object separately** — People, Companies, Opportunities, etc.
|
||||
2. **Include ID fields** — these help maintain relationships
|
||||
3. **Document field mappings** — note how Twenty fields map to your target system
|
||||
|
||||
## Handling Large Datasets (20,000+ Records)
|
||||
|
||||
The export limit is 20,000 records. For larger datasets:
|
||||
|
||||
### Option 1: Export in Batches
|
||||
|
||||
1. Add a filter (e.g., "Created date" ranges)
|
||||
2. Export the first batch
|
||||
3. Change the filter
|
||||
4. Export the next batch
|
||||
5. Combine files in your spreadsheet
|
||||
|
||||
**Example filters for batching:**
|
||||
|
||||
* By date range (January, February, March...)
|
||||
* By owner (Team member A, Team member B...)
|
||||
* By status (Active, Inactive...)
|
||||
|
||||
### Option 2: Use the API
|
||||
|
||||
The API has no record limit:
|
||||
|
||||
1. Get your API key from **Settings → Developers**
|
||||
2. Use the GraphQL API to query records
|
||||
3. Process results in your application
|
||||
|
||||
See: [API Documentation](/l/ar/developers/extend/capabilities/apis)
|
||||
|
||||
## Tips and Best Practices
|
||||
|
||||
### Create Export Views
|
||||
|
||||
Save views configured specifically for exports:
|
||||
|
||||
1. Configure columns and filters
|
||||
2. Click **View options** → **Save as new view**
|
||||
3. Name it "Export - [Purpose]"
|
||||
|
||||
### Secure Your Exports
|
||||
|
||||
Exported files may contain sensitive data:
|
||||
|
||||
* Store in secure locations
|
||||
* Delete old exports when no longer needed
|
||||
* Be careful sharing export files
|
||||
|
||||
### Check Before Exporting
|
||||
|
||||
<Check>Correct columns are visible</Check>
|
||||
<Check>Filters are set correctly (or removed for full export)</Check>
|
||||
<Check>You have Export permission</Check>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why are some columns missing from my export?">
|
||||
Only visible columns are exported. Add the columns you need via **Options → Fields** before exporting.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why doesn't my export have all records?">
|
||||
Check your filters. The export only includes records matching your current view filters. Remove filters to export all records.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I export more than 20,000 records?">
|
||||
Not in a single export. Use filters to export in batches, or use the API for larger datasets.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What format is the export file?">
|
||||
CSV (Comma Separated Values). Opens in Excel, Google Sheets, or any spreadsheet application.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Are relation IDs included?">
|
||||
Yes, but only on the "many" side of relationships. For example, a People export includes `companyId`, but a Companies export does not include people IDs.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I automate exports?">
|
||||
Not directly through the UI. Use the API to build automated export workflows.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
* [How to Update Existing Records](/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import) — edit and re-import your export
|
||||
* [How to Import Data via API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api) — for large datasets
|
||||
* [API Documentation](/l/ar/developers/extend/capabilities/apis) — build custom export workflows
|
||||
@@ -0,0 +1,430 @@
|
||||
---
|
||||
title: Fix Import Errors
|
||||
description: Complete troubleshooting guide for resolving CSV import errors.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Import not working? This guide helps you identify and fix common import errors step by step.
|
||||
|
||||
## How Import Validation Works
|
||||
|
||||
After uploading your file and mapping columns, Twenty validates your data:
|
||||
|
||||
1. **Validation runs** — Twenty checks each row for errors
|
||||
2. **Errors are highlighted** — problematic rows appear in **yellow**
|
||||
3. **You can fix in-place** — edit cells directly in the import UI
|
||||
4. **Or remove rows** — skip problematic records entirely
|
||||
|
||||
<Note>**Fix errors in the UI.** You don't need to go back to your spreadsheet. Edit cells directly during import to save time.</Note>
|
||||
|
||||
## Step-by-Step Troubleshooting
|
||||
|
||||
### Step 1: Identify the Error Type
|
||||
|
||||
Click on a highlighted row to see the specific error message. Common error types:
|
||||
|
||||
| Error Message | What It Means |
|
||||
| --------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| Duplicate values highlighted in yellow | Value already exists in Twenty or appears twice in your file |
|
||||
| `{field} is not a valid {type}` (hover on yellow cell) | Data doesn't match expected format |
|
||||
| Required field highlighted | A required field is empty |
|
||||
| `Can't connect to {object}. No unique record found...` (import fails) | Referenced record doesn't exist |
|
||||
| `Too many records. Up to 10000 allowed` (upload blocked) | File has more than 10,000 records |
|
||||
|
||||
### Step 2: Fix the Error
|
||||
|
||||
Follow the specific instructions below for each error type.
|
||||
|
||||
---
|
||||
|
||||
## Error: Duplicate Value
|
||||
|
||||
### ما ستراه
|
||||
|
||||
Rows with duplicate values are **highlighted in yellow** in the import UI before the import starts.
|
||||
|
||||
### What It Means
|
||||
|
||||
A unique field (email, domain) either:
|
||||
|
||||
* Already exists in Twenty
|
||||
* Appears twice in your file
|
||||
|
||||
### How to Fix
|
||||
|
||||
**Option 1: Edit the duplicate value**
|
||||
|
||||
1. Click the cell with the error
|
||||
2. Change to a unique value
|
||||
3. Continue with import
|
||||
|
||||
**Option 2: Remove the duplicate row**
|
||||
|
||||
1. Click the X next to the row
|
||||
2. The row will be skipped during import
|
||||
|
||||
**Option 3: Let Twenty update the existing record**
|
||||
|
||||
1. Ensure your file includes a unique identifier (`email`, `domain`, or `id`)
|
||||
2. Map the unique identifier field
|
||||
3. Twenty will update the existing record instead of creating a duplicate
|
||||
|
||||
<Note>
|
||||
**You can update unique fields too.**
|
||||
|
||||
* If you keep the `id` but change the `email` → the email will be updated
|
||||
* If you keep the `email` but change the `id` → the id will be updated
|
||||
|
||||
As long as one unique identifier matches, Twenty updates the record.
|
||||
</Note>
|
||||
|
||||
### How to Prevent This Error
|
||||
|
||||
Before importing:
|
||||
|
||||
1. Sort your spreadsheet by the unique field
|
||||
2. Remove duplicate rows
|
||||
3. Check if records already exist in Twenty
|
||||
|
||||
<Warning>
|
||||
**Soft-deleted records count toward uniqueness.**
|
||||
|
||||
Check Command Menu → See deleted records. Records there still enforce uniqueness. Permanently delete them or restore and update.
|
||||
</Warning>
|
||||
|
||||
For more details: [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
|
||||
|
||||
---
|
||||
|
||||
## Error: Invalid Format
|
||||
|
||||
### ما ستراه
|
||||
|
||||
The cell value is highlighted in yellow. Hover over it to see the error message:
|
||||
|
||||
```
|
||||
{field name} is not a valid {field type}
|
||||
```
|
||||
|
||||
### What It Means
|
||||
|
||||
The data doesn't match the expected format for that field type.
|
||||
|
||||
### How to Fix — By Field Type
|
||||
|
||||
#### البريد الإلكتروني
|
||||
|
||||
**Problem:** Invalid email format
|
||||
**Solution:** Use format `name@domain.com`
|
||||
|
||||
```
|
||||
❌ john.smith@
|
||||
❌ john smith@acme.com
|
||||
✓ john.smith@acme.com
|
||||
```
|
||||
|
||||
#### النطاق
|
||||
|
||||
**Problem:** Inconsistent format may cause duplicates
|
||||
**Solution:** Use `https://domain.com` format (recommended)
|
||||
|
||||
```
|
||||
⚠️ acme.com (valid, but not recommended)
|
||||
⚠️ www.acme.com (valid, but not recommended)
|
||||
✅ https://acme.com (recommended)
|
||||
```
|
||||
|
||||
<Note>All formats are valid, but `https://domain.com` is recommended because it matches the format used by email/calendar sync. Using other formats may create duplicate companies.</Note>
|
||||
|
||||
#### تاريخ
|
||||
|
||||
**Problem:** Unrecognized date format
|
||||
**Solution:** Use consistent format throughout file
|
||||
|
||||
```
|
||||
✓ 2024-03-15 (YYYY-MM-DD - recommended)
|
||||
✓ 03/15/2024 (MM/DD/YYYY)
|
||||
✓ 15/03/2024 (DD/MM/YYYY)
|
||||
```
|
||||
|
||||
#### هاتف
|
||||
|
||||
**Problem:** Missing required columns
|
||||
**Solution:** Include all phone columns
|
||||
|
||||
| Column | مثال |
|
||||
| --------------------------------------- | ------------ |
|
||||
| **Phones / Primary Phone Number** | `4159095555` |
|
||||
| **Phones / Primary Phone Country Code** | `US` |
|
||||
| **Phones / Primary Phone Calling Code** | `+1` |
|
||||
|
||||
#### قيمة منطقية
|
||||
|
||||
**Problem:** Wrong boolean value
|
||||
**Solution:** Use uppercase `TRUE` or `FALSE`
|
||||
|
||||
```
|
||||
❌ true
|
||||
❌ yes
|
||||
❌ 1
|
||||
✓ TRUE
|
||||
✓ FALSE
|
||||
```
|
||||
|
||||
#### Select / Multi-Select
|
||||
|
||||
**Problem:** Value doesn't match existing options
|
||||
**Solution:** Use **API names**, not display labels
|
||||
|
||||
How to find API names:
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object and field
|
||||
3. Enable **Advanced mode** (toggle at bottom right)
|
||||
4. Use the API name (e.g., `OPTION_1`, not "Option 1")
|
||||
|
||||
```
|
||||
❌ High Priority
|
||||
✓ HIGH_PRIORITY
|
||||
```
|
||||
|
||||
#### العملة
|
||||
|
||||
**Problem:** Missing amount or currency code
|
||||
**Solution:** Fill both columns
|
||||
|
||||
| Column | مثال |
|
||||
| --------------------- | --------- |
|
||||
| **Amount / Amount** | `1234.56` |
|
||||
| **Amount / Currency** | `USD` |
|
||||
|
||||
#### رقم
|
||||
|
||||
**Problem:** Non-numeric characters
|
||||
**Solution:** Numbers only, period for decimals
|
||||
|
||||
```
|
||||
❌ $1,234.56
|
||||
❌ 1,234.56
|
||||
✓ 1234.56
|
||||
```
|
||||
|
||||
For complete format reference: [Field Mapping](/l/ar/user-guide/data-migration/capabilities/field-mapping)
|
||||
|
||||
---
|
||||
|
||||
## Error: Required Field Missing
|
||||
|
||||
### ما ستراه
|
||||
|
||||
The row is highlighted in yellow with the required field cell marked.
|
||||
|
||||
### What It Means
|
||||
|
||||
A required field is empty for this row.
|
||||
|
||||
### How to Fix
|
||||
|
||||
**Option 1: Enter a value**
|
||||
|
||||
1. Click the empty cell
|
||||
2. Enter a value
|
||||
3. Continue with import
|
||||
|
||||
**Option 2: Remove the row**
|
||||
|
||||
1. If you don't have the data, click X to skip the row
|
||||
|
||||
### How to Prevent This Error
|
||||
|
||||
Before importing, identify required fields:
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select your object
|
||||
3. Check which fields are marked as required
|
||||
|
||||
---
|
||||
|
||||
## Error: Relation Not Found
|
||||
|
||||
### ما ستراه
|
||||
|
||||
This error appears **after the import starts** — the import fails with a message like:
|
||||
|
||||
```
|
||||
Can't connect to company. No unique record found with condition: id = 7776ee49-f608-4a77-8cc8-6fe96ae1e43f
|
||||
```
|
||||
|
||||
This means there is no Company in Twenty with that specific identifier.
|
||||
|
||||
<Note>Unlike other errors, this one is not caught during the data review step. The import will start and then fail when it encounters the missing relation.</Note>
|
||||
|
||||
### What It Means
|
||||
|
||||
You're trying to link to a record that doesn't exist in Twenty.
|
||||
|
||||
### How to Fix
|
||||
|
||||
**Option 1: Import parent records first**
|
||||
|
||||
1. Cancel the current import
|
||||
2. Import the parent records (e.g., Companies)
|
||||
3. Then import the child records (e.g., People)
|
||||
|
||||
**Option 2: Fix the reference value**
|
||||
|
||||
1. Check the reference value in your file
|
||||
2. Ensure it exactly matches an existing record
|
||||
3. Verify format: domains should be `https://domain.com`
|
||||
|
||||
**Option 3: Remove the relation**
|
||||
|
||||
1. Clear the cell to import without the relation
|
||||
2. Add the relation manually later
|
||||
|
||||
### How to Prevent This Error
|
||||
|
||||
1. **Import in the correct order:**
|
||||
* Companies first
|
||||
* People second (with company references)
|
||||
* Opportunities third
|
||||
|
||||
2. **Verify reference values:**
|
||||
* Export parent records to get exact identifiers
|
||||
* Use domain format `https://domain.com`
|
||||
* Check for typos and case sensitivity
|
||||
|
||||
<Warning>
|
||||
**Import will fail if a reference is made to a non-existent record.**
|
||||
|
||||
Always import parent objects before child objects.
|
||||
</Warning>
|
||||
|
||||
For more details: [Import Relations](/l/ar/user-guide/data-migration/capabilities/import-relations)
|
||||
|
||||
---
|
||||
|
||||
## Error: File Too Large
|
||||
|
||||
### ما ستراه
|
||||
|
||||
This error appears **when uploading your file** — the upload is blocked entirely:
|
||||
|
||||
```
|
||||
Too many records. Up to 10000 allowed
|
||||
```
|
||||
|
||||
You won't be able to proceed to the data review step until you reduce the file size.
|
||||
|
||||
### What It Means
|
||||
|
||||
Your file has more than 10,000 records.
|
||||
|
||||
### How to Fix
|
||||
|
||||
**Option 1: Split into multiple files**
|
||||
|
||||
1. Divide your data into files of 10,000 records or fewer
|
||||
2. Import each file separately
|
||||
3. Maintain import order (Companies before People)
|
||||
|
||||
**Option 2: Use API import**
|
||||
For very large datasets, use the API which has no record limit.
|
||||
See: [How to Import Data via API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api)
|
||||
|
||||
---
|
||||
|
||||
## Error: Field Not Recognized
|
||||
|
||||
### What It Means
|
||||
|
||||
A column in your file can't be mapped because the field doesn't exist in Twenty.
|
||||
|
||||
### How to Fix
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object you're importing
|
||||
3. Click **+ Add field**
|
||||
4. Create the custom field with the appropriate type
|
||||
5. Re-upload your file
|
||||
|
||||
<Note>The CSV import creates records, not fields. All fields must exist before importing.</Note>
|
||||
|
||||
---
|
||||
|
||||
## Error: User Relation Empty
|
||||
|
||||
### What It Means
|
||||
|
||||
You're trying to assign a record to a user (Owner, Assignee) but the relation isn't being mapped.
|
||||
|
||||
### Common Causes
|
||||
|
||||
1. **User hasn't accepted their invitation** — the user doesn't exist in Twenty yet
|
||||
2. **Using user ID from old system** — Twenty can't match IDs from another system
|
||||
3. **Wrong email format** — the email doesn't match the user's Twenty account
|
||||
|
||||
### How to Fix
|
||||
|
||||
1. Ensure all users have **accepted their invitation** to your Twenty workspace
|
||||
2. Use the user's **email address** (not their name or old system ID)
|
||||
3. Use the same email they used to join Twenty
|
||||
|
||||
<Warning>
|
||||
**Users must accept invitations before importing.**
|
||||
|
||||
If a user hasn't accepted their invitation, records referencing them will have empty user relations.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Pre-Import Checklist
|
||||
|
||||
Avoid errors by checking these before importing:
|
||||
|
||||
### File Requirements
|
||||
|
||||
<Check>File is CSV, XLSX, or XLS format</Check>
|
||||
<Check>File has fewer than 10,000 records</Check>
|
||||
<Check>File uses UTF-8 encoding</Check>
|
||||
|
||||
### Data Quality
|
||||
|
||||
<Check>No duplicate emails (for People)</Check>
|
||||
<Check>No duplicate domains (for Companies)</Check>
|
||||
<Check>All dates use consistent format</Check>
|
||||
<Check>All domains use `https://domain.com` format</Check>
|
||||
|
||||
### Field Formats
|
||||
|
||||
<Check>Boolean fields use `TRUE` or `FALSE` (uppercase)</Check>
|
||||
<Check>Select fields use API names, not display labels</Check>
|
||||
<Check>Phone fields have all required columns</Check>
|
||||
<Check>Currency fields have both Amount and Currency Code</Check>
|
||||
|
||||
### العلاقات
|
||||
|
||||
<Check>Parent records imported before child records</Check>
|
||||
<Check>Relation columns reference existing records</Check>
|
||||
<Check>Domain format matches Twenty's format exactly</Check>
|
||||
|
||||
### نموذج البيانات
|
||||
|
||||
<Check>All custom fields exist in Settings → Data Model</Check>
|
||||
<Check>Select options exist before importing</Check>
|
||||
|
||||
---
|
||||
|
||||
## Still Having Issues?
|
||||
|
||||
If you've tried the above solutions:
|
||||
|
||||
1. **Download the sample file** — see the exact format Twenty expects
|
||||
2. **Export existing records** — compare your file to working data
|
||||
3. **Test with a small batch** — try 5-10 rows first
|
||||
4. **Check the reference articles:**
|
||||
* [Field Mapping](/l/ar/user-guide/data-migration/capabilities/field-mapping)
|
||||
* [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
|
||||
* [Import Relations](/l/ar/user-guide/data-migration/capabilities/import-relations)
|
||||
* [Error Handling](/l/ar/user-guide/data-migration/capabilities/error-handling)
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: Import Companies via CSV
|
||||
description: Complete step-by-step guide to importing companies into Twenty.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
This guide walks you through importing your companies into Twenty. **Companies should be imported first** because People and Opportunities link to Companies.
|
||||
|
||||
## قبل أن تبدأ
|
||||
|
||||
### Prerequisites Checklist
|
||||
|
||||
<Check>
|
||||
Your file is CSV, XLSX, or XLS format
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
File has fewer than 10,000 records
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
No duplicate domains in your file
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
All custom fields exist in **Settings → Data Model**
|
||||
</Check>
|
||||
|
||||
<Note>
|
||||
Need to import more than 10,000 companies? Split into multiple files or use the [API import](/l/ar/user-guide/data-migration/how-tos/import-data-via-api).
|
||||
</Note>
|
||||
|
||||
## Step 1: Prepare Your Company Data
|
||||
|
||||
### Required and Recommended Fields
|
||||
|
||||
| الحقل | Required? | التنسيق | الملاحظات |
|
||||
| ----------------- | ----------- | -------------------- | ------------------------ |
|
||||
| **Name** | Recommended | نص | Company display name |
|
||||
| **Domain** | Recommended | `https://domain.com` | Unique identifier |
|
||||
| **Address** | Optional | Multiple columns | See below |
|
||||
| **Employees** | Optional | رقم | Employee count |
|
||||
| **Custom fields** | Optional | Varies | Must exist in Data Model |
|
||||
|
||||
### Domain Format
|
||||
|
||||
<Warning>
|
||||
**Use the format `https://domain.com` for domains.**
|
||||
|
||||
This matches the format used when Companies are auto-created from email/calendar sync, preventing duplicates later.
|
||||
</Warning>
|
||||
|
||||
**Domain columns:**
|
||||
|
||||
* **Domain / Domain Label**: `acme.com`
|
||||
* **Domain / Domain URL**: `https://acme.com`
|
||||
|
||||
### Address Format
|
||||
|
||||
Address is a nested field with multiple columns:
|
||||
|
||||
```
|
||||
Address / Address 1,Address / City,Address / State,Address / Country,Address / Post Code
|
||||
123 Main Street,San Francisco,CA,USA,94105
|
||||
```
|
||||
|
||||
### Sample CSV Structure
|
||||
|
||||
```csv
|
||||
name,Domain / Domain URL,Domain / Domain Label,Address / City,Address / Country,employees
|
||||
Acme Corp,https://acme.com,acme.com,San Francisco,USA,250
|
||||
Widget Co,https://widgets.co,widgets.co,New York,USA,50
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Pro tip:** Click **Download sample file** during import to see the exact column names Twenty expects.
|
||||
</Note>
|
||||
|
||||
## Step 2: Access the Import Feature
|
||||
|
||||
**Option 1: From the Companies View**
|
||||
|
||||
1. Navigate to **Companies** in the left sidebar
|
||||
2. Click the **⋮** icon on the top right
|
||||
3. Select **Import records**
|
||||
|
||||
**Option 2: Using Command Menu**
|
||||
|
||||
1. Press `Cmd + K` (Mac) or `Ctrl + K` (Windows)
|
||||
2. Type "import"
|
||||
3. Select **Import records**
|
||||
4. Choose **Companies**
|
||||
|
||||
## Step 3: Upload Your File
|
||||
|
||||
1. Click **Select file**
|
||||
2. Choose your CSV, XLSX, or XLS file
|
||||
3. Wait for Twenty to analyze your file
|
||||
|
||||
## Step 4: Map Your Columns
|
||||
|
||||
Twenty automatically tries to match your columns to fields. Review and adjust:
|
||||
|
||||
1. **Check automatic mappings** — verify they're correct
|
||||
2. **Fix incorrect mappings** — click the dropdown to select the right field
|
||||
3. **Skip columns** — select **Do not map** for columns you don't want to import
|
||||
|
||||
### Important Mapping Rules
|
||||
|
||||
* **Domain**: Map to **Domain / Domain URL** (not Domain Label)
|
||||
* **Address**: Map each part to its specific column (City, State, etc.)
|
||||
* **Select fields**: Values must match existing options (or you'll map them in the next step)
|
||||
|
||||
<img src="/images/user-guide/import-export-data/match-columns.png" style={{width:'100%'}} />
|
||||
|
||||
## Step 5: Map Select Field Values
|
||||
|
||||
If you have Select or Multi-Select fields:
|
||||
|
||||
1. Twenty shows your values alongside existing options
|
||||
2. Match each value in your file to a Twenty option
|
||||
3. Or create new options if needed
|
||||
|
||||
<Note>
|
||||
Select options use **API names**, not display labels. Check **Settings → Data Model** → Enable **Advanced mode** to see API names.
|
||||
</Note>
|
||||
|
||||
## Step 6: Review and Fix Errors
|
||||
|
||||
Before completing the import, Twenty validates your data:
|
||||
|
||||
1. Click **Next Steps**
|
||||
2. Rows with errors are highlighted in **yellow**
|
||||
3. **Fix errors directly** — click a cell and edit the value
|
||||
4. **Remove problematic rows** — click the X to skip that row
|
||||
|
||||
### Common Company Import Errors
|
||||
|
||||
| خطأ | Cause | Solution |
|
||||
| -------------------------- | ------------------------------- | ------------------------------------------ |
|
||||
| **Duplicate domain** | Domain already exists in Twenty | Remove from file or update existing record |
|
||||
| **Invalid domain format** | Wrong format | Use `https://domain.com` |
|
||||
| **Missing required field** | Required field is empty | Fill in the value or remove the row |
|
||||
|
||||
## Step 7: Complete the Import
|
||||
|
||||
1. Review the import summary
|
||||
2. Click **Confirm** to import
|
||||
3. Wait for the import to complete
|
||||
4. Verify by checking a few records
|
||||
|
||||
## After Importing Companies
|
||||
|
||||
Now you can import records that link to Companies:
|
||||
|
||||
1. **[Import People](/l/ar/user-guide/data-migration/how-tos/import-contacts-via-csv)** — link them to Companies using the domain
|
||||
2. **Import Opportunities** — link them to Companies
|
||||
3. **Verify the import** — spot-check a few records to ensure data is correct
|
||||
|
||||
## Updating Existing Companies
|
||||
|
||||
To update companies instead of creating new ones:
|
||||
|
||||
1. Include the `domain` or `id` column in your file
|
||||
2. Twenty matches records by this unique identifier
|
||||
3. Existing companies are updated; new ones are created
|
||||
|
||||
See [How to Update Existing Records](/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import) for details.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why can't two Companies have the same domain?">
|
||||
Domain is a unique identifier in Twenty. This prevents duplicate companies and ensures email sync correctly links emails to the right company.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if I have companies without a domain?">
|
||||
You can leave the domain empty. However, we recommend adding domains when possible for better data quality and automatic email linking.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I import companies without any People linked?">
|
||||
نعم! You can import companies first, then import People later and link them using the company domain.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if I import a domain that already exists?">
|
||||
If you include a unique identifier (domain or id) that matches an existing company, Twenty updates that company instead of creating a duplicate.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I fix 'duplicate domain' errors?">
|
||||
Either remove the duplicate from your file, or include the company's `id` to update the existing record instead.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
Having issues? Check:
|
||||
|
||||
* [How to Fix Import Errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
|
||||
* [Field Mapping Reference](/l/ar/user-guide/data-migration/capabilities/field-mapping)
|
||||
* [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
---
|
||||
title: Import Contacts via CSV
|
||||
description: Complete step-by-step guide to importing people/contacts into Twenty.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
This guide walks you through importing your contacts (People) into Twenty. **Import Companies first** if you want to link People to Companies.
|
||||
|
||||
## قبل أن تبدأ
|
||||
|
||||
### Prerequisites Checklist
|
||||
|
||||
<Check>
|
||||
Your file is CSV, XLSX, or XLS format
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
File has fewer than 10,000 records
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
No duplicate email addresses in your file
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
**Companies imported first** (if linking People to Companies)
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
All custom fields exist in **Settings → Data Model**
|
||||
</Check>
|
||||
|
||||
<Warning>
|
||||
**Import Companies Before People**
|
||||
|
||||
If you want to link People to Companies, import Companies first. The Company must exist before you can reference it.
|
||||
</Warning>
|
||||
|
||||
## Step 1: Prepare Your Contact Data
|
||||
|
||||
### Required and Recommended Fields
|
||||
|
||||
| الحقل | Required? | التنسيق | الملاحظات |
|
||||
| --------------------- | ----------- | ----------------- | ------------------------- |
|
||||
| **البريد الإلكتروني** | Recommended | `name@domain.com` | Must be unique |
|
||||
| **First Name** | Recommended | نص | |
|
||||
| **Last Name** | Recommended | نص | |
|
||||
| **Company** | Optional | Domain or ID | Links to existing Company |
|
||||
| **Phone** | Optional | Multiple columns | See below |
|
||||
| **Job Title** | Optional | نص | |
|
||||
| **Custom fields** | Optional | Varies | Must exist in Data Model |
|
||||
|
||||
### Email Format
|
||||
|
||||
* Must be valid email format: `name@domain.com`
|
||||
* **Must be unique** — no duplicates in your file or in Twenty
|
||||
* For additional emails, use the **Emails / Additional Emails** column:
|
||||
|
||||
```
|
||||
["jane@twenty.com","jane.doe@twenty.com"]
|
||||
```
|
||||
|
||||
### Phone Format
|
||||
|
||||
Phone is a **nested field** requiring multiple columns:
|
||||
|
||||
| Column | مثال |
|
||||
| --------------------------------------- | ------------ |
|
||||
| **Phones / Primary Phone Number** | `4159095555` |
|
||||
| **Phones / Primary Phone Country Code** | `US` |
|
||||
| **Phones / Primary Phone Calling Code** | `+1` |
|
||||
|
||||
### Linking to Companies
|
||||
|
||||
Add a column with the Company's unique identifier:
|
||||
|
||||
| Column Name | التنسيق | مثال |
|
||||
| --------------- | ---------- | -------------------------------------- |
|
||||
| `companyDomain` | URL format | `https://acme.com` |
|
||||
| `companyId` | UUID | `c776ee49-f608-4a77-8cc8-6fe96ae1e43f` |
|
||||
|
||||
<Note>
|
||||
**Use Domain URL format** (`https://acme.com`), not the label. This matches how Companies are stored in Twenty.
|
||||
</Note>
|
||||
|
||||
### Sample CSV Structure
|
||||
|
||||
```csv
|
||||
firstName,lastName,email,jobTitle,companyDomain,Phones / Primary Phone Number,Phones / Primary Phone Country Code
|
||||
John,Smith,john@acme.com,CEO,https://acme.com,4159095555,US
|
||||
Jane,Doe,jane@widgets.co,CTO,https://widgets.co,2125551234,US
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Pro tip:** Click **Download sample file** during import or export a few existing People to see the exact column names Twenty expects.
|
||||
</Note>
|
||||
|
||||
## Step 2: Access the Import Feature
|
||||
|
||||
**Option 1: From the People View**
|
||||
|
||||
1. Navigate to **People** in the left sidebar
|
||||
2. Click the **⋮** icon on the top right
|
||||
3. Select **Import records**
|
||||
|
||||
**Option 2: Using Command Menu**
|
||||
|
||||
1. Press `Cmd + K` (Mac) or `Ctrl + K` (Windows)
|
||||
2. Type "import"
|
||||
3. Select **Import records**
|
||||
4. Choose **People**
|
||||
|
||||
## Step 3: Upload Your File
|
||||
|
||||
1. Click **Select file**
|
||||
2. Choose your CSV, XLSX, or XLS file
|
||||
3. Wait for Twenty to analyze your file
|
||||
|
||||
## Step 4: Map Your Columns
|
||||
|
||||
Twenty automatically tries to match your columns to fields. Review and adjust:
|
||||
|
||||
1. **Check automatic mappings** — verify they're correct
|
||||
2. **Fix incorrect mappings** — click the dropdown to select the right field
|
||||
3. **Skip columns** — select **Do not map** for columns you don't want to import
|
||||
|
||||
### Important Mapping Rules
|
||||
|
||||
| Column Type | Map To | الملاحظات |
|
||||
| ----------------- | ------------------------------ | ---------------------------------- |
|
||||
| Company reference | **Company** relation field | Use domain OR id, not both |
|
||||
| البريد الإلكتروني | **البريد الإلكتروني** | Primary email address |
|
||||
| Additional emails | **Emails / Additional Emails** | Array format |
|
||||
| هاتف | Separate columns | Number, Country Code, Calling Code |
|
||||
|
||||
<img src="/images/user-guide/import-export-data/match-columns.png" style={{width:'100%'}} />
|
||||
|
||||
### Mapping the Company Relation
|
||||
|
||||
When mapping the company column:
|
||||
|
||||
1. Find your company reference column (e.g., `companyDomain`)
|
||||
2. Map it to the **Company** relation field
|
||||
3. Twenty will link each Person to the matching Company
|
||||
|
||||
<Warning>
|
||||
**Map only ONE unique identifier for relations.**
|
||||
|
||||
Don't map both `companyId` AND `companyDomain`. Choose one—preferably domain since it's human-readable.
|
||||
</Warning>
|
||||
|
||||
## Step 5: Map Select Field Values
|
||||
|
||||
If you have Select or Multi-Select fields (like Lead Source):
|
||||
|
||||
1. Twenty shows your values alongside existing options
|
||||
2. Match each value in your file to a Twenty option
|
||||
3. Or create new options if needed
|
||||
|
||||
<Note>
|
||||
Select options use **API names**, not display labels. Check **Settings → Data Model** → Enable **Advanced mode** to see API names.
|
||||
</Note>
|
||||
|
||||
## Step 6: Review and Fix Errors
|
||||
|
||||
Before completing the import, Twenty validates your data:
|
||||
|
||||
1. Click **Next Steps**
|
||||
2. Rows with errors are highlighted in **yellow**
|
||||
3. **Fix errors directly** — click a cell and edit the value
|
||||
4. **Remove problematic rows** — click the X to skip that row
|
||||
|
||||
### Common Contact Import Errors
|
||||
|
||||
| خطأ | Cause | Solution |
|
||||
| -------------------------- | -------------------------------------- | ------------------------------------------- |
|
||||
| **Duplicate email** | Email already exists in Twenty or file | Remove duplicate or update existing record |
|
||||
| **Invalid email format** | Email format incorrect | Fix to `name@domain.com` |
|
||||
| **Relation not found** | Company doesn't exist | Import Companies first or fix the reference |
|
||||
| **Missing required field** | Required field is empty | Fill in the value or remove the row |
|
||||
|
||||
## Step 7: Complete the Import
|
||||
|
||||
1. Review the import summary
|
||||
2. Click **Confirm** to import
|
||||
3. Wait for the import to complete
|
||||
4. Verify by checking a few records and their Company links
|
||||
|
||||
## After Importing Contacts
|
||||
|
||||
Your contacts are now in Twenty! Next steps:
|
||||
|
||||
1. **Verify Company links** — open a few People records to confirm they're linked to the right Company
|
||||
2. **Import Opportunities** — if needed, link them to People and Companies
|
||||
3. **Set up email sync** — connect your mailbox to see email history on contact records
|
||||
|
||||
## Updating Existing Contacts
|
||||
|
||||
To update contacts instead of creating new ones:
|
||||
|
||||
1. Include the `email` or `id` column in your file
|
||||
2. Twenty matches records by this unique identifier
|
||||
3. Existing contacts are updated; new ones are created
|
||||
|
||||
See [How to Update Existing Records](/l/ar/user-guide/data-migration/how-tos/update-existing-records-via-import) for details.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why can't two People have the same email?">
|
||||
Email is a unique identifier in Twenty. This prevents duplicate contacts and ensures email sync correctly links emails to the right person.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if I have people without an email?">
|
||||
You can leave the email empty. However, we recommend adding emails when possible for better data quality and email sync functionality.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I link People to Companies during import?">
|
||||
Add a column with the Company's domain (e.g., `https://acme.com`) or ID. During mapping, connect this column to the Company relation field.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if the Company doesn't exist yet?">
|
||||
Import Companies first, then import People. The Company must exist before you can reference it.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I import the ID from my previous CRM?">
|
||||
نعم! Create a custom field marked as "unique" in your data model to store the external ID. Note: the field name `id` is reserved for Twenty's internal ID.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I fix 'relation not found' errors?">
|
||||
The Company you're referencing doesn't exist. Either import the Company first, or check that the domain/ID exactly matches an existing Company.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
Having issues? Check:
|
||||
|
||||
* [How to Fix Import Errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
|
||||
* [How to Import Relations](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv)
|
||||
* [Field Mapping Reference](/l/ar/user-guide/data-migration/capabilities/field-mapping)
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
title: Import Data via API
|
||||
description: When and how to use Twenty's APIs for large-scale data imports.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty provides both **GraphQL** and **REST APIs** for programmatic data import. Use the API when CSV import isn't practical for your data volume or when you need automated, recurring imports.
|
||||
|
||||
## When to Use API Import
|
||||
|
||||
| Scenario | Recommended Method |
|
||||
| ---------------------------------- | ----------------------------- |
|
||||
| Under 10,000 records | CSV Import |
|
||||
| 10,000 - 50,000 records | CSV Import (split into files) |
|
||||
| **50,000+ records** | **API Import** |
|
||||
| One-time migration | Either (based on volume) |
|
||||
| **Recurring imports** | **API Import** |
|
||||
| **Real-time sync** | **API Import** |
|
||||
| **Integration with other systems** | **API Import** |
|
||||
|
||||
<Note>For datasets in the hundreds of thousands, the API is significantly faster and more reliable than multiple CSV imports.</Note>
|
||||
|
||||
## API Rate Limits
|
||||
|
||||
Twenty enforces rate limits to ensure system stability:
|
||||
|
||||
| Limit | القيمة |
|
||||
| -------------------------- | --------------------- |
|
||||
| **Requests per minute** | 100 |
|
||||
| **Records per batch call** | 60 |
|
||||
| **Maximum throughput** | ~6,000 records/minute |
|
||||
|
||||
<Warning>
|
||||
**Plan your import around these limits.**
|
||||
|
||||
For 100,000 records at maximum throughput, expect approximately 17 minutes of import time. Add buffer time for error handling and retries.
|
||||
</Warning>
|
||||
|
||||
## البدء
|
||||
|
||||
### Step 1: Get Your API Key
|
||||
|
||||
1. Go to **Settings → Developers**
|
||||
2. Click **+ Create API key**
|
||||
3. Give your key a descriptive name
|
||||
4. Copy the API key immediately (it won't be shown again)
|
||||
5. Store it securely
|
||||
|
||||
<Warning>
|
||||
**Keep your API key secret.**
|
||||
|
||||
Anyone with your API key can access and modify your workspace data. Never commit it to code repositories or share it publicly.
|
||||
</Warning>
|
||||
|
||||
### Step 2: Choose Your API
|
||||
|
||||
Twenty supports two API types:
|
||||
|
||||
| واجهة برمجة التطبيقات | Best For | التوثيق |
|
||||
| --------------------- | ----------------------------------------------------------- | ------------------------------------------------ |
|
||||
| **GraphQL** | Flexible queries, fetching related data, complex operations | [API Docs](/l/ar/developers/extend/capabilities/apis) |
|
||||
| **REST** | Simple CRUD operations, familiar REST patterns | [API Docs](/l/ar/developers/extend/capabilities/apis) |
|
||||
|
||||
Both APIs support:
|
||||
|
||||
* Creating, reading, updating, and deleting records
|
||||
* **Batch operations** — create or update up to 60 records per call
|
||||
|
||||
**For imports, use batch operations** to maximize throughput within rate limits.
|
||||
|
||||
### Step 3: Plan Your Import Order
|
||||
|
||||
Just like CSV imports, **order matters** for relations:
|
||||
|
||||
1. **Companies** first (no dependencies)
|
||||
2. **People** second (can link to Companies)
|
||||
3. **Opportunities** third (can link to Companies and People)
|
||||
4. **Tasks/Notes** (can link to any of the above)
|
||||
5. **Custom objects** (following their dependencies)
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
### Batch Your Requests
|
||||
|
||||
* Don't send records one at a time
|
||||
* Group up to **60 records per API call**
|
||||
* This maximizes throughput within rate limits
|
||||
|
||||
### Handle Rate Limits
|
||||
|
||||
* Implement delays between requests (600ms minimum for sustained imports)
|
||||
* Use exponential backoff when you hit limits
|
||||
* Monitor for 429 (Too Many Requests) responses
|
||||
|
||||
### Validate Data First
|
||||
|
||||
* Clean and validate your data before importing
|
||||
* Check required fields are populated
|
||||
* Verify formats match Twenty's requirements (see [Field Mapping](/l/ar/user-guide/data-migration/capabilities/field-mapping))
|
||||
|
||||
### Log Everything
|
||||
|
||||
* Log every record imported (including IDs)
|
||||
* Log errors with full context
|
||||
* This helps debug issues and verify completion
|
||||
|
||||
### Test First
|
||||
|
||||
* Test with a small batch (10-20 records)
|
||||
* Verify data appears correctly in Twenty
|
||||
* Then run the full import
|
||||
|
||||
### Upsert to Avoid Duplicates
|
||||
|
||||
The GraphQL API supports **batch upsert** — update if the record exists, create if not. This prevents duplicates when re-running imports.
|
||||
|
||||
## Finding Object and Field Names
|
||||
|
||||
To see available objects and fields:
|
||||
|
||||
1. Go to **Settings → API and Webhooks**
|
||||
2. Browse the **Metadata API**
|
||||
3. View all standard and custom objects with their fields
|
||||
|
||||
The documentation shows all standard and custom objects, their fields, and the expected data types.
|
||||
|
||||
## Professional Services
|
||||
|
||||
For complex API migrations, our partners can help:
|
||||
|
||||
| Service | What's Included |
|
||||
| ----------------------- | ---------------------------------- |
|
||||
| **Data Model Design** | design your optimal data structure |
|
||||
| **Migration Scripts** | write and run the import scripts |
|
||||
| **Data Transformation** | handle complex mapping and cleanup |
|
||||
| **Validation & QA** | verify the migration is complete |
|
||||
|
||||
**Best for:**
|
||||
|
||||
* Migrations of 100,000+ records
|
||||
* Complex data transformations
|
||||
* Tight timelines
|
||||
* Teams without developer resources
|
||||
|
||||
Contact us at [contact@twenty.com](mailto:contact@twenty.com) or explore our [Implementation Services](/l/ar/user-guide/getting-started/capabilities/implementation-services).
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What's the difference between GraphQL and REST?">
|
||||
GraphQL lets you request exactly the data you need in a single query and is better for complex operations. REST uses standard HTTP methods (GET, POST, PUT, DELETE) and may be more familiar if you've worked with traditional APIs.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I update existing records via API?">
|
||||
نعم! Use update mutations (GraphQL) or PUT/PATCH requests (REST) with the record's `id`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I handle duplicates?">
|
||||
Query for existing records first using unique identifiers (email, domain). Update if exists, create if not.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I delete records via API?">
|
||||
Yes, use delete mutations (GraphQL) or DELETE requests (REST).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is there a Python or Node.js SDK?">
|
||||
Not currently, but both APIs work with any HTTP client in any language.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## API Documentation
|
||||
|
||||
For full implementation details, code examples, and schema reference:
|
||||
|
||||
* [API Documentation](/l/ar/developers/extend/capabilities/apis)
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
---
|
||||
title: Import Relations Between Objects via CSV
|
||||
description: Complete step-by-step guide to linking records during CSV import.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
This guide walks you through importing relations between objects—for example, linking People to Companies, or Opportunities to People.
|
||||
|
||||
<Note>**What can be imported:** Only one-to-many relations pointing to a single object type. Relations pointing to multiple object types (like Notes linking to People AND Companies) are not yet supported for import.</Note>
|
||||
|
||||
## Understanding Relations
|
||||
|
||||
### What is a "One-to-Many" Relation?
|
||||
|
||||
In a one-to-many relation:
|
||||
|
||||
* **One** Company has **many** People (employees)
|
||||
* **One** Company has **many** Opportunities
|
||||
* **One** Person has **many** Tasks
|
||||
|
||||
The "one" side is the **parent**. The "many" side is the **child**.
|
||||
|
||||
### Common Relations in Twenty
|
||||
|
||||
| علاقة | "One" Side (Parent) | "Many" Side (Child) |
|
||||
| ------------------------- | ------------------- | ------------------- |
|
||||
| Companies → People | الشركة | الأشخاص |
|
||||
| Companies → Opportunities | الشركة | الفرص |
|
||||
| People → Tasks | شخص | المهام |
|
||||
| People → Notes | شخص | الملاحظات |
|
||||
|
||||
## Step 1: Identify the "One" and "Many" Sides
|
||||
|
||||
Before importing, determine which object is the parent and which is the child.
|
||||
|
||||
**Ask yourself:** "Does ONE [Object A] have MANY [Object B]?"
|
||||
|
||||
* One Company → Many People ✓ (Company is parent)
|
||||
* One Person → Many Companies ✗ (This is wrong—a person belongs to one company)
|
||||
|
||||
## Step 2: Import the Parent Records First
|
||||
|
||||
The parent ("one" side) must exist in Twenty before you can reference it.
|
||||
|
||||
**Import order:**
|
||||
|
||||
1. **Companies** first (no dependencies)
|
||||
2. **People** second (link to Companies)
|
||||
3. **Opportunities** third (link to Companies and/or People)
|
||||
4. **Tasks/Notes** (link to any of the above)
|
||||
|
||||
<Warning>
|
||||
**If the parent record doesn't exist, the import will fail.**
|
||||
|
||||
Always verify that Companies are imported before importing People with company references.
|
||||
</Warning>
|
||||
|
||||
## Step 3: Note the Parent's Unique Identifier
|
||||
|
||||
You need to reference the parent record using a **unique identifier**. Available options:
|
||||
|
||||
| Parent Object | Available Unique Identifiers |
|
||||
| --------------------- | --------------------------------------------------------------- |
|
||||
| **الشركات** | `id` (UUID), `domain` (recommended), or any custom unique field |
|
||||
| **People** | `id` (UUID), `email`, or any custom unique field |
|
||||
| **أعضاء مساحة العمل** | `id` (UUID), `email` (not name) |
|
||||
| **كائنات مخصصة** | `id` (UUID), or any field marked as unique |
|
||||
|
||||
<Note>**Recommended:** Use `domain` for Companies and `email` for People. These are human-readable and easy to verify in your spreadsheet.</Note>
|
||||
|
||||
### Finding the Identifier
|
||||
|
||||
If you need the `id`:
|
||||
|
||||
1. Export the parent records from Twenty
|
||||
2. The export includes the `id` column
|
||||
3. Use these IDs in your child records file
|
||||
|
||||
## Step 4: Verify the Relation Field Exists
|
||||
|
||||
Before importing, ensure the relation field exists between your objects.
|
||||
|
||||
**To check or create:**
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select your child object (e.g., People)
|
||||
3. Look for a relation field pointing to the parent (e.g., Company)
|
||||
4. If it doesn't exist, create it:
|
||||
* Click **+ Add field**
|
||||
* Select **Relation** type
|
||||
* Choose the parent object
|
||||
|
||||
## Step 5: Prepare Your CSV File
|
||||
|
||||
Add a column to your child CSV that references the parent using its unique identifier.
|
||||
|
||||
### Example: People Linking to Companies
|
||||
|
||||
**Your People CSV:**
|
||||
|
||||
```csv
|
||||
firstName,lastName,email,jobTitle,companyDomain
|
||||
John,Smith,john@acme.com,CEO,https://acme.com
|
||||
Jane,Doe,jane@widgets.co,CTO,https://widgets.co
|
||||
Bob,Johnson,bob@techstart.io,Developer,https://techstart.io
|
||||
```
|
||||
|
||||
The `companyDomain` column references the Company's domain.
|
||||
|
||||
### Format Requirements
|
||||
|
||||
| معرّف | التنسيق | مثال |
|
||||
| ----------------- | -------------- | -------------------------------------- |
|
||||
| النطاق | URL format | `https://acme.com` |
|
||||
| البريد الإلكتروني | Standard email | `john@acme.com` |
|
||||
| المعرف | UUID | `c776ee49-f608-4a77-8cc8-6fe96ae1e43f` |
|
||||
|
||||
<Warning>
|
||||
**Domain format matters!**
|
||||
|
||||
Use `https://domain.com` (not just `domain.com`). This matches how Twenty stores Company domains and prevents matching errors.
|
||||
</Warning>
|
||||
|
||||
### Important Rules
|
||||
|
||||
1. **Exact match required** — the value must exactly match the parent record
|
||||
2. **Map only ONE unique identifier** — don't include both `companyId` AND `companyDomain`
|
||||
3. **Case sensitive** — `Acme.com` ≠ `acme.com`
|
||||
|
||||
## Step 6: Upload and Map the Relation
|
||||
|
||||
1. Navigate to the child object (e.g., People)
|
||||
2. Click **⋮** → **Import records**
|
||||
3. Upload your CSV file
|
||||
4. In the field mapping step:
|
||||
* Find your relation column (e.g., `companyDomain`)
|
||||
* Map it to the **Company** relation field
|
||||
5. Complete the remaining mapping
|
||||
6. Review errors and confirm
|
||||
|
||||
Twenty will automatically link each child record to the matching parent.
|
||||
|
||||
## Step 7: Verify the Import
|
||||
|
||||
After importing:
|
||||
|
||||
1. Open a few child records (e.g., People)
|
||||
2. Verify the relation field shows the correct parent (e.g., Company)
|
||||
3. Open a parent record and check the related records section
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
| Mistake | Problem | Solution |
|
||||
| -------------------------- | -------------------------------------------------- | ------------------------------------------------------- |
|
||||
| **Wrong import order** | Importing People before Companies | Always import parents first, then children |
|
||||
| **Wrong domain format** | Using `acme.com` instead of `https://acme.com` | Use full URL format with `https://` |
|
||||
| **Multiple unique fields** | Mapping both `companyId` AND `companyDomain` | Map only ONE unique identifier |
|
||||
| **Missing relation field** | The relation field doesn't exist in the data model | Create it in **Settings → Data Model** before importing |
|
||||
| **Non-existent records** | The parent record doesn't exist in Twenty | Import parent records first, or check for typos |
|
||||
| **Case mismatch** | `Acme.com` in file but `acme.com` in Twenty | Ensure exact case matching |
|
||||
|
||||
## Linking to Workspace Members
|
||||
|
||||
When linking to Workspace Members (your team):
|
||||
|
||||
* Use their **email address**, not their name
|
||||
* Example: `owner@yourcompany.com`, not "John Smith"
|
||||
|
||||
```csv
|
||||
taskName,assignedTo
|
||||
Follow up with client,john@yourcompany.com
|
||||
Review proposal,jane@yourcompany.com
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What if my parent record doesn't have a domain or email?">
|
||||
You have two options:
|
||||
|
||||
1. Use the Twenty `id` (export parent records to get their IDs)
|
||||
2. Create a custom unique field in your data model to store an external ID from your previous system
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I update relations on existing records?">
|
||||
نعم! Include the child record's unique identifier (e.g., `email` for People) and the new relation value. The import will update the relation.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I import Many-to-Many relationships?">
|
||||
Many-to-Many relations are not yet supported for import. This is planned for H1 2026.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I link to multiple object types (like Notes to People AND Companies)?">
|
||||
Relations pointing to multiple object types are not yet supported for import/export. This is on our roadmap.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if a parent record doesn't exist?">
|
||||
The import will show an error for that row. يمكنك إما:
|
||||
|
||||
* Import the parent record first, then re-import
|
||||
* Fix the reference value
|
||||
* Remove the row from import
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why is my relation not linking correctly?">
|
||||
Common causes:
|
||||
|
||||
* Wrong format (use `https://domain.com` for domains)
|
||||
* Case mismatch (check exact spelling)
|
||||
* Parent doesn't exist (import parents first)
|
||||
* Mapping multiple identifiers (use only one)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Warning>
|
||||
**Remember: Soft-deleted records count toward uniqueness.**
|
||||
|
||||
If you're getting "not found" errors but the record seems to exist, check Command Menu → See deleted records. The parent may have been soft-deleted.
|
||||
</Warning>
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
Having issues? Check:
|
||||
|
||||
* [How to Fix Import Errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
|
||||
* [Import Relations Capabilities](/l/ar/user-guide/data-migration/capabilities/import-relations)
|
||||
* [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
---
|
||||
title: التحويل من أنظمة إدارة علاقات العملاء الأخرى},{
|
||||
description: Step-by-step guide to migrate your data from any CRM to Twenty.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
This guide walks you through migrating your data from any CRM to Twenty. The process involves auditing your data, preparing your Twenty workspace, exporting from your current system, and importing into Twenty.
|
||||
|
||||
<Note>Views, workflows, and permissions must be recreated manually after migration. Plan time for this configuration work.</Note>
|
||||
|
||||
## Step 1: Audit Your Current Data
|
||||
|
||||
Migration is an opportunity for a fresh start. Don't bring over clutter.
|
||||
|
||||
**What to keep:**
|
||||
|
||||
* Active contacts and companies
|
||||
* Open opportunities and deals
|
||||
* Important notes and activities
|
||||
* Custom fields you actually use
|
||||
|
||||
**What to leave behind:**
|
||||
|
||||
* Outdated contacts (no activity in 2+ years)
|
||||
* Duplicate records
|
||||
* Test data
|
||||
* Unused custom fields
|
||||
|
||||
## Step 2: Map Your Data Model
|
||||
|
||||
Create a mapping document between your current CRM and Twenty:
|
||||
|
||||
| Your CRM | Twenty |
|
||||
| ---------------------- | -------------------- |
|
||||
| Account / Organization | **Company** |
|
||||
| Contact / Person | **People** |
|
||||
| Deal / Opportunity | **Opportunity** |
|
||||
| Activity | **Task** or **Note** |
|
||||
| Custom Object | **Custom Object** |
|
||||
|
||||
**For each field, document:**
|
||||
|
||||
* The source field name
|
||||
* The target Twenty field
|
||||
* Any format transformations needed (dates, phone numbers, etc.)
|
||||
|
||||
<Note>Keep this mapping document handy during import—you'll reference it when mapping columns.</Note>
|
||||
|
||||
## Step 3: Set Up Your Twenty Workspace
|
||||
|
||||
Before importing data, prepare your Twenty workspace:
|
||||
|
||||
### Create Custom Objects and Fields
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Create any custom objects you need
|
||||
3. Add custom fields to standard and custom objects
|
||||
4. Configure field settings (unique, required, select options, etc.)
|
||||
|
||||
<Warning>
|
||||
**Fields must exist before import.**
|
||||
|
||||
The CSV import creates records, not fields. Create all custom fields in Settings → Data Model before importing.
|
||||
</Warning>
|
||||
|
||||
### Invite Your Team
|
||||
|
||||
<Warning>
|
||||
**Invite users BEFORE importing data.**
|
||||
|
||||
If your data includes user references (Account Owner, Assignee, etc.), those users must exist in Twenty before import. Otherwise, those relations cannot be mapped.
|
||||
</Warning>
|
||||
|
||||
1. انتقل إلى **الإعدادات → الأعضاء**
|
||||
2. Invite all team members
|
||||
3. **Wait for everyone to accept** their invitation
|
||||
4. Verify all users appear in your Members list
|
||||
|
||||
## Step 4: Export from Your Current CRM
|
||||
|
||||
Export your data from your current CRM:
|
||||
|
||||
1. Look for an **Export** function (usually under Settings, Data Management, or Admin)
|
||||
2. Export to **CSV format** when possible
|
||||
3. Export each object type separately (Companies, Contacts, Deals, etc.)
|
||||
4. Include all fields you want to migrate
|
||||
|
||||
**Export these objects (in this order for reference):**
|
||||
|
||||
1. Companies / Accounts / Organizations
|
||||
2. Contacts / People
|
||||
3. Deals / Opportunities
|
||||
4. Notes and Activities
|
||||
5. كائنات مخصصة
|
||||
|
||||
## Step 5: Clean and Format Your Data
|
||||
|
||||
Open each exported CSV in a spreadsheet application and prepare it for Twenty.
|
||||
|
||||
### Remove Duplicates
|
||||
|
||||
1. Sort by the unique field (email for People, domain for Companies)
|
||||
2. Remove or merge duplicate rows
|
||||
3. Verify no duplicates exist in Twenty already
|
||||
|
||||
### Format Fields Correctly
|
||||
|
||||
| Field Type | Required Format |
|
||||
| --------------------- | ------------------------------------------------- |
|
||||
| **Domain** | `https://domain.com` |
|
||||
| **البريد الإلكتروني** | `name@domain.com` (must be unique) |
|
||||
| **Date** | `YYYY-MM-DD` |
|
||||
| **Phone** | Three columns: Number, Country Code, Calling Code |
|
||||
| **Boolean** | `TRUE` or `FALSE` (uppercase) |
|
||||
| **Select fields** | Use API names, not display labels |
|
||||
|
||||
<Warning>
|
||||
**Domain format is critical.**
|
||||
|
||||
Use `https://domain.com` (not `domain.com` or `www.domain.com`). This matches Twenty's format and prevents duplicates when you connect email/calendar sync.
|
||||
</Warning>
|
||||
|
||||
See [How to Prepare Your CSV Files](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files) for complete formatting requirements for all field types.
|
||||
|
||||
### Add Relation Columns
|
||||
|
||||
To link records (e.g., People to Companies), add a column with the parent's unique identifier.
|
||||
|
||||
**Example: People CSV with Company link**
|
||||
|
||||
```csv
|
||||
firstName,lastName,email,companyDomain
|
||||
John,Smith,john@acme.com,https://acme.com
|
||||
Jane,Doe,jane@widgets.co,https://widgets.co
|
||||
```
|
||||
|
||||
See [How to Import Relations](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) for detailed instructions on linking records.
|
||||
|
||||
### Update User References
|
||||
|
||||
If your data includes user assignments (Owner, Assignee):
|
||||
|
||||
1. Add a column with the **user's email** (not just their ID from the old system)
|
||||
2. Use the same email addresses that users used to join your Twenty workspace
|
||||
|
||||
See [How to Prepare Your CSV Files](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files) for complete formatting guide.
|
||||
|
||||
## Step 6: Import to Twenty
|
||||
|
||||
<Warning>
|
||||
**Import Order Matters!**
|
||||
|
||||
Always import in this order:
|
||||
|
||||
1. **Companies** first (no dependencies)
|
||||
2. **People** second (link to Companies)
|
||||
3. **Opportunities** third (link to Companies/People)
|
||||
4. **Notes and Tasks** (link to records)
|
||||
5. **Custom objects** following their dependencies
|
||||
|
||||
The parent record must exist before you can reference it.
|
||||
</Warning>
|
||||
|
||||
### Import Each Object
|
||||
|
||||
For each CSV file, in order:
|
||||
|
||||
1. Navigate to the object in Twenty
|
||||
2. Click **⋮ → Import records**
|
||||
3. Upload the CSV file
|
||||
4. Map columns to fields:
|
||||
* Map user email columns to the appropriate relation fields
|
||||
* Map relation columns (like `companyDomain`) to relation fields
|
||||
5. Review and fix any errors in the UI
|
||||
6. Confirm the import
|
||||
7. Verify a few records before proceeding to the next file
|
||||
|
||||
**Detailed guides:**
|
||||
|
||||
* [How to Import Companies](/l/ar/user-guide/data-migration/how-tos/import-companies-via-csv)
|
||||
* [How to Import Contacts](/l/ar/user-guide/data-migration/how-tos/import-contacts-via-csv)
|
||||
* [How to Import Relations](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv)
|
||||
|
||||
## Step 7: Large Migrations (50,000+ Records)
|
||||
|
||||
For large migrations:
|
||||
|
||||
| Volume | Recommended Approach |
|
||||
| ----------------------- | ----------------------------- |
|
||||
| Under 10,000 records | Single CSV import |
|
||||
| 10,000 - 50,000 records | Split into multiple CSV files |
|
||||
| 50,000+ records | Use the API |
|
||||
|
||||
**For API imports:**
|
||||
|
||||
* Faster and more reliable for large datasets
|
||||
* Supports batch operations (up to 60 records per call)
|
||||
* See [How to Import Data via API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api)
|
||||
|
||||
## Step 8: Post-Migration Setup
|
||||
|
||||
After importing data, complete your workspace configuration:
|
||||
|
||||
### Recreate Views
|
||||
|
||||
* Set up saved views with filters, sorts, and column configurations
|
||||
* Create any kanban or calendar views you need
|
||||
|
||||
### إعادة إنشاء سير العمل
|
||||
|
||||
* Rebuild your automations in **Settings → Workflows**
|
||||
* Start with the most critical workflows
|
||||
* Test each one before relying on it
|
||||
|
||||
### Configure Roles and Permissions
|
||||
|
||||
* Set up roles in **Settings → Roles**
|
||||
* Assign users to appropriate roles
|
||||
|
||||
### Connect Email and Calendar
|
||||
|
||||
* Each user connects their own account in **Settings → Accounts**
|
||||
* Twenty will start syncing emails to contact records
|
||||
* See [Email & Calendar](/l/ar/user-guide/calendar-emails/overview)
|
||||
|
||||
### Train Your Team
|
||||
|
||||
* Walk through the new interface together
|
||||
* Document any team-specific processes
|
||||
|
||||
## المشاكل الشائعة والحلول
|
||||
|
||||
| Issue | Cause | Solution |
|
||||
| ----------------------- | --------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| **Duplicate errors** | Email/domain already exists | Remove duplicates from file, or include unique identifier to update existing records |
|
||||
| **Relation not found** | Parent record doesn't exist | Import parent objects first (Companies before People) |
|
||||
| **Missing fields** | Custom field doesn't exist | Create field in Settings → Data Model before importing |
|
||||
| **Select field errors** | Using display labels | Use API names (enable Advanced mode in Settings to find them) |
|
||||
| **User relation empty** | User hasn't accepted invite | Ensure all users accept invitations before importing |
|
||||
|
||||
See [How to Fix Import Errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors) for detailed troubleshooting steps.
|
||||
|
||||
## قائمة التحقق بعد التحويل
|
||||
|
||||
### Data Integrity
|
||||
|
||||
<Check>All records imported (compare counts with source system)</Check>
|
||||
<Check>Relations working correctly (People linked to Companies)</Check>
|
||||
<Check>User assignments mapped correctly (Owner, Assignee)</Check>
|
||||
<Check>Custom fields populated</Check>
|
||||
<Check>No unexpected duplicates</Check>
|
||||
|
||||
### التكوين
|
||||
|
||||
<Check>Views recreated</Check>
|
||||
<Check>Workflows recreated and tested</Check>
|
||||
<Check>Roles and permissions configured</Check>
|
||||
<Check>Email/calendar sync connected</Check>
|
||||
|
||||
### Team Readiness
|
||||
|
||||
<Check>Team trained on new system</Check>
|
||||
<Check>Old CRM access plan decided (keep for reference? When to disable?)</Check>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I migrate workflows automatically?">
|
||||
Not currently. Workflows must be recreated manually in Twenty.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What about file attachments?">
|
||||
File attachments are not included in CSV exports. You'll need to re-upload them manually, migrate via API, or contact our team for assistance.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I keep both systems running during migration?">
|
||||
Yes, we recommend keeping your old CRM running until you've verified the migration is complete. Just be careful not to create new data in both places.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How long does migration typically take?">
|
||||
Depends on data volume and complexity. Small migrations (under 10,000 records) can be done in a few hours. Large migrations may take several days including data cleanup and testing.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## هل تحتاج إلى مساعدة؟
|
||||
|
||||
For complex migrations or large datasets:
|
||||
|
||||
* **Guided setup:** Book a 4-hour onboarding pack
|
||||
* **Full migration service:** Our partners can handle the entire migration
|
||||
|
||||
Contact [contact@twenty.com](mailto:contact@twenty.com) or explore our [Implementation Services](/l/ar/user-guide/getting-started/capabilities/implementation-services).
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: التحويل من نظام ذاتي الاستضافة إلى السحابة
|
||||
description: Step-by-step guide to migrate your Twenty self-hosted instance to Twenty Cloud.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
This guide walks you through migrating your data from a Twenty self-hosted instance to Twenty Cloud. The process involves setting up your cloud workspace, exporting your data, and re-importing it.
|
||||
|
||||
<Note>Views, workflows, and roles must be recreated manually after migration. Plan time for this configuration work.</Note>
|
||||
|
||||
## Step 1: Create Your Cloud Workspace
|
||||
|
||||
1. Go to [app.twenty.com](https://app.twenty.com) and create a new workspace
|
||||
2. Complete the initial setup wizard
|
||||
3. Note your new workspace URL
|
||||
|
||||
## Step 2: Recreate Your Data Model
|
||||
|
||||
Before importing data, recreate your custom objects and fields:
|
||||
|
||||
1. Go to **Settings → Data Model** in your cloud instance
|
||||
2. Create custom objects that match your self-hosted setup
|
||||
3. Add custom fields to standard and custom objects
|
||||
4. Configure field settings (unique, required, etc.)
|
||||
|
||||
<Note>Take screenshots of your self-hosted data model for reference, or keep both instances open side by side.</Note>
|
||||
|
||||
## Step 3: Invite All Users
|
||||
|
||||
<Warning>
|
||||
**Critical: Invite users BEFORE importing data.**
|
||||
|
||||
Users must accept their invitations before you import any records that reference them (like Account Owner fields). If users don't exist yet, those relations cannot be mapped.
|
||||
</Warning>
|
||||
|
||||
1. Go to **Settings → Members** in your cloud instance
|
||||
2. Invite all team members who had accounts on self-hosted
|
||||
3. **Wait for everyone to accept** their invitation
|
||||
4. Verify all users appear in your Members list
|
||||
|
||||
## Step 4: Export Data from Self-Hosted
|
||||
|
||||
Export each object from your self-hosted instance:
|
||||
|
||||
1. Navigate to each object (Companies, People, Opportunities, etc.)
|
||||
2. Configure the view to show **all columns** you want to migrate
|
||||
3. Click **⋮ → Export view**
|
||||
4. Save each CSV file with a clear name (e.g., `companies-export.csv`)
|
||||
|
||||
**Export in this order** (for reference when importing):
|
||||
|
||||
1. الشركات
|
||||
2. الأشخاص
|
||||
3. الفرص
|
||||
4. Custom objects (following their dependencies)
|
||||
5. Tasks, Notes
|
||||
|
||||
## Step 5: Update Workspace Member References
|
||||
|
||||
The exported CSVs contain user IDs from your self-hosted instance. These IDs won't match your cloud instance, so you need to replace them with emails.
|
||||
|
||||
**For each CSV file with user references (Owner, Assignee, etc.):**
|
||||
|
||||
1. Open the CSV in a spreadsheet application
|
||||
2. Add a new column next to each user ID column (e.g., `accountOwnerEmail` next to `accountOwnerId`)
|
||||
3. Fill in the **email address** of each user
|
||||
4. You can delete the old ID column or leave it (it will be skipped during import)
|
||||
|
||||
**Example:**
|
||||
|
||||
قبل:
|
||||
|
||||
```csv
|
||||
name,domain,accountOwnerId
|
||||
Acme Corp,https://acme.com,old-uuid-123
|
||||
```
|
||||
|
||||
بعد:
|
||||
|
||||
```csv
|
||||
name,domain,accountOwnerEmail
|
||||
Acme Corp,https://acme.com,john@yourcompany.com
|
||||
```
|
||||
|
||||
<Note>Use the same email addresses that users used to accept their cloud workspace invitation.</Note>
|
||||
|
||||
## Step 6: Plan Your Import Order
|
||||
|
||||
Import files in the correct order to maintain relationships:
|
||||
|
||||
1. **Companies** first (no dependencies)
|
||||
2. **People** second (link to Companies)
|
||||
3. **Opportunities** third (link to Companies and People)
|
||||
4. **Custom objects** (following their dependencies)
|
||||
5. **Tasks and Notes** last (link to other records)
|
||||
|
||||
See [How to Import Relations](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) for details on maintaining relationships.
|
||||
|
||||
## Step 7: Import to Cloud
|
||||
|
||||
For each CSV file, in order:
|
||||
|
||||
1. Navigate to the object in your cloud instance
|
||||
2. Click **⋮ → Import records**
|
||||
3. Upload the CSV file
|
||||
4. Map columns to fields:
|
||||
* Map user email columns to the appropriate relation fields
|
||||
* Map other columns as usual
|
||||
5. Review and fix any errors
|
||||
6. Confirm the import
|
||||
7. Verify a few records before proceeding to the next file
|
||||
|
||||
## Step 8: Recreate Configuration
|
||||
|
||||
After importing data, manually recreate:
|
||||
|
||||
### العروض
|
||||
|
||||
* Recreate saved views with filters, sorts, and column configurations
|
||||
* Set up any kanban or calendar views
|
||||
|
||||
### سير العمل
|
||||
|
||||
* Recreate automations in **Settings → Workflows**
|
||||
* Test each workflow before relying on it
|
||||
|
||||
### Roles and Permissions
|
||||
|
||||
* Configure roles in **Settings → Roles**
|
||||
* Assign users to appropriate roles
|
||||
|
||||
### التكاملات
|
||||
|
||||
* Reconnect email and calendar sync for each user
|
||||
* Reconfigure any API integrations with new API keys
|
||||
|
||||
## قائمة التحقق بعد التحويل
|
||||
|
||||
<Check>All data imported successfully</Check>
|
||||
<Check>Relations between objects working correctly</Check>
|
||||
<Check>User assignments (Owner, Assignee) mapped correctly</Check>
|
||||
<Check>Views recreated</Check>
|
||||
<Check>Workflows recreated and tested</Check>
|
||||
<Check>Roles and permissions configured</Check>
|
||||
<Check>Email/calendar sync reconnected</Check>
|
||||
<Check>API integrations updated with new keys</Check>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I migrate workflows automatically?">
|
||||
Not currently. Workflows must be recreated manually in your cloud instance.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What about file attachments and images?">
|
||||
File attachments are not included in CSV exports. You'll need to re-upload any attachments manually, migrate them via API or contact our team for assistance with large migrations.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I keep both instances running during migration?">
|
||||
Yes, we recommend keeping your self-hosted instance running until you've verified the cloud migration is complete. Just be careful not to create new data in both places.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if a user hasn't accepted their invitation yet?">
|
||||
Records referencing that user will fail to import or the relation will be empty. Ensure all users accept invitations before importing data.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## هل تحتاج إلى مساعدة؟
|
||||
|
||||
For complex migrations or large datasets, contact us at [contact@twenty.com](mailto:contact@twenty.com) or explore our [Implementation Services](/l/ar/user-guide/getting-started/capabilities/implementation-services).
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
---
|
||||
title: حضِّر ملفات CSV الخاصة بك},{
|
||||
description: دليل كامل خطوة بخطوة لتنسيق بياناتك لاستيرادها إلى Twenty.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يرشدك هذا الدليل إلى كيفية إعداد ملف CSV لاستيراد ناجح. اتّبع هذه الخطوات لتجنّب الأخطاء.
|
||||
|
||||
## الخطوة 1: التحقّق من متطلبات الملف
|
||||
|
||||
قبل البدء، تأكّد من أنّ ملفك يستوفي هذه المتطلبات:
|
||||
|
||||
| المتطلب | تفاصيل |
|
||||
| --------------------- | --------------------- |
|
||||
| **التنسيق** | CSV، XLSX، أو XLS |
|
||||
| **الحد الأقصى للحجم** | 10,000 سجل لكل ملف |
|
||||
| **الترميز** | يوصى باستخدام UTF-8 |
|
||||
| **البنية** | نوع كائن واحد لكل ملف |
|
||||
|
||||
<Note>بالنسبة لمجموعات البيانات التي تزيد على 10,000 سجل، قم بتقسيمها إلى عدة ملفات أو استخدم [الاستيراد عبر API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api).</Note>
|
||||
|
||||
## الخطوة 2: تنزيل الملف النموذجي
|
||||
|
||||
**هذه هي أهم خطوة.** يوضّح لك الملف النموذجي أسماء الأعمدة الدقيقة والتنسيق الذي تتوقعه Twenty.
|
||||
|
||||
1. انتقل إلى عرض الكائن (الأشخاص، الشركات، إلخ)
|
||||
2. انقر **⋮** → **استيراد السجلات**
|
||||
3. انقر **تنزيل الملف النموذجي**
|
||||
4. استخدم هذا الملف كقالب
|
||||
|
||||
<Note>**نصيحة احترافية:** بدلًا من ذلك، صدِّر عددًا قليلًا من السجلات الموجودة. سيوفّر لك هذا أمثلة حقيقية على كيفية تنسيق البيانات، كما ستُطابَق أسماء الأعمدة تلقائيًا أثناء الاستيراد.</Note>
|
||||
|
||||
## الخطوة 3: إزالة القيم المكررة
|
||||
|
||||
تفرض Twenty التفرّد على حقول معيّنة. ستتسبب القيَم المكررة في أخطاء أثناء الاستيراد.
|
||||
|
||||
| كائن | الحقول الفريدة |
|
||||
| ---------------- | ------------------------------------- |
|
||||
| **الأشخاص** | `id`, `email` |
|
||||
| **الشركات** | `id`, `domain` |
|
||||
| **كائنات مخصصة** | `id`، بالإضافة إلى أي حقل وضعته كفريد |
|
||||
|
||||
**قبل الاستيراد:**
|
||||
|
||||
1. قم بفرز جدول البيانات حسب الحقل الفريد (البريد الإلكتروني أو النطاق)
|
||||
2. أزِل الصفوف المكررة أو ادمجها
|
||||
3. تحقّق من التكرارات الموجودة مسبقًا في Twenty
|
||||
|
||||
<Warning>**السجلات المحذوفة مؤقتًا تُحتسب ضمن التفرّد.** السجلات الموجودة في قائمة الأوامر → عرض السجلات المحذوفة ستتسبب في أخطاء تكرار. احذفها نهائيًا أو استعدها وقم بتحديثها.</Warning>
|
||||
|
||||
## الخطوة 4: تنسيق كل نوع من الحقول بشكل صحيح
|
||||
|
||||
تتطلب أنواع الحقول المختلفة تنسيقات محددة. إليك المرجع الكامل:
|
||||
|
||||
### حقول النص
|
||||
|
||||
* لا يلزم تنسيق خاص
|
||||
* تُزال الفراغات في البداية والنهاية تلقائيًا
|
||||
|
||||
### حقول البريد الإلكتروني
|
||||
|
||||
* يجب أن يكون بتنسيق بريد إلكتروني صالح: `name@domain.com`
|
||||
* يجب أن تكون فريدة (بدون تكرارات في الملف أو في Twenty)
|
||||
* للعناوين الإضافية للبريد الإلكتروني، استخدم هذا التنسيق في عمود **Emails / Additional Emails**:
|
||||
|
||||
```
|
||||
[\"jane@twenty.com\",\"jane.doe@twenty.com\"]
|
||||
```
|
||||
|
||||
### حقول النطاق
|
||||
|
||||
* **التنسيق الموصى به**: `https://domain.com`
|
||||
* يتطابق هذا مع التنسيق المستخدم في مزامنة صندوق البريد/التقويم (يمنع التكرارات)
|
||||
* املأ كلا العمودين:
|
||||
* **Domain / Domain Label**: `domain.com`
|
||||
* **Domain / Domain URL**: `https://domain.com`
|
||||
* يجب أن تكون فريدة داخل ملفك وفي Twenty
|
||||
|
||||
### حقول الهاتف
|
||||
|
||||
الهاتف حقل **متداخل** يتطلب عدة أعمدة:
|
||||
|
||||
| العمود | مثال |
|
||||
| --------------------------------------- | ------------ |
|
||||
| **Phones / Primary Phone Number** | `4159095555` |
|
||||
| **Phones / Primary Phone Country Code** | `US` |
|
||||
| **Phones / Primary Phone Calling Code** | `+1` |
|
||||
|
||||
### Address Fields
|
||||
|
||||
Address is a **nested field** with multiple columns (some can be left empty):
|
||||
|
||||
* **Address / Address 1**: Street address line 1
|
||||
* **Address / Address 2**: Street address line 2 (optional)
|
||||
* **Address / City**: City name
|
||||
* **Address / State**: State or province
|
||||
* **Address / Country**: Country name
|
||||
* **Address / Post Code**: Postal/ZIP code
|
||||
|
||||
### Date Fields
|
||||
|
||||
Use consistent formatting throughout your file:
|
||||
|
||||
* `YYYY-MM-DD` (recommended): `2024-03-15`
|
||||
* `MM/DD/YYYY`: `03/15/2024`
|
||||
* `DD/MM/YYYY`: `15/03/2024`
|
||||
* ISO 8601: `2024-03-15T10:30:00Z`
|
||||
|
||||
### Number Fields
|
||||
|
||||
* Numbers only (no text)
|
||||
* Use period for decimals: `1234.56`
|
||||
* No thousands separators (not `1,234.56`)
|
||||
|
||||
### Currency Fields
|
||||
|
||||
Currency is a **nested field** requiring two columns that **both must be filled**:
|
||||
|
||||
| Column | مثال |
|
||||
| --------------------- | --------- |
|
||||
| **Amount / Amount** | `1234.56` |
|
||||
| **Amount / Currency** | `USD` |
|
||||
|
||||
### Boolean Fields
|
||||
|
||||
Use uppercase: `TRUE` or `FALSE`
|
||||
|
||||
<Warning>Lowercase `true` or `false` will not work.</Warning>
|
||||
|
||||
### اختر الحقول
|
||||
|
||||
Use the **API name** of the option, not the display label.
|
||||
|
||||
**How to find API names:**
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object and field
|
||||
3. Enable **Advanced mode** (toggle at bottom right)
|
||||
4. Copy the API name (e.g., `OPTION_1`, not "Option 1")
|
||||
|
||||
<Note>New select options are not created automatically. Add them in **Settings → Data Model** before importing.</Note>
|
||||
|
||||
### Multi-Select Fields
|
||||
|
||||
Use API names in array format:
|
||||
|
||||
```
|
||||
["VALUE1","VALUE2"]
|
||||
```
|
||||
|
||||
### Array Fields
|
||||
|
||||
Use JSON array format:
|
||||
|
||||
```
|
||||
["value1","value2"]
|
||||
```
|
||||
|
||||
### Rating Fields
|
||||
|
||||
Use the format: `RATING_1`, `RATING_2`, `RATING_3`, `RATING_4`, or `RATING_5`
|
||||
|
||||
### Links/URL Fields
|
||||
|
||||
Fill both columns:
|
||||
|
||||
* **Links / Link Label**: `Twenty`
|
||||
* **Links / Link URL**: `https://twenty.com`
|
||||
|
||||
For secondary links, use the **Links / Secondary Links** column:
|
||||
|
||||
```
|
||||
[{"url":"https://twenty.com","label":"Twenty"}]
|
||||
```
|
||||
|
||||
### JSON Fields
|
||||
|
||||
Use valid JSON format:
|
||||
|
||||
```
|
||||
{"key":"value","key2":"value2"}
|
||||
```
|
||||
|
||||
### ID Fields
|
||||
|
||||
* **Optional**: Twenty auto-generates IDs if not provided
|
||||
* **Format**: UUID (e.g., `c776ee49-f608-4a77-8cc8-6fe96ae1e43f`)
|
||||
* **Use case**: Include ID to update existing records instead of creating new ones
|
||||
|
||||
## Step 5: Add Relation Columns (If Linking Records)
|
||||
|
||||
To link records to other objects (e.g., People to Companies), add a column with the unique identifier of the related record.
|
||||
|
||||
**Example**: Linking People to Companies
|
||||
|
||||
Add a column to your People CSV:
|
||||
|
||||
```
|
||||
firstName,lastName,email,companyDomain
|
||||
John,Smith,john@acme.com,https://acme.com
|
||||
Jane,Doe,jane@widgets.co,https://widgets.co
|
||||
```
|
||||
|
||||
**Important rules for relations:**
|
||||
|
||||
* The parent record must already exist in Twenty
|
||||
* Use the **Domain URL** format (`https://domain.com`), not the label
|
||||
* Map only ONE unique identifier (don't include both `companyId` AND `companyDomain`)
|
||||
* For Workspace Members, use their **email** (not name)
|
||||
|
||||
<Warning>
|
||||
**Import Order Matters!**
|
||||
|
||||
Import the "one" side before the "many" side:
|
||||
|
||||
1. **Companies** first
|
||||
2. **People** second (with company reference)
|
||||
3. **Opportunities** third
|
||||
|
||||
The parent record must exist before you can reference it.
|
||||
</Warning>
|
||||
|
||||
See [How to Import Relations](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) for detailed instructions.
|
||||
|
||||
## Step 6: Ensure Fields Exist in Twenty
|
||||
|
||||
The import creates **records**, not **fields**. All fields you want to import must already exist in your data model.
|
||||
|
||||
**Before importing:**
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select your object
|
||||
3. Create any custom fields you need
|
||||
4. Note the exact field names (they must match your column headers)
|
||||
|
||||
## Step 7: Final Checklist
|
||||
|
||||
Before uploading your file, verify:
|
||||
|
||||
<Check>File is CSV, XLSX, or XLS format</Check>
|
||||
<Check>File has fewer than 10,000 records</Check>
|
||||
<Check>Encoding is UTF-8</Check>
|
||||
<Check>No duplicate emails (for People) or domains (for Companies)</Check>
|
||||
<Check>Dates use consistent format throughout</Check>
|
||||
<Check>Domains use `https://domain.com` format</Check>
|
||||
<Check>Boolean fields use `TRUE` or `FALSE` (uppercase)</Check>
|
||||
<Check>Select fields use API names, not display labels</Check>
|
||||
<Check>All custom fields exist in Settings → Data Model</Check>
|
||||
<Check>Parent records imported before child records</Check>
|
||||
<Check>Relation columns reference existing records</Check>
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
| Mistake | Solution |
|
||||
| -------------------------------------------- | ------------------------------------- |
|
||||
| Using `true` instead of `TRUE` | Boolean values must be uppercase |
|
||||
| Using display labels for Select fields | Find and use API names in Settings |
|
||||
| Importing People before Companies | Always import parent objects first |
|
||||
| Missing currency code for Currency fields | Fill both Amount and Currency columns |
|
||||
| Wrong domain format | Use `https://domain.com` consistently |
|
||||
| Mapping multiple unique fields for relations | Map only ONE (domain OR id, not both) |
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
Your file is ready! Now:
|
||||
|
||||
* [Import Companies](/l/ar/user-guide/data-migration/how-tos/import-companies-via-csv) (import these first)
|
||||
* [Import Contacts](/l/ar/user-guide/data-migration/how-tos/import-contacts-via-csv)
|
||||
* [Fix any import errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
---
|
||||
title: Update Existing Records via Import
|
||||
description: Complete step-by-step guide to bulk updating records using CSV import.
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Need to update many records at once? Instead of editing them one by one, use the CSV import to bulk update existing records.
|
||||
|
||||
**حالات الاستخدام:**
|
||||
|
||||
* Update job titles for multiple people
|
||||
* Change company information in bulk
|
||||
* Add data to new custom fields
|
||||
* Correct data errors across many records
|
||||
|
||||
## كيف يعمل
|
||||
|
||||
When you import a file containing a **unique identifier** that matches an existing record, Twenty updates that record instead of creating a duplicate.
|
||||
|
||||
| If unique identifier... | Twenty will... |
|
||||
| -------------------------- | ------------------------------------------------ |
|
||||
| Matches an existing record | **Update** the existing record |
|
||||
| Doesn't match any record | **Create** a new record |
|
||||
| Is missing from your file | **Create** a new record (with auto-generated ID) |
|
||||
|
||||
<Warning>
|
||||
**Multi-Select fields are overwritten, not merged.**
|
||||
|
||||
If a record has `Option A` and `Option B` selected, and you import `["Option C"]`, the record will only have `Option C` after import. The import replaces all previous selections—it does not add to them.
|
||||
|
||||
To keep existing values, include them all in your import: `["Option A","Option B","Option C"]`
|
||||
</Warning>
|
||||
|
||||
## Step 1: Export Your Current Data
|
||||
|
||||
First, export the records you want to update:
|
||||
|
||||
1. Navigate to the object (People, Companies, etc.)
|
||||
2. **Add the columns you need** — click **Options → Fields** to show the fields you want to update
|
||||
3. **Filter if needed** — narrow down to only the records you want to update
|
||||
4. Click **⋮** → **Export view**
|
||||
5. Save the CSV file
|
||||
|
||||
<Note>**Why export first?** The exported file has the correct format, includes unique identifiers, and maps automatically during import.</Note>
|
||||
|
||||
### What Gets Exported
|
||||
|
||||
* All visible columns in your current view
|
||||
* The record's unique identifiers (`id`, `email`, `domain`)
|
||||
* Current field values you can modify
|
||||
|
||||
## Step 2: Edit the CSV File
|
||||
|
||||
Open the exported file in your spreadsheet application (Excel, Google Sheets, etc.):
|
||||
|
||||
1. **Keep the unique identifier column** — don't delete `id`, `email`, or `domain`
|
||||
2. **Update the values** in the columns you want to change
|
||||
3. **Remove columns you don't need to update** (optional, but cleaner)
|
||||
4. **Don't change unique identifier values** — or Twenty will create new records
|
||||
|
||||
### Example: Updating Job Titles
|
||||
|
||||
**Exported file:**
|
||||
|
||||
```csv
|
||||
id,email,firstName,lastName,jobTitle
|
||||
550e8400-e29b-41d4-a716-446655440001,john@acme.com,John,Smith,Sales Rep
|
||||
550e8400-e29b-41d4-a716-446655440002,jane@acme.com,Jane,Doe,Sales Rep
|
||||
550e8400-e29b-41d4-a716-446655440003,bob@acme.com,Bob,Johnson,Sales Rep
|
||||
```
|
||||
|
||||
**After your edits:**
|
||||
|
||||
```csv
|
||||
id,email,firstName,lastName,jobTitle
|
||||
550e8400-e29b-41d4-a716-446655440001,john@acme.com,John,Smith,Account Executive
|
||||
550e8400-e29b-41d4-a716-446655440002,jane@acme.com,Jane,Doe,Senior Account Executive
|
||||
550e8400-e29b-41d4-a716-446655440003,bob@acme.com,Bob,Johnson,Account Executive
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**Don't change the unique identifier values.**
|
||||
|
||||
If you change `john@acme.com` to `john.smith@acme.com`, Twenty will create a new record instead of updating the existing one.
|
||||
</Warning>
|
||||
|
||||
## Step 3: Import the Updated File
|
||||
|
||||
1. Navigate to the object
|
||||
2. Click **⋮** → **Import records**
|
||||
3. Upload your edited CSV file
|
||||
4. **Ensure the unique identifier is mapped** — verify `email`, `domain`, or `id` is mapped correctly
|
||||
5. Review the field mappings
|
||||
6. Check for errors
|
||||
7. Click **Confirm**
|
||||
|
||||
Twenty matches records by the unique identifier and updates them with new values.
|
||||
|
||||
## Choosing the Right Unique Identifier
|
||||
|
||||
| كائن | Recommended | Alternative | الملاحظات |
|
||||
| ---------------- | ------------------- | ----------- | ---------------------------- |
|
||||
| **People** | `البريد الإلكتروني` | `id` | Email is human-readable |
|
||||
| **الشركات** | `النطاق` | `id` | Domain is human-readable |
|
||||
| **كائنات مخصصة** | Any unique field | `id` | Use your custom unique field |
|
||||
|
||||
<Note>**Use only ONE unique identifier.** Don't map both `email` AND `id`. This can cause confusion and errors.</Note>
|
||||
|
||||
### Using Custom Unique Fields
|
||||
|
||||
If you have a custom field marked as unique (like an external ID from another system):
|
||||
|
||||
1. Include that field in your export and import
|
||||
2. Map it during import
|
||||
3. Twenty will match on that field
|
||||
|
||||
## Step 4: Verify the Updates
|
||||
|
||||
After importing:
|
||||
|
||||
1. Open a few updated records
|
||||
2. Verify the changes were applied
|
||||
3. Check that no duplicate records were created
|
||||
|
||||
## What About Fields Not in Your File?
|
||||
|
||||
**Fields not included in your import file remain unchanged.**
|
||||
|
||||
| Your file includes... | النتيجة |
|
||||
| ---------------------------- | ------------------------------------------------------ |
|
||||
| `email`, `jobTitle` | Only `jobTitle` is updated; other fields stay the same |
|
||||
| `email`, `jobTitle`, `phone` | `jobTitle` and `phone` are updated |
|
||||
|
||||
This means you only need to include the fields you want to change (plus the unique identifier).
|
||||
|
||||
## Combining Updates and New Records
|
||||
|
||||
You can update existing records AND create new ones in the same import:
|
||||
|
||||
```csv
|
||||
email,firstName,lastName,jobTitle
|
||||
john@acme.com,John,Smith,Senior Manager ← Updates existing (email matches)
|
||||
newperson@acme.com,New,Person,Analyst ← Creates new (email doesn't match)
|
||||
```
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
| Mistake | Problem | النتيجة | Solution |
|
||||
| ------------------------------ | ------------------------------------------------------- | -------------------------------------- | ----------------------------------------- |
|
||||
| **Changing unique identifier** | Changed `john@acme.com` to `john.smith@acme.com` | Creates new record instead of updating | Keep unique identifiers unchanged |
|
||||
| **Multiple unique fields** | Mapping both `email` AND `id` | Potential matching conflicts | Map only ONE unique identifier |
|
||||
| **No unique identifier** | File only has `firstName`, `lastName`, `jobTitle` | All rows create new records | Always include `email`, `domain`, or `id` |
|
||||
| **Case mismatch** | File has `John@acme.com` but Twenty has `john@acme.com` | Creates new record | Export from Twenty to get exact values |
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What if some records don't exist yet?">
|
||||
Records with unique identifiers that don't match existing records will be created as new records. This lets you update and create in the same import.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I clear/empty a field value?">
|
||||
Yes, leave the cell empty in your CSV. The import will clear that field's value on the existing record.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens to fields I don't include in the import?">
|
||||
Fields not in your import file remain unchanged on existing records. Only fields you include are updated.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I update relation fields (like Company)?">
|
||||
نعم! Include the relation's unique identifier (e.g., `companyDomain`) and map it to the relation field. The relation will be updated.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I know which records will be updated vs. created?">
|
||||
During the import review step, Twenty shows you how many records will be updated vs. created based on unique identifier matches.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I undo a bulk update?">
|
||||
There's no automatic undo. We recommend exporting your data as a backup before making bulk updates.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
1. **Export first** — always start from an export to ensure correct format
|
||||
2. **Backup before updating** — export your data before making bulk changes
|
||||
3. **Test with a few records** — try updating 5-10 records first before doing a large batch
|
||||
4. **Use human-readable identifiers** — `email` and `domain` are easier to verify than `id`
|
||||
5. **Only include necessary columns** — fewer columns means less chance for errors
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
Having issues? Check:
|
||||
|
||||
* [How to Fix Import Errors](/l/ar/user-guide/data-migration/how-tos/fix-import-errors)
|
||||
* [Uniqueness Constraints](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
|
||||
* [Field Mapping Reference](/l/ar/user-guide/data-migration/capabilities/field-mapping)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: ترحيل البيانات},{
|
||||
description: استيراد وتصدير بيانات CRM عبر ملفات CSV أو عبر API.
|
||||
image: /images/user-guide/import-export-data/cloud.png
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/import-export-data/cloud.png" alt="ترحيل البيانات" />
|
||||
</Frame>
|
||||
|
||||
## طرق الاستيراد
|
||||
|
||||
تدعم Twenty طريقتين رئيستين لاستيراد البيانات:
|
||||
|
||||
| طريقة | الأفضل لـ | حد الحجم |
|
||||
| --------------------- | ----------------------------------- | ------------------ |
|
||||
| **استيراد CSV** | عمليات ترحيل قياسية، تحديثات منتظمة | 10,000 سجل لكل ملف |
|
||||
| **الاستيراد عبر API** | عمليات ترحيل واسعة النطاق، أتمتة | غير محدود |
|
||||
|
||||
<Note>بالنسبة لمجموعات البيانات الكبيرة جداً (مئات الآلاف من السجلات)، استخدم API. يمكن لشركائنا في [التنفيذ](/l/ar/user-guide/getting-started/capabilities/implementation-services) المساعدة في تشغيل هذه النصوص البرمجية عند الحاجة.</Note>
|
||||
|
||||
## أساسيات استيراد CSV
|
||||
|
||||
يمكنك استيراد البيانات لأي جسم باستخدام ملفات CSV أو XLSX أو XLS. يجب أن يحتوي كل ملف على **نوع واحد فقط من الأجسام** (مثلًا، سجلات الأشخاص فقط).
|
||||
|
||||
<Note>**يجب أن تكون الحقول موجودة قبل الاستيراد.** يؤدي رفع ملف CSV إلى إنشاء سجلات لكنه لا ينشئ حقولاً. إذا كنت تحتاج إلى حقول مخصّصة، فأنشئها أولاً ضمن **الإعدادات → نموذج البيانات**.</Note>
|
||||
|
||||
### الخطوات
|
||||
|
||||
1. انتقل إلى الجسم الذي تريد استيراد البيانات إليه
|
||||
2. انقر أيقونة **⋮** في أعلى اليمين (هذه هي قائمة الأوامر) ثم انقر **استيراد السجلات**
|
||||
3. قم بتنزيل ملف القالب للتأكد من أن بياناتك بالتنسيق المتوقع
|
||||
4. ارفع ملف CSV المُنسَّق الخاص بك
|
||||
5. طابِق أعمدتك مع حقول Twenty
|
||||
6. راجِع الأخطاء (المظلَّلة باللون الأصفر) وأصلحها من خلال التحرير مباشرةً في واجهة المستخدم
|
||||
7. أكد الاستيراد
|
||||
|
||||
### استيراد العلاقات بين الأجسام
|
||||
|
||||
يمكنك استيراد العلاقات بين الأجسام باستخدام ميزة استيراد CSV. تحتاج إلى الإشارة إلى الجسم المرتبط باستخدام حقل فريد من هذا الجسم: `id`، و`email` للأشخاص وأعضاء مساحة العمل، و`domain` للشركات، وأي حقل آخر مُعيَّن كفريد في نموذج البيانات لأي جسم آخر.
|
||||
|
||||
<Note>**تُحتسب السجلات المحذوفة ضمن التفرّد.** السجلات المحذوفة حذفاً ناعماً (المرئية ضمن قائمة الأوامر → عرض السجلات المحذوفة) تُدرج ضمن عمليات التحقق من التفرّد. إذا قمتَ باستيراد سجل بالقيمة الفريدة نفسها لسجل محذوف، فسيتم استعادة السجل المحذوف.</Note>
|
||||
|
||||
<Warning>
|
||||
**ترتيب الاستيراد مهم!**
|
||||
|
||||
عند استيراد أجسام مترابطة، ارفع الملفات بهذا الترتيب:
|
||||
|
||||
1. **الشركات** أولاً (جانب "الواحد" من العلاقات)
|
||||
2. **الأفراد** ثانياً (مرتبطون بالشركات عبر companyId)
|
||||
3. **الفرص** ثالثاً (مرتبطة بالشركات/الأفراد)
|
||||
4. **الأجسام المخصّصة** ذات العلاقات أخيراً
|
||||
|
||||
لماذا؟ يجب أن يكون جانب "الواحد" من علاقة واحد إلى متعدد موجودًا قبل أن تتمكن من الإشارة إليه. على سبيل المثال، يجب أن يكون سجل الشركة موجودًا قبل أن تستورد شخصًا يحمل معرّف تلك الشركة.
|
||||
</Warning>
|
||||
|
||||
يرجى الرجوع إلى [هذه المقالة](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) للاطلاع على دليل خطوة بخطوة حول كيفية المتابعة.
|
||||
|
||||
## تصدير البيانات
|
||||
|
||||
صدّر بيانات مساحة العمل للنسخ الاحتياطي أو إعداد التقارير أو الترحيل.
|
||||
|
||||
### الخطوات
|
||||
|
||||
1. انتقل إلى الجسم الذي تريد تصديره
|
||||
2. قم بتهيئة العرض بالأعمدة التي تحتاجها
|
||||
3. انقر **⋮** → **تصدير العرض**
|
||||
4. احفظ ملف CSV
|
||||
|
||||
<Note>**يتم تصدير الأعمدة المرئية فقط.** سيحتوي ملف CSV فقط على الأعمدة المعروضة في عرضك الحالي. أضِف الأعمدة أو أخفها قبل التصدير للتحكم في البيانات المُدرجة.</Note>
|
||||
|
||||
<Note>**حدود التصدير**: حتى 20,000 سجل لكل عملية تصدير.</Note>
|
||||
|
||||
## الصلاحيات
|
||||
|
||||
يتطلّب استيراد وتصدير البيانات أذونات محددة:
|
||||
|
||||
* **الاستيراد**: يتطلب إذن "Import CSV"
|
||||
* **التصدير**: يتطلب إذن "Export CSV"
|
||||
|
||||
تواصل مع مسؤول مساحة العمل لديك إذا لم تكن تملك هذه الأذونات.
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
* [حضّر ملفات CSV الخاصة بك](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files)
|
||||
* [استيراد العلاقات بين الأجسام](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv)
|
||||
* [الاستيراد عبر API لمجموعات البيانات الكبيرة](/l/ar/user-guide/data-migration/how-tos/import-data-via-api)
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: الحقول
|
||||
description: فهم دور الحقول وكيفية إدارتها.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## حول الحقول
|
||||
|
||||
الحقول مثل الأعمدة في جدول البيانات. تُخزّن أنواعًا مختلفة من البيانات مثل النصوص أو الأرقام أو التواريخ. يمكن أن تكون الحقول قياسية (مدمجة) أو مخصصة (التي تقوم بإنشائها).
|
||||
|
||||
### الحقول القياسية
|
||||
|
||||
الحقول القياسية تأتي مدمجة في Twenty للتعامل مع احتياجات الأعمال العامة.
|
||||
|
||||
على سبيل المثال، `الاسم الأول` و`الاسم الأخير` هما حقول قياسية في كائن `الأشخاص`. تخزن البيانات النصية للأسماء الفردية.
|
||||
|
||||
لا يمكنك حذف الحقول القياسية، ولكن يمكنك إلغاء تنشيطها إذا لم تكن بحاجة إليها.
|
||||
|
||||
يمكنك أيضًا تخصيص خيارات الحقول القياسية من نوع `SELECT`، على سبيل المثال خيارات `Stage` في الفرص.
|
||||
|
||||
<img src="/images/user-guide/fields/standard-fields.png" style={{width:'100%'}} />
|
||||
|
||||
### الحقول المخصصة
|
||||
|
||||
يمكن إضافة الحقول المخصصة إلى أي كائن. يمكنك تخزين النصوص أو الأرقام أو التواريخ أو الخيارات المنسدلة والمزيد. استخدم الحقول المخصصة لتتبع المعلومات الخاصة بأعمالك.
|
||||
|
||||
على سبيل المثال، الحقل المخصص لـ SpaceX يمكن أن يكون `حالة الصاروخ النشطة`، مما يشير إلى ما إذا كان الصاروخ يعمل.
|
||||
|
||||
<img src="/images/user-guide/fields/custom-fields.png" style={{width:'100%'}} />
|
||||
|
||||
## أنواع الحقول
|
||||
|
||||
يدعم Twenty أنواعًا متعددة من الحقول:
|
||||
|
||||
| النوع | الوصف | مثال |
|
||||
| ----------------- | ----------------------------------------------------------------- | --------------------------- |
|
||||
| العنوان | عنوان مُهيكل يتضمن الشارع، المدينة، الولاية، البلد، الرمز البريدي | عنوان المكتب |
|
||||
| مصفوفة | قائمة بقيم نصية | الوسوم |
|
||||
| قيمة منطقية | خانة اختيار صح/خطأ | نشط |
|
||||
| العملة | قيمة نقدية مع رمز العملة | مبلغ الصفقة (USD) |
|
||||
| تاريخ | قيم التاريخ | تاريخ الإغلاق |
|
||||
| التاريخ والوقت | تاريخ مع الوقت | وقت الاجتماع |
|
||||
| النطاق | نطاق موقع الويب (يُستخدم للشركات) | acme.com |
|
||||
| البريد الإلكتروني | عناوين البريد الإلكتروني (مع الأساسي + الإضافي) | بريد جهة الاتصال الإلكتروني |
|
||||
| JSON | بيانات JSON مُهيكلة | بيانات وصفية مخصصة |
|
||||
| روابط | عناوين URL مع تسميات (أساسي + ثانوي) | موقع الويب، لينكدإن |
|
||||
| نص طويل | نص متعدد الأسطر | الوصف، الملاحظات |
|
||||
| التحديد المتعدد | خيارات متعددة من قائمة محددة مسبقًا | وسوم، فئات |
|
||||
| رقم | قيم رقمية (صحيحة أو عشرية) | الكمية، الدرجة |
|
||||
| هاتف | أرقام هواتف مع رمز البلد | هاتف العمل |
|
||||
| تقييم | تقييم بالنجوم (1-5) | الأولوية، الدرجة |
|
||||
| علاقة | روابط إلى السجلات في كائنات أخرى | الشركة → الأشخاص |
|
||||
| اختيار | خيار واحد من قائمة محددة مسبقًا | المرحلة، الحالة |
|
||||
| نص | سطر واحد من النص | الاسم، العنوان |
|
||||
|
||||
## إنشاء حقل مخصص
|
||||
|
||||
لإضافة حقل مخصص لأي كائن، اتبع هذه الخطوات:
|
||||
|
||||
1. اذهب إلى `الإعدادات` في الشريط الجانبي الأيسر.
|
||||
2. اذهب إلى `نموذج البيانات`، ثم حدد الكائن الذي ترغب في تخصيصه.
|
||||
3. تقدم من خلال النقر على `إضافة حقل`.
|
||||
4. اختر اسم الحقل والنوع الذي يناسب احتياجاتك. فكر في إضافة وصف للحقل لفهم أفضل.
|
||||
|
||||
أصبح الحقل الجديد الذي أنشأته الآن متاحًا ضمن حقول التطبيق. لعرضه في عرض محدد، انقر على قائمة الخيارات، ثم اختر `الحقول`.
|
||||
|
||||
<VimeoEmbed videoId="927628219" title="عرض توضيحي بالفيديو" />
|
||||
|
||||
**طريقة سريعة:** اضغط على زر **+** في أعلى يمين أي جدول كائن، ثم اختر `تخصيص الحقول`. سيأخذك هذا مباشرة إلى إعدادات نموذج البيانات.
|
||||
|
||||
<img src="/images/user-guide/fields/quick-new-field.png" style={{width:'100%'}} />
|
||||
|
||||
## إلغاء تنشيط حقل
|
||||
|
||||
يمكنك إلغاء تنشيط حقل لإخفائه من التطبيق دون فقدان بياناتك. اعتبره كإخفاء للحقل بدلاً من حذفه.
|
||||
|
||||
إليك كيفية القيام بذلك:
|
||||
|
||||
1. ابحث عن الحقل الذي تريد إلغاء تنشيطه في إعدادات الكائن الخاصة بك.
|
||||
|
||||
2. انقر على الثلاث نقاط `⋮` بجانب الحقل لفتح القائمة.
|
||||
|
||||
3. اختر `إلغاء التنشيط` من القائمة المنسدلة.
|
||||
|
||||
<img src="/images/user-guide/fields/deactivate-field.png" style={{width:'100%'}} />
|
||||
|
||||
ماذا يحدث عند إلغاء تنشيط حقل؟
|
||||
|
||||
1. **في التطبيق:** يختفي الحقل ولا يمكنك إضافة قيم جديدة إليه.
|
||||
|
||||
2. **العلاقات الموجودة:** إذا كان حقل علاقة، ستبقى الروابط القائمة ولكن لا يمكنك إنشاء روابط جديدة.
|
||||
|
||||
3. **الوصول عبر API:** لا يزال بإمكانك الوصول إلى الحقل وبياناته عبر API.
|
||||
|
||||
يمكنك إعادة تنشيط الحقول القياسية والمخصصة أو لديك خيار حذفها نهائيًا.
|
||||
|
||||
## جعل الحقول فريدة
|
||||
|
||||
اجعل الحقل فريدًا لضمان عدم إمكانية أن تحتوي السجلات المميزة على نفس القيمة. على سبيل المثال، عناوين البريد الإلكتروني فريدة لكل شخص.
|
||||
|
||||
إذا ظهرت لك رسالة خطأ عند تعيين خاصية التفرد، فتحقق من وجود قيم مكررة في بياناتك (بما في ذلك السجلات المحذوفة).
|
||||
|
||||
## أفضل ممارسات تكوين الحقول
|
||||
|
||||
### اتفاقيات التسمية والقيود
|
||||
|
||||
* **يجب أن تكون الأسماء الفردية والجمع مختلفة**: يحتاج API الخاص بنا إلى أسماء مميزة للتغييرات
|
||||
* **أسماء الحقول المحمية**: بعض الأسماء محجوزة للاستخدام الخاص بالنظام (مثل `Type`، `Application`)
|
||||
|
||||
### حقول العملة والهاتف
|
||||
|
||||
* **العملة الافتراضية**: يمكن تهيئتها عبر نموذج البيانات
|
||||
* **أكواد الدول الافتراضية**: يمكن تهيئتها لحقول الهاتف عبر نموذج البيانات
|
||||
|
||||
### حقول الاختيار
|
||||
|
||||
* **يمكن اختيار خيار افتراضي** لكل حقل اختيار
|
||||
|
||||
### حقول نص السجلات
|
||||
|
||||
* **كل كائن لديه حقل عرض رئيسي واحد**: يظهر هذا الحقل في العمود الأول ويمثل السجل عند الربط بأشياء أخرى. يجب أن يكون حقل نصي. على سبيل المثال، يستخدم الأشخاص `الاسم` كحقل رئيسي، لذلك عندما تربط شخصًا بشركة، سترى اسمهم في عرض الشركة.
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: كائنات
|
||||
description: Learn about standard and custom objects in Twenty.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## Standard Objects
|
||||
|
||||
الكائنات القياسية هي كيانات مُعرّفة مسبقًا في مساحة العمل الخاصة بك لمساعدتك في البدء. هي جزء من نموذج بيانات مشترك يمكن الوصول إليه بواسطة جميع مستخدمي Twenty. يمكنك استخدامها كما هي أو تخصيصها أو تعطيلها.
|
||||
|
||||
<img src="/images/user-guide/objects/standard-objects.png" style={{width:'100%'}} />
|
||||
|
||||
### الأشخاص
|
||||
|
||||
كائن "الأشخاص" يخزن جهات الاتصال الخاصة بك. يتضمن تفاصيل الاتصال وتاريخ التفاعل، مما يتيح لك رؤية جميع تفاعلات العملاء في مكان واحد.
|
||||
|
||||
### الشركة
|
||||
|
||||
كائن "الشركات" يخزن المعلومات المتعلقة بحسابات العمل الخاصة بك. يتضمن تفاصيل مثل الصناعة والحجم والموقع. الشركات تتصل بكائنات "الأشخاص" و "الفرص".
|
||||
|
||||
### الفرص
|
||||
|
||||
كائن "الفرص" يخزن البيانات المتعلقة بالصفقات. يتتبع تقدم المبيعات المحتملة من البداية إلى الإغلاق، مع تسجيل المراحل، أحجام الصفقات، الحساب المرتبط، وتاريخ الإغلاق المتوقع. يمكنك عرض مسار المبيعات الخاص بك في تخطيط كانبان.
|
||||
|
||||
### الملاحظات
|
||||
|
||||
The `Notes` object stores free-form notes that can be attached to People, Companies, Opportunities, and other records. Use notes to capture meeting summaries, important details, or any contextual information.
|
||||
|
||||
### المهام
|
||||
|
||||
The `Tasks` object stores to-dos and action items. Tasks can be linked to People, Companies, Opportunities, and other records. Track due dates, assignees, and completion status to stay on top of your follow-ups.
|
||||
|
||||
## كائنات مخصصة
|
||||
|
||||
تتيح لك الكائنات المخصصة تخزين المعلومات الفريدة لمنظمتك والتي لا يمكن للكائنات القياسية التعامل معها. على سبيل المثال، إذا كنت SpaceX، قد ترغب في إنشاء كائن مخصص للصواريخ والإطلاقات.
|
||||
|
||||
<img src="/images/user-guide/objects/custom-objects.png" style={{width:'100%'}} />
|
||||
|
||||
### Creating a New Custom Object
|
||||
|
||||
لإنشاء كائن مخصص جديد:
|
||||
|
||||
1. اذهب إلى الإعدادات في الشريط الجانبي الأيسر.
|
||||
2. تحت قسم مساحة العمل، انتقل إلى نموذج البيانات. هنا ستتمكن من رؤية نظرة عامة على جميع الكائنات القياسية والمخصصة الحالية (النشطة والمعطلة).
|
||||
|
||||
<VimeoEmbed videoId="926288174" title="Video demonstration" />
|
||||
|
||||
3. انقر على "+ كائن جديد" في الأعلى. أدخل الاسم (مفرد وجمع)، اختر أيقونة، أضف وصفًا للكائن المخصص واضغط حفظ (في الزاوية العليا اليمنى). باستخدام القائمة كمثال للكائن المخصص، سيكون المفرد هو "قائمة" والجمع "قوائم" مع الوصف كمثل "قوائم قام المضيفون بإنشائها لعرض ممتلكاتهم."
|
||||
|
||||
4. Your custom object is now created and will appear in your sidebar. You can start adding records to it right away.
|
||||
|
||||
## Managing Objects
|
||||
|
||||
### Deactivating Objects
|
||||
|
||||
If you don't need a standard or custom object:
|
||||
|
||||
1. Go to Settings → Data Model
|
||||
2. Find the object you want to deactivate
|
||||
3. Click the toggle to deactivate it
|
||||
4. The object will be hidden from your workspace but data is preserved
|
||||
|
||||
### Reactivating Objects
|
||||
|
||||
To bring back a deactivated object:
|
||||
|
||||
1. Go to Settings → Data Model
|
||||
2. Look for deactivated objects (they'll be grayed out)
|
||||
3. Click the toggle to reactivate it
|
||||
4. The object and all its data will be restored
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
### When to Create Custom Objects
|
||||
|
||||
* **Unique business entities**: Things specific to your industry or process
|
||||
* **Complex relationships**: When you need to track connections between multiple entities
|
||||
* **Scalable data**: When you might have many instances of something
|
||||
|
||||
### When to Use Fields Instead
|
||||
|
||||
* **Simple attributes**: Properties that describe existing objects
|
||||
* **Categories or labels**: Ways to classify existing records
|
||||
* **Single values**: Information that doesn't need its own lifecycle
|
||||
|
||||
### Object Naming
|
||||
|
||||
* **Use clear, descriptive names**: Make it obvious what the object represents
|
||||
* **Follow conventions**: Use singular for the object name, plural for the collection
|
||||
* **Consider your team**: Choose names everyone will understand
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: حقول العلاقات
|
||||
description: Connect records across different objects using relation fields.
|
||||
---
|
||||
|
||||
## Types of Relations
|
||||
|
||||
### One-to-Many
|
||||
|
||||
One record in Object A can be linked to many records in Object B.
|
||||
|
||||
**Example:** One Company can have many People (employees).
|
||||
|
||||
### Many-to-One
|
||||
|
||||
Many records in Object A can be linked to one record in Object B.
|
||||
|
||||
**Example:** Many People can belong to one Company.
|
||||
|
||||
### Relations to Multiple Object Types
|
||||
|
||||
Some objects can link to multiple object types on one side of the relation.
|
||||
|
||||
**Example:** A Note can be attached to one Person AND one Company AND one Opportunity simultaneously. The Note is on the "many" side, connecting to multiple "one" sides.
|
||||
|
||||
<img src="/images/user-guide/fields/many-to-one-morph.png" style={{width:'100%'}} />
|
||||
|
||||
Similarly, a Project (on the "one" side) could receive links from multiple People, multiple Companies, and multiple Notes.
|
||||
|
||||
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
|
||||
|
||||
<Warning>
|
||||
**Import/Export limitation**: Relations pointing to multiple object types are not yet supported for CSV import/export. This is on our roadmap.
|
||||
</Warning>
|
||||
|
||||
### Many-to-Many
|
||||
|
||||
Many records in Object A can be linked to many records in Object B.
|
||||
|
||||
**Example:** Many People can be linked to many Projects, and vice versa.
|
||||
|
||||
<Warning>
|
||||
**Many-to-Many is not yet supported.**
|
||||
|
||||
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
|
||||
</Warning>
|
||||
|
||||
## Creating a Relation Field
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object where you want to add the relation
|
||||
3. Click **+ Add Field**
|
||||
4. Select **Relation** as the field type
|
||||
5. Choose the target object(s) to relate to
|
||||
6. Configure the relation settings:
|
||||
* **Field name on source object**: The name of the relation field on the object you're editing
|
||||
* **Field name on destination object**: The name of the relation field that will appear on the target object
|
||||
* Relation type (one-to-many, many-to-one)
|
||||
7. انقر على **حفظ**
|
||||
|
||||
## Standard Relations
|
||||
|
||||
Twenty comes with pre-built relations between standard objects:
|
||||
|
||||
| From Object | To Object | Relation Type |
|
||||
| ----------- | --------- | ------------- |
|
||||
| الأشخاص | الشركات | Many-to-One |
|
||||
| الفرص | الشركات | Many-to-One |
|
||||
| الفرص | الأشخاص | Many-to-One |
|
||||
|
||||
## أفضل الممارسات
|
||||
|
||||
### Planning Relations
|
||||
|
||||
* **Map your data model**: Plan relations before creating them
|
||||
* **Consider direction**: Think about which object "owns" the relationship
|
||||
* **Avoid circular dependencies**: Keep your data model clean
|
||||
|
||||
### Naming Relations
|
||||
|
||||
* **Use clear names**: Make it obvious what the relation represents
|
||||
* **Be consistent**: Use similar naming patterns across relations
|
||||
* **Consider both sides**: Name both sides of the relation appropriately
|
||||
|
||||
### Performance
|
||||
|
||||
* **Don't over-relate**: Too many relations can slow down your workspace
|
||||
|
||||
## Limitations
|
||||
|
||||
* **Deleting relations** removes the link but not the related records
|
||||
* **Circular relations** should be avoided for data integrity
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Create Custom Fields
|
||||
description: Step-by-step guide to adding custom fields to any object.
|
||||
---
|
||||
|
||||
Custom fields let you capture information specific to your business. Add them to any object—standard or custom.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object you want to add a field to
|
||||
3. Click **+ Add Field**
|
||||
4. Choose a **field type** (see [Fields](/l/ar/user-guide/data-model/capabilities/fields) for all types)
|
||||
5. Enter the **field name** and optional description
|
||||
6. Configure field-specific settings (see below)
|
||||
7. انقر على **حفظ**
|
||||
|
||||
**Quick method:** Click the **+** at the end of column headers in any table view → **Customize fields**.
|
||||
|
||||
## Show the Field in Views
|
||||
|
||||
New fields aren't automatically visible. To display:
|
||||
|
||||
1. Open the object's table view
|
||||
2. Click **Options → Fields**
|
||||
3. Click the **eye icon** next to your field to show it
|
||||
4. Drag to reorder
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### For Select / Multi-Select
|
||||
|
||||
1. Click **+ Add option** to create choices
|
||||
2. Set a **default option** if desired
|
||||
3. Drag to reorder options
|
||||
|
||||
<Note>
|
||||
**Use API names for imports.** Enable **Advanced mode** in Settings to see API names. See [Field Mapping](/l/ar/user-guide/data-migration/capabilities/field-mapping).
|
||||
</Note>
|
||||
|
||||
### For Currency Fields
|
||||
|
||||
Set the **default currency** (USD, EUR, etc.) for new records.
|
||||
|
||||
### For Phone Fields
|
||||
|
||||
Set the **default country code** to pre-fill for new phone numbers.
|
||||
|
||||
### Making a Field Unique
|
||||
|
||||
Toggle **Unique** to prevent duplicate values across records.
|
||||
|
||||
<Note>
|
||||
If duplicates exist (including in deleted records), you'll get an error. Clean up duplicates first.
|
||||
</Note>
|
||||
|
||||
### Setting Default Values
|
||||
|
||||
For Select fields, you can choose which option is pre-selected for new records. For Checkbox fields, set whether it's checked or unchecked by default.
|
||||
|
||||
## Deactivating a Field
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Find the field
|
||||
3. Click **⋮ → Deactivate**
|
||||
|
||||
Data is preserved. You can reactivate or permanently delete later.
|
||||
|
||||
## Related
|
||||
|
||||
* [Fields](/l/ar/user-guide/data-model/capabilities/fields) — all field types explained
|
||||
* [Data Model FAQ](/l/ar/user-guide/data-model/how-tos/data-model-faq) — common questions
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Create Custom Objects
|
||||
description: Step-by-step guide to creating custom objects in Twenty.
|
||||
---
|
||||
|
||||
Custom objects let you store information unique to your business that standard objects don't cover. For example: Projects, Products, Tickets, or Listings.
|
||||
|
||||
<Note>
|
||||
**Not sure if you need an object or a field?** See [Understanding Your Data Model](/l/ar/user-guide/data-model/overview) for guidance.
|
||||
</Note>
|
||||
|
||||
## Steps
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Click **+ New object**
|
||||
3. Fill in:
|
||||
* **Singular name** (e.g., "Listing")
|
||||
* **Plural name** (e.g., "Listings")
|
||||
* **Icon**
|
||||
* **Description** (optional)
|
||||
4. انقر على **حفظ**
|
||||
|
||||
Your object appears in the sidebar immediately.
|
||||
|
||||
## Next: Add Fields
|
||||
|
||||
New objects start with basic fields. Add custom fields to capture the data you need:
|
||||
|
||||
1. In **Settings → Data Model**, select your object
|
||||
2. Click **+ Add Field**
|
||||
3. Choose a field type, configure, and save
|
||||
|
||||
See [How to Create Custom Fields](/l/ar/user-guide/data-model/how-tos/create-custom-fields) for details on field types and configuration.
|
||||
|
||||
## Connecting to Other Objects
|
||||
|
||||
To link your object to People, Companies, or other objects, create a relation field. See [How to Create Relation Fields](/l/ar/user-guide/data-model/how-tos/create-relation-fields).
|
||||
|
||||
## Deactivating an Object
|
||||
|
||||
If you no longer need an object:
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Toggle the object off
|
||||
|
||||
The object is hidden but data is preserved. You can reactivate or permanently delete later.
|
||||
|
||||
## Related
|
||||
|
||||
* [Objects](/l/ar/user-guide/data-model/capabilities/objects) — standard vs custom objects
|
||||
* [Data Model FAQ](/l/ar/user-guide/data-model/how-tos/data-model-faq) — common questions
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Create Relation Fields
|
||||
description: Step-by-step guide to connecting objects with relation fields.
|
||||
---
|
||||
|
||||
Relation fields connect records from different objects—for example, linking People to Companies.
|
||||
|
||||
<Note>
|
||||
**Relation names cannot be changed after creation** (they affect the API). Plan your names carefully.
|
||||
</Note>
|
||||
|
||||
## قبل أن تبدأ
|
||||
|
||||
Decide:
|
||||
|
||||
* Which objects are you connecting? (e.g., People → Companies)
|
||||
* Which is the "one" side? (e.g., Company)
|
||||
* Which is the "many" side? (e.g., People — many people work at one company)
|
||||
* What should the field be named on each side?
|
||||
|
||||
See [Relation Fields](/l/ar/user-guide/data-model/capabilities/relation-fields) for relation types explained.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Select the object where you want the relation (typically the "many" side)
|
||||
3. Click **+ Add Field**
|
||||
4. Select **Relation** as the field type
|
||||
5. Choose the **target object**
|
||||
6. Select **One-to-Many** or **Many-to-One**
|
||||
7. Enter field names for **both sides** of the relation
|
||||
8. انقر على **حفظ**
|
||||
|
||||
## Example: People → Companies
|
||||
|
||||
* Go to **Settings → Data Model → People**
|
||||
* Add a Relation field
|
||||
* Target: **Companies**
|
||||
* Type: **Many-to-One**
|
||||
* Field on People: **Company**
|
||||
* Field on Companies: **Employees**
|
||||
|
||||
Now each Person can be linked to a Company, and each Company shows its People.
|
||||
|
||||
## Deleting a Relation
|
||||
|
||||
1. Go to **Settings → Data Model**
|
||||
2. Find the relation field
|
||||
3. Click **⋮ → Deactivate**
|
||||
|
||||
Links are preserved but hidden. Reactivate to restore.
|
||||
|
||||
<Note>
|
||||
**Deleting a relation doesn't delete records.** Only the link between them is removed.
|
||||
</Note>
|
||||
|
||||
## Related
|
||||
|
||||
* [Relation Fields](/l/ar/user-guide/data-model/capabilities/relation-fields) — types and limitations
|
||||
* [How to Import Relations](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) — bulk import linked records
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: تخصيص نموذج البيانات الخاص بك},{
|
||||
description: نظرة عامة على خيارات تخصيص نموذج البيانات.
|
||||
---
|
||||
|
||||
نموذج بيانات Twenty قابل للتخصيص بالكامل. أنشئ كائنات وحقولًا وعلاقات لتناسب نشاطك التجاري.
|
||||
|
||||
## روابط سريعة
|
||||
|
||||
| أريد أن... | دليل |
|
||||
| ------------------- | ---------------------------------------------------------------------------------- |
|
||||
| إنشاء كائن جديد | [كيفية إنشاء كائنات مخصصة](/l/ar/user-guide/data-model/how-tos/create-custom-objects) |
|
||||
| إضافة حقول إلى كائن | [كيفية إنشاء حقول مخصصة](/l/ar/user-guide/data-model/how-tos/create-custom-fields) |
|
||||
| ربط الكائنات معًا | [كيفية إنشاء حقول العلاقات](/l/ar/user-guide/data-model/how-tos/create-relation-fields) |
|
||||
|
||||
## معرفة المزيد
|
||||
|
||||
* [فهم نموذج بياناتك](/l/ar/user-guide/data-model/overview) — مفاهيم أساسية ونصائح للتخطيط
|
||||
* [الكائنات](/l/ar/user-guide/data-model/capabilities/objects) — الكائنات القياسية مقابل الكائنات المخصصة
|
||||
* [الحقول](/l/ar/user-guide/data-model/capabilities/fields) — جميع أنواع الحقول
|
||||
* [حقول العلاقات](/l/ar/user-guide/data-model/capabilities/relation-fields) — ربط الكائنات
|
||||
* [الأسئلة الشائعة حول نموذج البيانات](/l/ar/user-guide/data-model/how-tos/data-model-faq) — أسئلة شائعة
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: الأسئلة المتكررة حول نموذج البيانات
|
||||
description: Frequently asked questions about Twenty's data model.
|
||||
---
|
||||
|
||||
## إدارة الكائنات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I delete a custom object?">
|
||||
Yes, custom objects can be deleted. You can also deactivate them first, which hides the object and its data from the interface while preserving the data.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني حذف الكائنات القياسية (الأشخاص، الشركات، إلخ)؟">
|
||||
No, standard objects cannot be deleted. You can only deactivate them, which hides them from the interface but preserves the data.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How many custom objects can I create?">
|
||||
You can create as many custom objects and fields as you need — the price doesn't change.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I rename a standard object?">
|
||||
You can rename the label of standard objects (People, Companies, Opportunities), but not their API names. The API names are fixed for consistency across all Twenty workspaces.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I change an object's icon?">
|
||||
Yes, you can change the icon for both standard and custom objects in **Settings → Data Model**.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إعادة ترتيب الكائنات في شريط التنقل الأيسر؟">
|
||||
ليس بعد. ترتيب الكائنات في التنقل ثابت حاليًا، ولكن هذه الميزة مخطط لها لإصدارات قادمة.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إخفاء الكائنات من شريط التنقل الأيسر؟">
|
||||
تظهر كل الكائنات النشطة في التنقل. يمكنك إلغاء تفعيل الكائنات التي لا تحتاجها تحت **الإعدادات → نموذج البيانات**.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## قدرات الحقول
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I change a field type after creation?">
|
||||
No, field types cannot be changed after creation. If you need a different type, create a new field with the correct type, migrate your data, then deactivate the old field.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="لماذا أحتاج إلى أسماء مفردة وجمع مختلفة؟">
|
||||
تستخدم واجهة GraphQL كلا النموذجين لعمليات مختلفة:
|
||||
|
||||
* `createPerson` (مفرد) لأعمال سجلات فردية
|
||||
* `createPeople` (جمع) للعمليات الجماعية
|
||||
|
||||
هذا يخلق قيودًا عندما تكون الأشكال المفردة والجمع متشابهة، ولكنه يحسن تجربة المطور.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="لماذا يتم حماية بعض أسماء الحقول؟">
|
||||
بعض أسماء الحقول مثل `Type` أو `Application` محجوزة لاستخدام النظام. اختر أسماء بديلة مثل `Category` أو `Classification` بدلاً من ذلك.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens when I deactivate a field?">
|
||||
* The field is hidden from the interface
|
||||
* Existing data is preserved
|
||||
* You can still access the field via API
|
||||
* Existing relations remain but you can't create new ones
|
||||
* You can reactivate the field later
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I make a field required?">
|
||||
Currently, you cannot make custom fields required. All fields accept empty values. You can use workflows to enforce required fields by sending alerts or blocking actions when fields are empty.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What's the difference between unique and required?">
|
||||
* **Unique**: No two records can have the same value in this field
|
||||
* **Required**: The field must have a value (not currently supported for custom fields)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إنشاء حقول صيغة؟">
|
||||
حقول الصيغ قادمة في **الربع الأول من عام 2026**. في الوقت الحالي، يمكنك استخدام سير العمل لحساب وتحديث قيم الحقول تلقائيًا.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكن أن يكون لدي حقول متداخلة داخل الكائنات الخاصة بي؟">
|
||||
الحقول المتداخلة قادمة في **الربع الأول من عام 2026**. حاليًا، يمكنك استخدام سير العمل لإحضار قيم الحقول من الكائنات ذات الصلة. مثلاً، لعرض صناعة شركة على سجل شخص، قم بإنشاء حقل مخصص للأشخاص واستخدم سير العمل لمزامنة القيمة.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إعادة ترتيب الحقول داخل الكائنات؟">
|
||||
سيكون إعادة ترتيب الحقول متاحًا مع التخطيطات المخصصة في **الربع الرابع من عام 2025**. Currently, fields appear in alphabetical order.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## العلاقات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I have a relation pointing to the same object?">
|
||||
نعم! Self-referencing relations are supported and recommended for use cases like account hierarchies. For example, create a relation from Companies to Companies to track parent/child accounts.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إنشاء علاقات عديدة-إلى-عدة؟">
|
||||
Many-to-many relationships are coming in **H1 2026**. Currently, create an intermediate object with two one-to-many relationships as a workaround.
|
||||
|
||||
For example, to link People and Projects (many-to-many), create a "Project Assignments" object with:
|
||||
|
||||
* A relation to People (many assignments → one person)
|
||||
* A relation to Projects (many assignments → one project)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What are relations to multiple object types (Morph relationships)?">
|
||||
These allow one object to relate to multiple different object types through a single field. For example, Notes can be attached to People AND Companies AND Opportunities simultaneously.
|
||||
|
||||
Each Note links to one Person, one Company, and one Opportunity at the same time.
|
||||
|
||||
Learn more in [Relation Fields](/l/ar/user-guide/data-model/capabilities/relation-fields).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I have multiple relations between the same objects?">
|
||||
Yes, you can create multiple relations between the same two objects. For example, a Company could have both a "Primary Contact" and "Billing Contact" relation to People.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens to relations when I delete a record?">
|
||||
When you delete a record, the relation link is removed from the related records. The related records themselves are not deleted.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I create circular relations?">
|
||||
While technically possible, circular relations (A → B → C → A) should be avoided as they can cause confusion and potential performance issues.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## الوصول والصلاحيات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="أين يمكنني رؤية وتعديل نموذج البيانات الخاص بي؟">
|
||||
Go to **Settings → Data Model** to view and edit all your objects and fields.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="لماذا لا أستطيع رؤية نموذج البيانات تحت الإعدادات؟">
|
||||
اتصل بمسؤول المساحة لديك. يكون الوصول إلى نموذج البيانات عادة محظورًا على المسؤولين فقط.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Data Management
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Is there a limit to how many records I can have?">
|
||||
There's no hard limit on record counts. However, very large datasets may impact performance in some views. Use filters and views to manage large datasets effectively.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I import data into custom objects?">
|
||||
Yes, you can import CSV data into any object, including custom objects. The import process supports field mapping for custom fields. See [How to Prepare Your CSV Files](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I export my data model configuration?">
|
||||
Currently, there's no built-in export for data model configuration. Contact support if you need to migrate your data model between workspaces.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## هل تحتاج إلى المزيد من المساعدة؟
|
||||
|
||||
Check our [Implementation Services](/l/ar/user-guide/getting-started/capabilities/implementation-services) for help with complex data model design.
|
||||
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: نموذج البيانات
|
||||
description: Learn what a data model is and how to design one that fits your business.
|
||||
image: /images/user-guide/fields/custom_data_model.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/fields/custom_data_model.png" alt="نموذج البيانات" />
|
||||
</Frame>
|
||||
|
||||
## What is a Data Model?
|
||||
|
||||
A data model is the structure that defines how information is organized in your CRM. Think of it as the **blueprint** of your customer data — you design it once, then fill it with your actual data.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### كائنات
|
||||
|
||||
**Objects** are the main categories of data in your CRM. Each object represents a type of thing you want to track.
|
||||
|
||||
Twenty comes with standard objects:
|
||||
|
||||
* **People** — individuals (contacts, leads, partners)
|
||||
* **Companies** — organizations
|
||||
* **Opportunities** — deals or sales
|
||||
* **Notes** — attached notes on records
|
||||
* **Tasks** — to-dos linked to records
|
||||
|
||||
You can also create **custom objects** for anything specific to your business (e.g., Projects, Subscriptions, Events).
|
||||
|
||||
### الحقول
|
||||
|
||||
**Fields** are the properties or attributes that describe each object. They store the actual information.
|
||||
|
||||
For example, the **People** object has fields like:
|
||||
|
||||
* الاسم
|
||||
* البريد الإلكتروني
|
||||
* هاتف
|
||||
* المسمى الوظيفي
|
||||
* Company (a relation to the Companies object)
|
||||
|
||||
Fields have different **types**: text, number, date, select, multi-select, relation, and more. You can add custom fields to any object.
|
||||
|
||||
### السجلات
|
||||
|
||||
**Records** are the individual entries within an object — the actual data you create and manage.
|
||||
|
||||
على سبيل المثال:
|
||||
|
||||
* "John Smith" is a **record** in the People object
|
||||
* "Acme Corp" is a **record** in the Companies object
|
||||
|
||||
**An analogy:**
|
||||
|
||||
| Data Model Concept | Real-World Analogy |
|
||||
| ------------------ | ------------------------------------------ |
|
||||
| **Objects** | Sections in a book (the categories) |
|
||||
| **حقول** | Columns in a spreadsheet (the properties) |
|
||||
| **Records** | Rows in a spreadsheet (the actual entries) |
|
||||
|
||||
You design the data model (objects + fields) once, then create many records within that structure.
|
||||
|
||||
## Why Customize Your Data Model?
|
||||
|
||||
كل شركة تعمل بطريقة مختلفة. Customizing your data model means you can shape Twenty around **your** processes instead of forcing yours into a rigid system.
|
||||
|
||||
Twenty offers full flexibility:
|
||||
|
||||
* Create as many custom objects as you need
|
||||
* Add unlimited custom fields
|
||||
* The price doesn't change based on customization
|
||||
|
||||
## Tips to Design Your Data Model
|
||||
|
||||
### 1. Start with Your Core Objects
|
||||
|
||||
Identify the main concepts you work with. Twenty already provides:
|
||||
|
||||
* **People** — your contacts
|
||||
* **Companies** — your accounts
|
||||
* **Opportunities** — your deals
|
||||
|
||||
Think about what else you might need:
|
||||
|
||||
* Stripe would need a `Subscriptions` object
|
||||
* Airbnb would need a `Trips` object
|
||||
* An accelerator would need a `Batches` object
|
||||
|
||||
### ٢. Use Fields for Variations, Not New Objects
|
||||
|
||||
If something is just a characteristic of an existing object, make it a **field**.
|
||||
|
||||
**Use fields for:**
|
||||
|
||||
* Categories and labels (e.g., `Industry` for Companies)
|
||||
* Status values (e.g., `Stage` for Opportunities)
|
||||
* Attributes and properties
|
||||
|
||||
### ٣. Create an Object When It Stands on Its Own
|
||||
|
||||
If the concept has its own lifecycle, properties, or relationships, it deserves an object.
|
||||
|
||||
**Create an object for:**
|
||||
|
||||
* **Projects** — have deadlines, owners, and tasks
|
||||
* **Subscriptions** — connect companies, products, and invoices
|
||||
* **Events** — involve attendees and follow-up actions
|
||||
|
||||
تتجاوز هذه الأشياء مجرد ما يمكن تضمينه في حقل واحد لأنها تحمل بياناتها وعلاقاتها الخاصة.
|
||||
|
||||
### 4. Create an Object When Records Are Open-Ended
|
||||
|
||||
If something can be linked multiple times and you don't know how many, use an object.
|
||||
|
||||
**Bad approach:**
|
||||
Creating fields like `Product 1`, `Product 2`, `Product 3`...
|
||||
|
||||
**Good approach:**
|
||||
Create a `Products` object and relate it to records. This supports one, two, or a hundred products without changing your model.
|
||||
|
||||
### 5. Keep It Simple First
|
||||
|
||||
Start with fields. Move to new objects only when you feel the limits:
|
||||
|
||||
* Too many fields on one object
|
||||
* Repeated records that should be separate
|
||||
* Relationships that don't fit neatly
|
||||
|
||||
## Special Note on People, Companies, and Opportunities
|
||||
|
||||
<Warning>
|
||||
**Email and calendar sync only works with People, Companies, and Opportunities.**
|
||||
|
||||
These are the only objects where you can access synchronized emails and meetings from your mailbox/calendar. We recommend using them as much as possible.
|
||||
</Warning>
|
||||
|
||||
**Best practices:**
|
||||
|
||||
* If you need categories of People, use fields (not new objects)
|
||||
* Example: Use a `Person Type` field with values "Prospect" and "Partner" instead of creating separate objects
|
||||
* Create different **views** to filter: one showing partners, another showing prospects
|
||||
|
||||
**It's okay to have fields that don't apply to every record.** For example, a `Referral Link` field on People that only applies when `Person Type = Partner`. Hide this field from views where it's not relevant.
|
||||
|
||||
## Questions to Guide Your Choice
|
||||
|
||||
اسأل نفسك:
|
||||
|
||||
<Check>Is this just a property of something I already have, or does it need its own properties?</Check>
|
||||
<Check>Will I ever need to track multiple of these per record, without knowing how many?</Check>
|
||||
<Check>Does this concept connect to several different objects, not just one?</Check>
|
||||
<Check>Will it have its own lifecycle (stages, start/end dates)?</Check>
|
||||
|
||||
If the answer is "yes" to one or more, it's probably time for a new object.
|
||||
|
||||
## Accessing Your Data Model
|
||||
|
||||
1. Go to **Settings** in the left sidebar
|
||||
2. Click **Data Model**
|
||||
3. View all your objects (standard and custom)
|
||||
4. Click any object to see and edit its fields
|
||||
|
||||
<Note>
|
||||
**Don't see Data Model in Settings?**
|
||||
|
||||
Access to the data model is usually restricted to administrators. Contact your workspace admin if you need access.
|
||||
</Note>
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
Once you've planned your data model:
|
||||
|
||||
* [How to Create Custom Objects](/l/ar/user-guide/data-model/how-tos/create-custom-objects)
|
||||
* [How to Create Custom Fields](/l/ar/user-guide/data-model/how-tos/create-custom-fields)
|
||||
* [How to Create Relation Fields](/l/ar/user-guide/data-model/how-tos/create-relation-fields)
|
||||
|
||||
## هل تحتاج إلى مساعدة؟
|
||||
|
||||
Our team can help you design and create the data model you need. Discover our [Implementation Services](/l/ar/user-guide/getting-started/capabilities/implementation-services).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user