Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54a9a1087b | ||
|
|
d74d74da4c | ||
|
|
2ecc1ba897 | ||
|
|
f377727ff5 | ||
|
|
2835935f11 | ||
|
|
596b7cc62d | ||
|
|
b56f45e871 | ||
|
|
9b7953e7a3 | ||
|
|
6b97e3b428 | ||
|
|
3ed67b825e | ||
|
|
0e7e471312 | ||
|
|
b1bd749abd | ||
|
|
e35b4b86cb | ||
|
|
ccf2eae684 | ||
|
|
30c13fc947 | ||
|
|
41329a0ea9 | ||
|
|
799b08e1f2 | ||
|
|
e40130f575 | ||
|
|
9cf88df67b | ||
|
|
351e2030ff | ||
|
|
b6635ba272 | ||
|
|
579c59bd11 | ||
|
|
e599d6c4d0 | ||
|
|
02a181a49f | ||
|
|
801878cb2b | ||
|
|
f9c400cd78 | ||
|
|
a004662221 | ||
|
|
196de28a0c | ||
|
|
c388b29f7c | ||
|
|
472fe4eec3 | ||
|
|
29c0c952e7 | ||
|
|
b64951634e | ||
|
|
39470b74a4 | ||
|
|
527c00d326 | ||
|
|
038f9cc44f | ||
|
|
dc982abb78 | ||
|
|
70582d063e | ||
|
|
042b15a7d1 | ||
|
|
1731c3427e | ||
|
|
d0bc8b9ffe | ||
|
|
1f1ccb281e | ||
|
|
b15c042e77 | ||
|
|
83d9c580a0 | ||
|
|
4ce6119be7 | ||
|
|
5a5e9eeb9e | ||
|
|
374303455a | ||
|
|
3c26ebace2 | ||
|
|
28e98086b0 | ||
|
|
e0d1edb940 | ||
|
|
1a38152451 | ||
|
|
f381516d30 | ||
|
|
59c7295033 | ||
|
|
905898d109 | ||
|
|
3958664e87 | ||
|
|
3c0314d14a | ||
|
|
e61b92132c | ||
|
|
7cf0dc522a | ||
|
|
2505c631de | ||
|
|
859e718cf2 | ||
|
|
0b3f243f24 | ||
|
|
4ab95235ce | ||
|
|
dc93cf4c59 | ||
|
|
42c6a04be6 | ||
|
|
58619e3b02 | ||
|
|
66ec2f124f | ||
|
|
7c713577f1 | ||
|
|
72e89d5c6b | ||
|
|
92a080b704 | ||
|
|
5cef07af45 | ||
|
|
b001991047 | ||
|
|
2c596e7b1e | ||
|
|
6070dbf16a | ||
|
|
fae6d0e262 | ||
|
|
d51c988a9e | ||
|
|
a91bac9dd9 | ||
|
|
92eff62523 | ||
|
|
c737028dd6 | ||
|
|
a429bc922d | ||
|
|
1b9dc414c7 | ||
|
|
5a617e4718 | ||
|
|
b7fbe1c49e | ||
|
|
5bcbe43596 | ||
|
|
8a27c4987d | ||
|
|
5fc4e810f7 | ||
|
|
fe0d84c97e | ||
|
|
76cbe59929 | ||
|
|
dcdf9e4d9c | ||
|
|
6a709d9c50 | ||
|
|
9620961f16 | ||
|
|
964f1c6227 | ||
|
|
9750bcba81 | ||
|
|
1917c1c9b9 | ||
|
|
8908e0785f | ||
|
|
0fa8098c50 | ||
|
|
8413c6f3dd | ||
|
|
2cff75d772 | ||
|
|
3f28fde03a | ||
|
|
161e8670d0 | ||
|
|
abdccf2940 | ||
|
|
f218d652df | ||
|
|
466d272f9c | ||
|
|
975401e18f | ||
|
|
55b4ac4314 | ||
|
|
d8f0115fef | ||
|
|
f894f6b1c4 | ||
|
|
6e53f5ab92 | ||
|
|
8379e19587 | ||
|
|
79e0207efa | ||
|
|
93aef30046 | ||
|
|
a9e07d4f97 | ||
|
|
9e4247c934 | ||
|
|
2c24180b44 | ||
|
|
f70b171843 | ||
|
|
6b14cfaa89 | ||
|
|
0412ce2cda | ||
|
|
ef8d7c18c1 | ||
|
|
245bd510ae | ||
|
|
6f18eaa3f1 | ||
|
|
ec87b29286 | ||
|
|
3ecfb24939 | ||
|
|
70704264ac | ||
|
|
d95ff4e252 | ||
|
|
20b5a8f5e7 |
@@ -13,6 +13,7 @@ This directory contains Twenty's development guidelines and best practices in th
|
||||
- **architecture.mdc** - Project overview, technology stack, and infrastructure setup (Always Applied)
|
||||
- **nx-rules.mdc** - Nx workspace guidelines and best practices (Auto-attached to Nx files)
|
||||
- **server-migrations.mdc** - Backend migration and TypeORM guidelines for `twenty-server` (Auto-attached to server entities and migration files)
|
||||
- **creating-syncable-entity.mdc** - Comprehensive guide for creating new syncable entities (with universalIdentifier and applicationId) in the workspace migration system (Agent-requested for metadata-modules and workspace-migration files)
|
||||
|
||||
### Code Quality
|
||||
- **typescript-guidelines.mdc** - TypeScript best practices and conventions (Auto-attached to .ts/.tsx files)
|
||||
@@ -39,6 +40,7 @@ You can manually reference any rule using the `@ruleName` syntax:
|
||||
- `@nx-rules` - Include Nx-specific guidance
|
||||
- `@react-general-guidelines` - Load React best practices
|
||||
- `@testing-guidelines` - Get testing recommendations
|
||||
- `@creating-syncable-entity` - Guide for creating new syncable entities
|
||||
|
||||
### Rule Types Used
|
||||
- **Always Applied** - Loaded in every context (architecture.mdc, README.mdc)
|
||||
@@ -53,7 +55,7 @@ You can manually reference any rule using the `@ruleName` syntax:
|
||||
# Testing
|
||||
npx nx test twenty-front # Run unit tests
|
||||
npx nx storybook:build twenty-front # Build Storybook
|
||||
npx nx storybook:serve-and-test:static # Run Storybook tests
|
||||
npx nx storybook:test # Run Storybook tests
|
||||
|
||||
# Development
|
||||
npx nx lint:diff-with-main twenty-front # Lint files changed vs main (fastest)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#
|
||||
|
||||
"preserve_hierarchy": true
|
||||
"base_path": ".."
|
||||
|
||||
files: [
|
||||
{
|
||||
@@ -6,6 +6,7 @@
|
||||
"project_id": 2
|
||||
"preserve_hierarchy": true
|
||||
"base_url": "https://twenty.api.crowdin.com"
|
||||
"base_path": ".."
|
||||
|
||||
files: [
|
||||
{
|
||||
@@ -56,8 +56,6 @@ jobs:
|
||||
run: df -h
|
||||
- name: Front / Write .env
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Front / Clean storybook-static
|
||||
run: rm -rf packages/twenty-front/storybook-static
|
||||
- name: Front / Build storybook
|
||||
run: npx nx storybook:build twenty-front
|
||||
- name: Save storybook build cache
|
||||
@@ -83,18 +81,27 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Build dependencies
|
||||
run: |
|
||||
npx nx build twenty-shared
|
||||
npx nx build twenty-ui
|
||||
- name: Install Playwright
|
||||
run: cd packages/twenty-front && npx playwright install
|
||||
- name: Restore storybook build cache
|
||||
uses: ./.github/actions/restore-cache
|
||||
with:
|
||||
key: ${{ env.STORYBOOK_BUILD_CACHE_KEY_FOR_RESTORE_ACTION }}
|
||||
run: |
|
||||
cd packages/twenty-front
|
||||
npx playwright install
|
||||
- name: Front / Write .env
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Run storybook tests
|
||||
run: npx nx storybook:serve-and-test:static twenty-front --configuration=${{ matrix.storybook_scope }} --shard=${{ matrix.shard }}/${{ env.SHARD_COUNTER }} --checkCoverage=false
|
||||
run: npx nx storybook:test twenty-front --configuration=${{ matrix.storybook_scope }} --shard=${{ matrix.shard }}/${{ env.SHARD_COUNTER }}
|
||||
- name: Rename coverage file
|
||||
run: mv packages/twenty-front/coverage/storybook/coverage-storybook.json packages/twenty-front/coverage/storybook/coverage-shard-${{matrix.shard}}.json
|
||||
run: |
|
||||
if [ -f "packages/twenty-front/coverage/storybook/coverage-final.json" ]; then
|
||||
mv packages/twenty-front/coverage/storybook/coverage-final.json packages/twenty-front/coverage/storybook/coverage-shard-${{matrix.shard}}.json
|
||||
else
|
||||
echo "Error: coverage-final.json not found"
|
||||
ls -la packages/twenty-front/coverage/storybook/ || echo "Coverage directory does not exist"
|
||||
exit 1
|
||||
fi
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -24,7 +24,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/twenty-docs/**'
|
||||
- 'crowdin-docs.yml'
|
||||
- '.github/crowdin-docs.yml'
|
||||
- '.github/workflows/docs-i18n-pull.yaml'
|
||||
|
||||
concurrency:
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
for lang in $LANGUAGES; do
|
||||
echo "=== Pulling translations for $lang ==="
|
||||
crowdin download \
|
||||
--config crowdin-docs.yml \
|
||||
--config .github/crowdin-docs.yml \
|
||||
--token "$CROWDIN_PERSONAL_TOKEN" \
|
||||
--base-url "https://twenty.api.crowdin.com" \
|
||||
--language "$lang" \
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
- 'packages/twenty-docs/**/*.mdx'
|
||||
- '!packages/twenty-docs/l/**'
|
||||
- 'packages/twenty-docs/navigation/navigation.template.json'
|
||||
- 'crowdin-docs.yml'
|
||||
- '.github/crowdin-docs.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
download_translations: false
|
||||
localization_branch_name: i18n-docs
|
||||
base_url: 'https://twenty.api.crowdin.com'
|
||||
config: 'crowdin-docs.yml'
|
||||
config: '.github/crowdin-docs.yml'
|
||||
env:
|
||||
# Docs translations project
|
||||
CROWDIN_PROJECT_ID: '2'
|
||||
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
create_pull_request: false
|
||||
skip_ref_checkout: true
|
||||
dryrun_action: false
|
||||
config: 'crowdin-app.yml'
|
||||
config: '.github/crowdin-app.yml'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
# App translations project
|
||||
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
download_translations: false
|
||||
localization_branch_name: i18n
|
||||
base_url: 'https://twenty.api.crowdin.com'
|
||||
config: 'crowdin-app.yml'
|
||||
config: '.github/crowdin-app.yml'
|
||||
env:
|
||||
# App translations project
|
||||
CROWDIN_PROJECT_ID: '1'
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
"mcpServers": {
|
||||
"postgres": {
|
||||
"type": "stdio",
|
||||
"command": "uv",
|
||||
"args": ["run", "postgres-mcp", "--access-mode=unrestricted"],
|
||||
"env": {
|
||||
"DATABASE_URI": "${PG_DATABASE_URL}"
|
||||
}
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-postgres", "${PG_DATABASE_URL}"],
|
||||
"env": {}
|
||||
},
|
||||
"playwright": {
|
||||
"type": "stdio",
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Add files here to ignore them from prettier formatting
|
||||
/dist
|
||||
/coverage
|
||||
/.nx/cache
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
@@ -73,7 +73,8 @@
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"env": {
|
||||
"NODE_ENV": "test"
|
||||
"NODE_ENV": "test",
|
||||
"NODE_OPTIONS": "--max-old-space-size=12288 --import tsx/esm"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ npx nx run twenty-server:test:integration:with-db-reset # Integration tests wit
|
||||
|
||||
# Storybook
|
||||
npx nx storybook:build twenty-front # Build Storybook
|
||||
npx nx storybook:serve-and-test:static twenty-front # Run Storybook tests
|
||||
npx nx storybook:test twenty-front # Run Storybook tests
|
||||
|
||||
|
||||
When testing the UI end to end, click on "Continue with Email" and use the prefilled credentials.
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
DOCKER_NETWORK=twenty_network
|
||||
|
||||
ensure-docker-network:
|
||||
docker network inspect $(DOCKER_NETWORK) >/dev/null 2>&1 || docker network create $(DOCKER_NETWORK)
|
||||
|
||||
postgres-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) \
|
||||
--name twenty_pg \
|
||||
-e POSTGRES_USER=postgres \
|
||||
-e POSTGRES_PASSWORD=postgres \
|
||||
-e ALLOW_NOSSL=true \
|
||||
-v twenty_db_data:/var/lib/postgresql/data \
|
||||
-p 5432:5432 \
|
||||
postgres:16
|
||||
@echo "Waiting for PostgreSQL to be ready..."
|
||||
@until docker exec twenty_pg psql -U postgres -d postgres \
|
||||
-c 'SELECT pg_is_in_recovery();' 2>/dev/null | grep -q 'f'; do \
|
||||
sleep 1; \
|
||||
done
|
||||
docker exec twenty_pg psql -U postgres -d postgres \
|
||||
-c "CREATE DATABASE \"default\" WITH OWNER postgres;" \
|
||||
-c "CREATE DATABASE \"test\" WITH OWNER postgres;"
|
||||
|
||||
redis-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
|
||||
|
||||
clickhouse-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) --name twenty_clickhouse -p 8123:8123 -p 9000:9000 -e CLICKHOUSE_PASSWORD=devPassword clickhouse/clickhouse-server:latest \
|
||||
|
||||
grafana-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) \
|
||||
--name twenty_grafana \
|
||||
-p 4000:3000 \
|
||||
-e GF_SECURITY_ADMIN_USER=admin \
|
||||
-e GF_SECURITY_ADMIN_PASSWORD=admin \
|
||||
-e GF_INSTALL_PLUGINS=grafana-clickhouse-datasource \
|
||||
-v $(PWD)/packages/twenty-docker/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources \
|
||||
grafana/grafana-oss:latest
|
||||
|
||||
opentelemetry-collector-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) \
|
||||
--name twenty_otlp_collector \
|
||||
-p 4317:4317 \
|
||||
-p 4318:4318 \
|
||||
-p 13133:13133 \
|
||||
-v $(PWD)/packages/twenty-docker/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
|
||||
otel/opentelemetry-collector-contrib:latest \
|
||||
--config /etc/otel-collector-config.yaml
|
||||
@@ -11,6 +11,8 @@ import unicornPlugin from 'eslint-plugin-unicorn';
|
||||
import unusedImportsPlugin from 'eslint-plugin-unused-imports';
|
||||
import jsoncParser from 'jsonc-eslint-parser';
|
||||
|
||||
const twentyRules = await nxPlugin.loadWorkspaceRules('packages/twenty-eslint-rules');
|
||||
|
||||
export default [
|
||||
// Base JavaScript configuration
|
||||
js.configs.recommended,
|
||||
@@ -195,6 +197,7 @@ export default [
|
||||
plugins: {
|
||||
...mdxPlugin.flat.plugins,
|
||||
'@nx': nxPlugin,
|
||||
'twenty': { rules: twentyRules },
|
||||
},
|
||||
},
|
||||
mdxPlugin.flatCodeBlocks,
|
||||
@@ -205,9 +208,9 @@ export default [
|
||||
'unused-imports/no-unused-imports': 'off',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
// Enforce JSX tags on separate lines to prevent Crowdin translation issues
|
||||
'@nx/workspace-mdx-component-newlines': 'error',
|
||||
'twenty/mdx-component-newlines': 'error',
|
||||
// Disallow angle bracket placeholders to prevent Crowdin translation errors
|
||||
'@nx/workspace-no-angle-bracket-placeholders': 'error',
|
||||
'twenty/no-angle-bracket-placeholders': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { getJestProjects } from '@nx/jest';
|
||||
|
||||
export default {
|
||||
projects: getJestProjects(),
|
||||
};
|
||||
@@ -1,3 +1,9 @@
|
||||
const nxPreset = require('@nx/jest/preset').default;
|
||||
|
||||
module.exports = { ...nxPreset };
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
// Override the new testEnvironmentOptions added in @nx/jest 22.3.3
|
||||
// which breaks Lingui's module resolution
|
||||
testEnvironmentOptions: {},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
command -v node >/dev/null 2>&1 || { echo >&2 "Nx requires NodeJS to be available. To install NodeJS and NPM, see: https://nodejs.org/en/download/ ."; exit 1; }
|
||||
command -v npm >/dev/null 2>&1 || { echo >&2 "Nx requires npm to be available. To install NodeJS and NPM, see: https://nodejs.org/en/download/ ."; exit 1; }
|
||||
path_to_root=$(dirname $BASH_SOURCE)
|
||||
node $path_to_root/.nx/nxw.js $@
|
||||
@@ -4,9 +4,7 @@
|
||||
"libsDir": "packages"
|
||||
},
|
||||
"namedInputs": {
|
||||
"default": [
|
||||
"{projectRoot}/**/*"
|
||||
],
|
||||
"default": ["{projectRoot}/**/*"],
|
||||
"excludeStories": [
|
||||
"default",
|
||||
"!{projectRoot}/.storybook/*",
|
||||
@@ -34,26 +32,17 @@
|
||||
"targetDefaults": {
|
||||
"build": {
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"^production",
|
||||
"production"
|
||||
],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"inputs": ["^production", "production"],
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"start": {
|
||||
"cache": false,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"cache": true,
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
],
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"eslintConfig": "{projectRoot}/eslint.config.mjs",
|
||||
"cache": true,
|
||||
@@ -67,9 +56,7 @@
|
||||
"fix": true
|
||||
}
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"lint:diff-with-main": {
|
||||
"executor": "nx:run-commands",
|
||||
@@ -103,46 +90,36 @@
|
||||
"write": true
|
||||
}
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"typecheck": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "tsc -b tsconfig.json --incremental"
|
||||
"command": "tsgo -p tsconfig.json"
|
||||
},
|
||||
"configurations": {
|
||||
"watch": {
|
||||
"watch": true
|
||||
"command": "tsgo -p tsconfig.json --watch"
|
||||
}
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": [
|
||||
"^default",
|
||||
"excludeStories",
|
||||
"{workspaceRoot}/jest.preset.js"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/coverage"
|
||||
],
|
||||
"outputs": ["{projectRoot}/coverage"],
|
||||
"options": {
|
||||
"jestConfig": "{projectRoot}/jest.config.mjs",
|
||||
"coverage": true,
|
||||
"coverageReporters": [
|
||||
"text-summary"
|
||||
],
|
||||
"coverageReporters": ["text-summary"],
|
||||
"cacheDirectory": "../../.cache/jest/{projectRoot}"
|
||||
},
|
||||
"configurations": {
|
||||
@@ -151,10 +128,7 @@
|
||||
"maxWorkers": 3
|
||||
},
|
||||
"coverage": {
|
||||
"coverageReporters": [
|
||||
"lcov",
|
||||
"text"
|
||||
]
|
||||
"coverageReporters": ["lcov", "text"]
|
||||
},
|
||||
"watch": {
|
||||
"watch": true
|
||||
@@ -163,36 +137,25 @@
|
||||
},
|
||||
"test:e2e": {
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"storybook:build": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"^default",
|
||||
"excludeTests"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/{options.output-dir}"
|
||||
],
|
||||
"inputs": ["^default", "excludeTests"],
|
||||
"outputs": ["{projectRoot}/{options.output-dir}"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "NODE_OPTIONS='--max-old-space-size=10240' VITE_DISABLE_TYPESCRIPT_CHECKER=true storybook build --test",
|
||||
"output-dir": "storybook-static",
|
||||
"config-dir": ".storybook"
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"storybook:serve:dev": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "storybook dev",
|
||||
@@ -201,9 +164,7 @@
|
||||
},
|
||||
"storybook:serve:static": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"storybook:build"
|
||||
],
|
||||
"dependsOn": ["storybook:build"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx http-server {args.staticDir} -a={args.host} --port={args.port} --silent={args.silent}",
|
||||
@@ -216,38 +177,21 @@
|
||||
"storybook:test": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"^default",
|
||||
"excludeTests"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/coverage/storybook"
|
||||
],
|
||||
"inputs": ["^default", "excludeTests"],
|
||||
"outputs": ["{projectRoot}/coverage/storybook"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
"test-storybook --url http://localhost:{args.port} --maxWorkers=3 --coverage --coverageDirectory={args.coverageDir} --shard={args.shard}",
|
||||
"nx storybook:coverage {projectName} --coverageDir={args.coverageDir} --checkCoverage={args.checkCoverage}"
|
||||
],
|
||||
"shard": "1/1",
|
||||
"parallel": false,
|
||||
"coverageDir": "coverage/storybook",
|
||||
"port": 6006,
|
||||
"checkCoverage": true
|
||||
"command": "vitest run --coverage --shard={args.shard}",
|
||||
"shard": "1/1"
|
||||
}
|
||||
},
|
||||
"storybook:test:no-coverage": {
|
||||
"executor": "nx:run-commands",
|
||||
"inputs": [
|
||||
"^default",
|
||||
"excludeTests"
|
||||
],
|
||||
"inputs": ["^default", "excludeTests"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
"test-storybook --url http://localhost:{args.port} --maxWorkers=2"
|
||||
],
|
||||
"port": 6006
|
||||
"command": "vitest run --shard={args.shard}",
|
||||
"shard": "1/1"
|
||||
}
|
||||
},
|
||||
"storybook:coverage": {
|
||||
@@ -274,17 +218,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"storybook:serve-and-test:static": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --shard={args.shard} --checkCoverage={args.checkCoverage} --port={args.port} --configuration={args.scope}'"
|
||||
],
|
||||
"shard": "1/1",
|
||||
"checkCoverage": true,
|
||||
"port": 6006
|
||||
}
|
||||
},
|
||||
"chromatic": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
@@ -326,29 +259,21 @@
|
||||
"inputs": [
|
||||
"default",
|
||||
"{workspaceRoot}/eslint.config.mjs",
|
||||
"{workspaceRoot}/tools/eslint-rules/**/*"
|
||||
]
|
||||
},
|
||||
"@nx/vite:test": {
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"default",
|
||||
"^default"
|
||||
"{workspaceRoot}/packages/twenty-eslint-rules/**/*"
|
||||
]
|
||||
},
|
||||
"@nx/vite:build": {
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"inputs": [
|
||||
"default",
|
||||
"^default"
|
||||
]
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["default", "^default"]
|
||||
},
|
||||
"@nx/vitest:test": {
|
||||
"cache": true,
|
||||
"inputs": ["default", "^default"]
|
||||
}
|
||||
},
|
||||
"installation": {
|
||||
"version": "22.0.3"
|
||||
"version": "22.3.3"
|
||||
},
|
||||
"generators": {
|
||||
"@nx/react": {
|
||||
@@ -376,9 +301,7 @@
|
||||
"tasksRunnerOptions": {
|
||||
"default": {
|
||||
"options": {
|
||||
"cacheableOperations": [
|
||||
"storybook:build"
|
||||
]
|
||||
"cacheableOperations": ["storybook:build"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -72,19 +72,20 @@
|
||||
"@graphql-codegen/typescript": "^3.0.4",
|
||||
"@graphql-codegen/typescript-operations": "^3.0.4",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.3.7",
|
||||
"@nx/eslint": "22.0.3",
|
||||
"@nx/eslint-plugin": "22.0.3",
|
||||
"@nx/jest": "22.0.3",
|
||||
"@nx/js": "22.0.3",
|
||||
"@nx/react": "22.0.3",
|
||||
"@nx/storybook": "22.0.3",
|
||||
"@nx/vite": "22.0.3",
|
||||
"@nx/web": "22.0.3",
|
||||
"@nx/eslint": "22.3.3",
|
||||
"@nx/eslint-plugin": "22.3.3",
|
||||
"@nx/jest": "22.3.3",
|
||||
"@nx/js": "22.3.3",
|
||||
"@nx/react": "22.3.3",
|
||||
"@nx/storybook": "22.3.3",
|
||||
"@nx/vite": "22.3.3",
|
||||
"@nx/web": "22.3.3",
|
||||
"@sentry/types": "^8",
|
||||
"@storybook-community/storybook-addon-cookie": "^5.0.0",
|
||||
"@storybook/addon-coverage": "^3.0.0",
|
||||
"@storybook/addon-docs": "^10.1.11",
|
||||
"@storybook/addon-links": "^10.1.11",
|
||||
"@storybook/addon-vitest": "^10.1.11",
|
||||
"@storybook/icons": "^2.0.1",
|
||||
"@storybook/react-vite": "^10.1.11",
|
||||
"@storybook/test-runner": "^0.24.2",
|
||||
@@ -134,7 +135,11 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.39.0",
|
||||
"@typescript-eslint/parser": "^8.39.0",
|
||||
"@typescript-eslint/utils": "^8.39.0",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260116.1",
|
||||
"@vitejs/plugin-react-swc": "3.11.0",
|
||||
"@vitest/browser-playwright": "^4.0.17",
|
||||
"@vitest/coverage-istanbul": "^4.0.17",
|
||||
"@vitest/coverage-v8": "^4.0.17",
|
||||
"@yarnpkg/types": "^4.0.0",
|
||||
"chromatic": "^6.18.0",
|
||||
"concurrently": "^8.2.2",
|
||||
@@ -163,9 +168,9 @@
|
||||
"jest-environment-node": "^29.4.1",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"jsdom": "~22.1.0",
|
||||
"msw": "^2.0.11",
|
||||
"msw-storybook-addon": "^2.0.5",
|
||||
"nx": "22.0.3",
|
||||
"msw": "^2.12.7",
|
||||
"msw-storybook-addon": "^2.0.6",
|
||||
"nx": "22.3.3",
|
||||
"prettier": "^3.1.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^5.0.5",
|
||||
@@ -179,7 +184,8 @@
|
||||
"ts-node": "10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsx": "^4.17.0",
|
||||
"vite": "^7.0.0"
|
||||
"vite": "^7.0.0",
|
||||
"vitest": "^4.0.17"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
@@ -195,7 +201,9 @@
|
||||
"typescript": "5.9.2",
|
||||
"graphql-redis-subscriptions/ioredis": "^5.6.0",
|
||||
"prosemirror-view": "1.40.0",
|
||||
"prosemirror-transform": "1.10.4"
|
||||
"prosemirror-transform": "1.10.4",
|
||||
"@lingui/core": "5.1.2",
|
||||
"@types/qs": "6.9.16"
|
||||
},
|
||||
"version": "0.2.1",
|
||||
"nx": {},
|
||||
@@ -220,7 +228,12 @@
|
||||
"packages/twenty-apps",
|
||||
"packages/twenty-cli",
|
||||
"packages/create-twenty-app",
|
||||
"tools/eslint-rules"
|
||||
"packages/twenty-eslint-rules"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,29 +28,29 @@ Create Twenty App is the official scaffolding CLI for building apps on top of [T
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# Get Help
|
||||
yarn run help
|
||||
|
||||
# Authenticate using your API key (you'll be prompted)
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
|
||||
# Add a new entity to your application (guided)
|
||||
yarn create-entity
|
||||
yarn entity:add
|
||||
|
||||
# Generate a typed Twenty client and workspace entity types
|
||||
yarn generate
|
||||
yarn app:generate
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn dev
|
||||
yarn app:dev
|
||||
|
||||
# Or run a one‑time sync
|
||||
yarn sync
|
||||
yarn app:sync
|
||||
|
||||
# Watch your application's functions logs
|
||||
yarn logs
|
||||
yarn function:logs
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
yarn uninstall
|
||||
|
||||
# Display commands' help
|
||||
yarn help
|
||||
yarn app:uninstall
|
||||
```
|
||||
|
||||
## What gets scaffolded
|
||||
@@ -60,9 +60,9 @@ yarn help
|
||||
- Example placeholders to help you add entities, actions, and sync logic
|
||||
|
||||
## Next steps
|
||||
- Explore the generated project and add your first entity with `yarn create-entity`.
|
||||
- Keep your types up‑to‑date using `yarn generate`.
|
||||
- Use `yarn dev` while you iterate to see changes instantly in your workspace.
|
||||
- Explore the generated project and add your first entity with `yarn entity:add`.
|
||||
- Keep your types up‑to‑date using `yarn app:generate`.
|
||||
- Use `yarn app:dev` while you iterate to see changes instantly in your workspace.
|
||||
|
||||
|
||||
## Publish your application
|
||||
@@ -90,8 +90,8 @@ git push
|
||||
Our team reviews contributions for quality, security, and reusability before merging.
|
||||
|
||||
## Troubleshooting
|
||||
- Auth prompts not appearing: run `yarn auth` again and verify the API key permissions.
|
||||
- Types not generated: ensure `yarn generate` runs without errors, then re‑start `yarn dev`.
|
||||
- Auth prompts not appearing: run `yarn auth:login` again and verify the API key permissions.
|
||||
- Types not generated: ensure `yarn app:generate` runs without errors, then re‑start `yarn app:dev`.
|
||||
|
||||
## Contributing
|
||||
- See our [GitHub](https://github.com/twentyhq/twenty)
|
||||
|
||||
@@ -5,17 +5,41 @@ This is a [Twenty](https://twenty.com) application project bootstrapped with [`c
|
||||
First, authenticate to your workspace:
|
||||
|
||||
```bash
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
```
|
||||
|
||||
Then, install this app to your workspace:
|
||||
Then, start development mode to sync your app and watch for changes:
|
||||
|
||||
```bash
|
||||
yarn sync
|
||||
yarn app:dev
|
||||
```
|
||||
|
||||
Or run a one-time sync:
|
||||
|
||||
```bash
|
||||
yarn app:sync
|
||||
```
|
||||
|
||||
Open your Twenty instance and go to `/settings/applications` section to see the result.
|
||||
|
||||
## Available Commands
|
||||
|
||||
```bash
|
||||
# Authentication
|
||||
yarn auth:login # Authenticate with Twenty
|
||||
yarn auth:logout # Remove credentials
|
||||
yarn auth:status # Check auth status
|
||||
|
||||
# Application
|
||||
yarn app:dev # Start dev mode (sync + watch)
|
||||
yarn app:sync # One-time sync
|
||||
yarn entity:add # Add a new entity (function, object, role)
|
||||
yarn app:generate # Generate typed Twenty client
|
||||
yarn function:logs # Stream function logs
|
||||
yarn function:execute # Execute a function with JSON payload
|
||||
yarn app:uninstall # Uninstall app from workspace
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Twenty applications, take a look at the following resources:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": ".",
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { copyBaseApplicationProject } from '@/utils/app-template';
|
||||
import { convertToLabel } from '@/utils/convert-to-label';
|
||||
import { install } from '@/utils/install';
|
||||
import { tryGitInit } from '@/utils/try-git-init';
|
||||
import chalk from 'chalk';
|
||||
import * as fs from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
import * as path from 'path';
|
||||
import { copyBaseApplicationProject } from '@/utils/app-template';
|
||||
import kebabCase from 'lodash.kebabcase';
|
||||
import { convertToLabel } from '@/utils/convert-to-label';
|
||||
import { tryGitInit } from '@/utils/try-git-init';
|
||||
import { install } from '@/utils/install';
|
||||
import * as path from 'path';
|
||||
|
||||
const CURRENT_EXECUTION_DIRECTORY = process.env.INIT_CWD || process.cwd();
|
||||
|
||||
@@ -119,10 +119,13 @@ export class CreateAppCommand {
|
||||
}
|
||||
|
||||
private logSuccess(appDirectory: string): void {
|
||||
const dirName = appDirectory.split('/').reverse()[0] ?? '';
|
||||
|
||||
console.log(chalk.green('✅ Application created!'));
|
||||
console.log('');
|
||||
console.log(chalk.blue('Next steps:'));
|
||||
console.log(`cd ${appDirectory.split('/').reverse()[0] ?? ''}`);
|
||||
console.log('yarn auth');
|
||||
console.log(chalk.gray(` cd ${dirName}`));
|
||||
console.log(chalk.gray(' yarn auth:login # Authenticate with Twenty'));
|
||||
console.log(chalk.gray(' yarn app:dev # Start dev mode'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ describe('copyBaseApplicationProject', () => {
|
||||
expect(packageJson.name).toBe('my-test-app');
|
||||
expect(packageJson.version).toBe('0.1.0');
|
||||
expect(packageJson.dependencies['twenty-sdk']).toBe('0.3.1');
|
||||
expect(packageJson.scripts.sync).toBe('twenty app sync');
|
||||
expect(packageJson.scripts.dev).toBe('twenty app dev');
|
||||
expect(packageJson.scripts['app:sync']).toBe('twenty app:sync');
|
||||
expect(packageJson.scripts['app:dev']).toBe('twenty app:dev');
|
||||
});
|
||||
|
||||
it('should create .gitignore file', async () => {
|
||||
|
||||
@@ -32,6 +32,10 @@ export const copyBaseApplicationProject = async ({
|
||||
appDirectory: appFolderPath,
|
||||
});
|
||||
|
||||
await createDefaultFrontComponent({
|
||||
appDirectory: appFolderPath,
|
||||
});
|
||||
|
||||
await createApplicationConfig({
|
||||
displayName: appDisplayName,
|
||||
description: appDescription,
|
||||
@@ -115,6 +119,38 @@ export default defineRole({
|
||||
await fs.writeFile(join(appDirectory, 'default-function.role.ts'), content);
|
||||
};
|
||||
|
||||
const createDefaultFrontComponent = async ({
|
||||
appDirectory,
|
||||
}: {
|
||||
appDirectory: string;
|
||||
}) => {
|
||||
const universalIdentifier = v4();
|
||||
|
||||
const content = `import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export const HelloWorld = () => {
|
||||
return (
|
||||
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
|
||||
<h1>Hello, World!</h1>
|
||||
<p>This is your first front component.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineFrontComponent({
|
||||
universalIdentifier: '${universalIdentifier}',
|
||||
name: 'hello-world',
|
||||
description: 'A sample front component',
|
||||
component: HelloWorld,
|
||||
});
|
||||
`;
|
||||
|
||||
await fs.writeFile(
|
||||
join(appDirectory, 'hello-world.front-component.tsx'),
|
||||
content,
|
||||
);
|
||||
};
|
||||
|
||||
const createApplicationConfig = async ({
|
||||
displayName,
|
||||
description,
|
||||
@@ -156,16 +192,21 @@ const createPackageJson = async ({
|
||||
},
|
||||
packageManager: 'yarn@4.9.2',
|
||||
scripts: {
|
||||
'create-entity': 'twenty app add',
|
||||
dev: 'twenty app dev',
|
||||
generate: 'twenty app generate',
|
||||
sync: 'twenty app sync',
|
||||
logs: 'twenty app logs',
|
||||
uninstall: 'twenty app uninstall',
|
||||
'auth:login': 'twenty auth:login',
|
||||
'auth:logout': 'twenty auth:logout',
|
||||
'auth:status': 'twenty auth:status',
|
||||
'auth:switch': 'twenty auth:switch',
|
||||
'auth:list': 'twenty auth:list',
|
||||
'app:dev': 'twenty app:dev',
|
||||
'app:sync': 'twenty app:sync',
|
||||
'entity:add': 'twenty entity:add',
|
||||
'app:generate': 'twenty app:generate',
|
||||
'function:logs': 'twenty function:logs',
|
||||
'function:execute': 'twenty function:execute',
|
||||
'app:uninstall': 'twenty app:uninstall',
|
||||
help: 'twenty help',
|
||||
auth: 'twenty auth login',
|
||||
lint: 'eslint',
|
||||
'lint-fix': 'eslint --fix',
|
||||
'lint:fix': 'eslint --fix',
|
||||
},
|
||||
dependencies: {
|
||||
'twenty-sdk': '0.3.1',
|
||||
@@ -173,6 +214,8 @@ const createPackageJson = async ({
|
||||
devDependencies: {
|
||||
typescript: '^5.9.3',
|
||||
'@types/node': '^24.7.2',
|
||||
'@types/react': '^19.0.2',
|
||||
react: '^19.0.2',
|
||||
eslint: '^9.32.0',
|
||||
'typescript-eslint': '^8.50.0',
|
||||
},
|
||||
|
||||
@@ -75,6 +75,7 @@ export default defineConfig(() => {
|
||||
'path',
|
||||
'fs',
|
||||
'child_process',
|
||||
'util',
|
||||
],
|
||||
output: [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import typescriptParser from '@typescript-eslint/parser';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import reactConfig from '../../eslint.config.react.mjs';
|
||||
import reactConfig from '../../../../twenty-eslint-rules/eslint.config.react.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@emotion/react",
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
# Makefile for building Twenty CRM docker images.
|
||||
# Makefile for building and running Twenty CRM docker containers.
|
||||
# Set the tag and/or target build platform using make command-line variables assignments.
|
||||
#
|
||||
# Optional make variables:
|
||||
# PLATFORM - defaults to 'linux/amd64'
|
||||
# TAG - defaults to 'latest'
|
||||
#
|
||||
# Example: make
|
||||
# Example: make PLATFORM=linux/aarch64 TAG=my-tag
|
||||
# Example: make prod-build
|
||||
# Example: make PLATFORM=linux/aarch64 TAG=my-tag prod-build
|
||||
# Example: make postgres-on-docker (for local development)
|
||||
|
||||
PLATFORM ?= linux/amd64
|
||||
TAG ?= latest
|
||||
DOCKER_NETWORK=twenty_network
|
||||
|
||||
# =============================================================================
|
||||
# Production Image Building
|
||||
# =============================================================================
|
||||
|
||||
prod-build:
|
||||
@cd ../.. && docker build -f ./packages/twenty-docker/twenty/Dockerfile --platform $(PLATFORM) --tag twenty:$(TAG) . && cd -
|
||||
@@ -25,3 +31,55 @@ prod-website-build:
|
||||
|
||||
prod-website-run:
|
||||
@docker run -d -p 3000:3000 --name twenty-website twenty-website:$(TAG)
|
||||
|
||||
# =============================================================================
|
||||
# Local Development Services
|
||||
# Run these from the repository root: make -C packages/twenty-docker <target>
|
||||
# =============================================================================
|
||||
|
||||
ensure-docker-network:
|
||||
docker network inspect $(DOCKER_NETWORK) >/dev/null 2>&1 || docker network create $(DOCKER_NETWORK)
|
||||
|
||||
postgres-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) \
|
||||
--name twenty_pg \
|
||||
-e POSTGRES_USER=postgres \
|
||||
-e POSTGRES_PASSWORD=postgres \
|
||||
-e ALLOW_NOSSL=true \
|
||||
-v twenty_db_data:/var/lib/postgresql/data \
|
||||
-p 5432:5432 \
|
||||
postgres:16
|
||||
@echo "Waiting for PostgreSQL to be ready..."
|
||||
@until docker exec twenty_pg psql -U postgres -d postgres \
|
||||
-c 'SELECT pg_is_in_recovery();' 2>/dev/null | grep -q 'f'; do \
|
||||
sleep 1; \
|
||||
done
|
||||
docker exec twenty_pg psql -U postgres -d postgres \
|
||||
-c "CREATE DATABASE \"default\" WITH OWNER postgres;" \
|
||||
-c "CREATE DATABASE \"test\" WITH OWNER postgres;"
|
||||
|
||||
redis-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
|
||||
|
||||
clickhouse-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) --name twenty_clickhouse -p 8123:8123 -p 9000:9000 -e CLICKHOUSE_PASSWORD=devPassword clickhouse/clickhouse-server:latest \
|
||||
|
||||
grafana-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) \
|
||||
--name twenty_grafana \
|
||||
-p 4000:3000 \
|
||||
-e GF_SECURITY_ADMIN_USER=admin \
|
||||
-e GF_SECURITY_ADMIN_PASSWORD=admin \
|
||||
-e GF_INSTALL_PLUGINS=grafana-clickhouse-datasource \
|
||||
-v $(PWD)/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources \
|
||||
grafana/grafana-oss:latest
|
||||
|
||||
opentelemetry-collector-on-docker: ensure-docker-network
|
||||
docker run -d --network $(DOCKER_NETWORK) \
|
||||
--name twenty_otlp_collector \
|
||||
-p 4317:4317 \
|
||||
-p 4318:4318 \
|
||||
-p 13133:13133 \
|
||||
-v $(PWD)/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
|
||||
otel/opentelemetry-collector-contrib:latest \
|
||||
--config /etc/otel-collector-config.yaml
|
||||
|
||||
@@ -8,7 +8,7 @@ COPY ./yarn.lock .
|
||||
COPY ./.yarnrc.yml .
|
||||
COPY ./.yarn/releases /app/.yarn/releases
|
||||
COPY ./.yarn/patches /app/.yarn/patches
|
||||
COPY ./tools/eslint-rules /app/tools/eslint-rules
|
||||
COPY ./packages/twenty-eslint-rules /app/packages/twenty-eslint-rules
|
||||
COPY ./packages/twenty-ui/package.json /app/packages/twenty-ui/
|
||||
COPY ./packages/twenty-shared/package.json /app/packages/twenty-shared/
|
||||
COPY ./packages/twenty-website/package.json /app/packages/twenty-website/package.json
|
||||
@@ -40,4 +40,4 @@ RUN chown -R 1000 /app
|
||||
# Use non root user with uid 1000
|
||||
USER 1000
|
||||
|
||||
CMD ["/bin/sh", "-c", "npx nx start"]
|
||||
CMD ["/bin/sh", "-c", "npx nx start"]
|
||||
|
||||
@@ -8,7 +8,6 @@ COPY ./package.json ./yarn.lock ./.yarnrc.yml ./tsconfig.base.json ./nx.json /ap
|
||||
COPY ./.yarn/releases /app/.yarn/releases
|
||||
COPY ./.yarn/patches /app/.yarn/patches
|
||||
|
||||
COPY ./.prettierrc /app/
|
||||
COPY ./packages/twenty-emails/package.json /app/packages/twenty-emails/
|
||||
COPY ./packages/twenty-server/package.json /app/packages/twenty-server/
|
||||
COPY ./packages/twenty-server/patches /app/packages/twenty-server/patches
|
||||
|
||||
@@ -112,7 +112,7 @@ You should run all commands in the following steps from the root of the project.
|
||||
|
||||
**Option 2:** If you have docker installed:
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Mac OS">
|
||||
@@ -162,7 +162,7 @@ You should run all commands in the following steps from the root of the project.
|
||||
|
||||
**Option 2:** If you have docker installed:
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Windows (WSL)">
|
||||
@@ -179,7 +179,7 @@ You should run all commands in the following steps from the root of the project.
|
||||
Running Docker on WSL adds an extra layer of complexity.
|
||||
Only use this option if you are comfortable with the extra steps involved, including turning on [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -196,7 +196,7 @@ Twenty requires a redis cache to provide the best performance
|
||||
|
||||
**Option 2:** If you have docker installed:
|
||||
```bash
|
||||
make redis-on-docker
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Mac OS">
|
||||
@@ -209,7 +209,7 @@ Twenty requires a redis cache to provide the best performance
|
||||
|
||||
**Option 2:** If you have docker installed:
|
||||
```bash
|
||||
make redis-on-docker
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Windows (WSL)">
|
||||
@@ -218,7 +218,7 @@ Twenty requires a redis cache to provide the best performance
|
||||
|
||||
**Option 2:** If you have docker installed:
|
||||
```bash
|
||||
make redis-on-docker
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -34,32 +34,35 @@ npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# Authenticate using your API key (you'll be prompted)
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn dev
|
||||
yarn app:dev
|
||||
```
|
||||
|
||||
From here you can:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new entity to your application (guided)
|
||||
yarn create-entity
|
||||
yarn app:create-entity
|
||||
|
||||
# Generate a typed Twenty client and workspace entity types
|
||||
yarn generate
|
||||
yarn app:generate
|
||||
|
||||
# Run a one‑time sync (instead of watch mode)
|
||||
yarn sync
|
||||
yarn app:sync
|
||||
|
||||
# Watch your application's functions logs
|
||||
yarn logs
|
||||
yarn function:logs
|
||||
|
||||
# Execute a function by name
|
||||
yarn function:execute -n my-function -p '{"name": "test"}'
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
yarn uninstall
|
||||
yarn app:uninstall
|
||||
|
||||
# Display commands' help
|
||||
yarn help
|
||||
yarn app: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).
|
||||
@@ -159,29 +162,43 @@ At a high level:
|
||||
|
||||
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/app/` for your custom objects, functions, or roles.
|
||||
|
||||
- `yarn app:generate` will create a `generated/` folder (typed Twenty client + workspace types).
|
||||
- `yarn app:create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
|
||||
l
|
||||
|
||||
## Authentication
|
||||
|
||||
The first time you run `yarn auth`, you'll be prompted for:
|
||||
The first time you run `yarn auth:login`, 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>`.
|
||||
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch between them.
|
||||
|
||||
Examples:
|
||||
### Managing workspaces
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Login interactively (recommended)
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
|
||||
# Use a specific workspace profile
|
||||
yarn auth --workspace my-custom-workspace
|
||||
# Login to a specific workspace profile
|
||||
yarn auth:login --workspace my-custom-workspace
|
||||
|
||||
# List all configured workspaces
|
||||
yarn auth:list
|
||||
|
||||
# Switch the default workspace (interactive)
|
||||
yarn auth:switch
|
||||
|
||||
# Switch to a specific workspace
|
||||
yarn auth:switch production
|
||||
|
||||
# Check current authentication status
|
||||
yarn auth:status
|
||||
```
|
||||
|
||||
Once you've switched workspaces with `auth:switch`, all subsequent commands will use that workspace by default. You can still override it temporarily with `--workspace <name>`.
|
||||
|
||||
## Use the SDK resources (types & config)
|
||||
|
||||
The twenty-sdk provides typed building blocks and helper functions you use inside your app. Below are the key pieces you'll touch most often.
|
||||
@@ -278,7 +295,7 @@ Key points:
|
||||
- The `universalIdentifier` must be unique and stable across deployments.
|
||||
- Each field requires a `name`, `type`, `label`, and its own stable `universalIdentifier`.
|
||||
- The `fields` array is optional — you can define objects without custom fields.
|
||||
- You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships.
|
||||
- You can scaffold new objects using `yarn app:create-entity`, which guides you through naming, fields, and relationships.
|
||||
|
||||
<Note>
|
||||
**Base fields are created automatically.** When you define a custom object, Twenty automatically adds standard fields such as `name`, `createdAt`, `updatedAt`, `createdBy`, `position`, and `deletedAt`. You don't need to define these in your `fields` array — only add your custom fields.
|
||||
@@ -443,18 +460,18 @@ Each function file uses `defineFunction()` to export a configuration with a hand
|
||||
```typescript
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '../../generated';
|
||||
|
||||
const handler = async (
|
||||
params:
|
||||
| { name?: string }
|
||||
| RoutePayload
|
||||
| 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'
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
|
||||
const result = await client.mutation({
|
||||
@@ -491,7 +508,8 @@ export default defineFunction({
|
||||
{
|
||||
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
|
||||
type: 'databaseEvent',
|
||||
eventName: 'person.created',
|
||||
eventName: 'person.updated',
|
||||
updatedFields: ['name'],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -501,21 +519,111 @@ 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`
|
||||
- **databaseEvent**: Runs on workspace object lifecycle events. When the event operation is `updated`, specific fields to listen to can be specified in the `updatedFields` array. If left undefined or empty, any update will trigger the function.
|
||||
> e.g. `person.updated`
|
||||
|
||||
Notes:
|
||||
- The `triggers` array is optional. Functions without triggers can be used as utility functions called by other functions.
|
||||
- You can mix multiple trigger types in a single function.
|
||||
|
||||
### Route trigger payload
|
||||
|
||||
<Warning>
|
||||
**Breaking change (v1.16, January 2026):** The route trigger payload format has changed. Prior to v1.16, query parameters, path parameters, and body were sent directly as the payload. Starting with v1.16, they are nested inside a structured `RoutePayload` object.
|
||||
|
||||
**Before v1.16:**
|
||||
```typescript
|
||||
const handler = async (params) => {
|
||||
const { param1, param2 } = params; // Direct access
|
||||
};
|
||||
```
|
||||
|
||||
**After v1.16:**
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const { param1, param2 } = event.body; // Access via .body
|
||||
const { queryParam } = event.queryStringParameters;
|
||||
const { id } = event.pathParameters;
|
||||
};
|
||||
```
|
||||
|
||||
**To migrate existing functions:** Update your handler to destructure from `event.body`, `event.queryStringParameters`, or `event.pathParameters` instead of directly from the params object.
|
||||
</Warning>
|
||||
|
||||
When a route trigger invokes your function, it receives a `RoutePayload` object that follows the AWS HTTP API v2 format. Import the type from `twenty-sdk`:
|
||||
|
||||
```typescript
|
||||
import { defineFunction, type RoutePayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (event: RoutePayload) => {
|
||||
// Access request data
|
||||
const { headers, queryStringParameters, pathParameters, body } = event;
|
||||
|
||||
// HTTP method and path are available in requestContext
|
||||
const { method, path } = event.requestContext.http;
|
||||
|
||||
return { message: 'Success' };
|
||||
};
|
||||
```
|
||||
|
||||
The `RoutePayload` type has the following structure:
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `headers` | `Record<string, string \| undefined>` | HTTP headers (only those listed in `forwardedRequestHeaders`) |
|
||||
| `queryStringParameters` | `Record<string, string \| undefined>` | Query string parameters (multiple values joined with commas) |
|
||||
| `pathParameters` | `Record<string, string \| undefined>` | Path parameters extracted from the route pattern (e.g., `/users/:id` → `{ id: '123' }`) |
|
||||
| `body` | `object \| null` | Parsed request body (JSON) |
|
||||
| `isBase64Encoded` | `boolean` | Whether the body is base64 encoded |
|
||||
| `requestContext.http.method` | `string` | HTTP method (GET, POST, PUT, PATCH, DELETE) |
|
||||
| `requestContext.http.path` | `string` | Raw request path |
|
||||
|
||||
### Forwarding HTTP headers
|
||||
|
||||
By default, HTTP headers from incoming requests are **not** passed to your serverless function for security reasons. To access specific headers, explicitly list them in the `forwardedRequestHeaders` array:
|
||||
|
||||
```typescript
|
||||
export default defineFunction({
|
||||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||||
name: 'webhook-handler',
|
||||
handler,
|
||||
triggers: [
|
||||
{
|
||||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||||
type: 'route',
|
||||
path: '/webhook',
|
||||
httpMethod: 'POST',
|
||||
isAuthRequired: false,
|
||||
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
In your handler, you can then access these headers:
|
||||
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const signature = event.headers['x-webhook-signature'];
|
||||
const contentType = event.headers['content-type'];
|
||||
|
||||
// Validate webhook signature...
|
||||
return { received: true };
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
Header names are normalized to lowercase. Access them using lowercase keys (for example, `event.headers['content-type']`).
|
||||
</Note>
|
||||
|
||||
You can create new functions in two ways:
|
||||
|
||||
- **Scaffolded**: Run `yarn create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
|
||||
- **Scaffolded**: Run `yarn app:create-entity` and choose the option to add a new function. This generates a starter file with a handler and config.
|
||||
- **Manual**: Create a new `*.function.ts` file and use `defineFunction()`, 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:
|
||||
Run yarn app:generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
|
||||
|
||||
```typescript
|
||||
import Twenty from './generated';
|
||||
@@ -524,7 +632,7 @@ 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.
|
||||
The client is re-generated by `yarn app:generate`. Re-run after changing your objects and `yarn app:sync` or when onboarding to a new workspace.
|
||||
|
||||
#### Runtime credentials in serverless functions
|
||||
|
||||
@@ -568,13 +676,13 @@ Then add scripts like these:
|
||||
}
|
||||
```
|
||||
|
||||
Now you can run the same commands via Yarn, e.g. `yarn dev`, `yarn sync`, etc.
|
||||
Now you can run the same commands via Yarn, e.g. `yarn app:dev`, `yarn app:sync`, etc.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Authentication errors: run `yarn auth` and ensure your API key has the required permissions.
|
||||
- Authentication errors: run `yarn auth:login` 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.
|
||||
- Types or client missing/outdated: run `yarn app:generate` and then `yarn app:dev`.
|
||||
- Dev mode not syncing: ensure `yarn app:dev` is running and that changes are not ignored by your environment.
|
||||
|
||||
Discord Help Channel: https://discord.com/channels/1130383047699738754/1130386664812982322
|
||||
|
||||
@@ -288,3 +288,44 @@ yarn command:prod cron:workflow:automated-cron-trigger
|
||||
<Warning>
|
||||
**Environment-only mode:** If you set `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`, add these variables to your `.env` file instead.
|
||||
</Warning>
|
||||
|
||||
## Serverless Functions
|
||||
|
||||
Twenty supports serverless functions for workflows and custom logic. The execution environment is configured via the `SERVERLESS_TYPE` environment variable.
|
||||
|
||||
<Warning>
|
||||
**Security Notice:** The local serverless driver (`SERVERLESS_TYPE=LOCAL`) runs code directly on the host in a Node.js process with no sandboxing. It should only be used for trusted code in development. For production deployments handling untrusted code, we highly recommend using `SERVERLESS_TYPE=LAMBDA` or `SERVERLESS_TYPE=DISABLED`.
|
||||
</Warning>
|
||||
|
||||
### Available Drivers
|
||||
|
||||
| Driver | Environment Variable | Use Case | Security Level |
|
||||
|--------|---------------------|----------|----------------|
|
||||
| Disabled | `SERVERLESS_TYPE=DISABLED` | Disable serverless functions entirely | N/A |
|
||||
| Local | `SERVERLESS_TYPE=LOCAL` | Development and trusted environments | Low (no sandboxing) |
|
||||
| Lambda | `SERVERLESS_TYPE=LAMBDA` | Production with untrusted code | High (hardware-level isolation) |
|
||||
|
||||
### Recommended Configuration
|
||||
|
||||
**For development:**
|
||||
```bash
|
||||
SERVERLESS_TYPE=LOCAL # default
|
||||
```
|
||||
|
||||
**For production (AWS):**
|
||||
```bash
|
||||
SERVERLESS_TYPE=LAMBDA
|
||||
SERVERLESS_LAMBDA_REGION=us-east-1
|
||||
SERVERLESS_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role
|
||||
SERVERLESS_LAMBDA_ACCESS_KEY_ID=your-access-key
|
||||
SERVERLESS_LAMBDA_SECRET_ACCESS_KEY=your-secret-key
|
||||
```
|
||||
|
||||
**To disable serverless functions:**
|
||||
```bash
|
||||
SERVERLESS_TYPE=DISABLED
|
||||
```
|
||||
|
||||
<Note>
|
||||
When using `SERVERLESS_TYPE=DISABLED`, any attempt to execute a serverless function will return an error. This is useful if you want to run Twenty without serverless function capabilities.
|
||||
</Note>
|
||||
|
||||
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 295 KiB |
|
After Width: | Height: | Size: 369 KiB |
|
After Width: | Height: | Size: 405 KiB |
|
After Width: | Height: | Size: 370 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 40 KiB |
@@ -2,7 +2,7 @@
|
||||
title: أفضل الممارسات
|
||||
---
|
||||
|
||||
This document outlines the best practices you should follow when working on the backend.
|
||||
تحدد هذه الوثيقة أفضل الممارسات التي يجب اتباعها عند العمل في الواجهة الخلفية.
|
||||
|
||||
## اتبع نهجًا معياريًا
|
||||
|
||||
@@ -11,7 +11,7 @@ This document outlines the best practices you should follow when working on the
|
||||
|
||||
## إتاحة الخدمات لاستخدامها في الوحدات
|
||||
|
||||
قم دائمًا بإنشاء خدمات ذات مسؤولية واضحة ووحيدة، مما يعزز من قابلية قراءة وصيانة الكود. Name the services descriptively and consistently.
|
||||
قم دائمًا بإنشاء خدمات ذات مسؤولية واضحة ووحيدة، مما يعزز من قابلية قراءة وصيانة الكود. قم بتسمية الخدمات بأسماء وصفية وبطريقة متسقة.
|
||||
|
||||
يجب أيضًا إتاحة الخدمات التي تريد استخدامها في وحدات أخرى. إتاحة الخدمات للوحدات الأخرى ممكنة عبر نظام حقن التبعيات القوي في NestJS، كما يعزز ذلك الاقتران الضعيف بين المكونات.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Feature Flags
|
||||
title: أعلام الميزة
|
||||
---
|
||||
|
||||
تُستخدم أعلام الميزات لإخفاء الميزات التجريبية. For Twenty, they are set on workspace level and not on a user level.
|
||||
تُستخدم أعلام الميزات لإخفاء الميزات التجريبية. بالنسبة إلى Twenty، يتم ضبطها على مستوى مساحة العمل وليس على مستوى المستخدم.
|
||||
|
||||
## إضافة علم ميزة جديد
|
||||
|
||||
|
||||
@@ -7,18 +7,18 @@ info: نظرة تفصيلية داخل هيكلية مجلدات الخادم
|
||||
|
||||
```
|
||||
server
|
||||
└───ability
|
||||
└───constants
|
||||
└───core
|
||||
└───database
|
||||
└───decorators
|
||||
└───filters
|
||||
└───guards
|
||||
└───health
|
||||
└───integrations
|
||||
└───metadata
|
||||
└───workspace
|
||||
└───utils
|
||||
└───قدرات
|
||||
└───ثوابت
|
||||
└───نواة
|
||||
└───قاعدة البيانات
|
||||
└───زخارف
|
||||
└───فلاتر
|
||||
└───حمايات
|
||||
└───الصحة
|
||||
└───تكاملات
|
||||
└───بيانات وصفية
|
||||
└───مساحة العمل
|
||||
└───أدوات
|
||||
```
|
||||
|
||||
## قدرات
|
||||
@@ -56,28 +56,28 @@ server
|
||||
```
|
||||
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
|
||||
└───منشئ مساحة العمل
|
||||
└───مصانع
|
||||
└───أنواع GraphQL
|
||||
└───قاعدة البيانات
|
||||
└───واجهات
|
||||
└───تعريفات الكائنات
|
||||
└───خدمات
|
||||
└───التخزين
|
||||
└───أدوات
|
||||
└───منشئ مستعرض مساحة العمل
|
||||
└───مصانع
|
||||
└───واجهات
|
||||
└───منشئ الاستعلامات مساحة العمل
|
||||
└───مصانع
|
||||
└───واجهات
|
||||
└───مشغل استعلامات مساحة العمل
|
||||
└───واجهات
|
||||
└───أدوات
|
||||
└───مصدر بيانات مساحة العمل
|
||||
└───مدير مساحة العمل
|
||||
└───مشغل الانتقالات مساحة العمل
|
||||
└───أدوات
|
||||
└───workspace.module.ts
|
||||
└───workspace.factory.spec.ts
|
||||
└───workspace.factory.ts
|
||||
@@ -96,7 +96,7 @@ workspace
|
||||
مصانع متخصصة لتوليد الإنشاءات المتعلقة بـ GraphQL.
|
||||
|
||||
* المصنع النوعي يترجم بيانات الحقول الوصفية إلى أنواع GraphQL باستخدام `TypeMapperService`.
|
||||
* The type-definition.factory creates GraphQL input or output objects derived from `objectMetadata`.
|
||||
* ينشئ type-definition.factory كائنات إدخال أو إخراج لـ GraphQL مشتقة من `objectMetadata`.
|
||||
|
||||
#### أنواع GraphQL
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Message Queue
|
||||
title: قائمة انتظار الرسائل
|
||||
---
|
||||
|
||||
تسهل القوائم العمليات غير المتزامنة. يمكن استخدامها لأداء مهام الخلفية مثل إرسال بريد ترحيبي عند التسجيل.
|
||||
@@ -10,9 +10,9 @@ title: Message Queue
|
||||
## خطوات إنشاء واستخدام قائمة جديدة
|
||||
|
||||
1. أضف اسم قائمة لقائمة جديدة تحت التعداد `MESSAGE_QUEUES`.
|
||||
2. Provide the factory implementation of the queue with the queue name as the dependency token.
|
||||
2. وفّر تنفيذ المصنع لقائمة الانتظار مع استخدام اسم قائمة الانتظار بوصفه رمز التبعية.
|
||||
3. قم بإدراج القائمة التي أنشأتها في الوحدة/الخدمة المطلوبة مع اسم القائمة كرمز تبعية.
|
||||
4. Add worker class with token based injection just like producer.
|
||||
4. أضِف صنف عامل مع حقن قائم على الرمز تمامًا مثل المنتِج.
|
||||
|
||||
### نموذج للاستخدام
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ 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 {command} twenty-server` (أو `npx nx run twenty-server:{command}`).
|
||||
|
||||
### إعداد المرة الأولى
|
||||
|
||||
@@ -22,7 +22,7 @@ npx nx run twenty-server:start
|
||||
### Lint
|
||||
|
||||
```
|
||||
npx nx run twenty-server:lint # pass --fix to fix lint errors
|
||||
npx nx run twenty-server:lint # مرر --fix لإصلاح أخطاء التدقيق
|
||||
```
|
||||
|
||||
### تجربة
|
||||
@@ -36,13 +36,13 @@ npx nx run twenty-server:test:integration # تشغيل اختبارات التك
|
||||
|
||||
### إعادة تعيين قاعدة البيانات
|
||||
|
||||
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)
|
||||
|
||||
@@ -67,13 +67,13 @@ npx nx run twenty-server:command workspace:sync-metadata -f
|
||||
|
||||
## "التقنية المستخدمة"
|
||||
|
||||
Twenty primarily uses NestJS for the backend.
|
||||
تستخدم Twenty بشكل أساسي NestJS للواجهة الخلفية.
|
||||
|
||||
Prisma كان أول ORM استخدمناه. ولكن للسماح للمستخدمين بإنشاء الحقول والعناصر المخصصة، كان من المنطقي استخدام مستوى أقل حيث نحتاج إلى تحكم دقيق. الآن يستخدم المشروع TypeORM.
|
||||
|
||||
إليك شكل العناصر التقنية الآن.
|
||||
|
||||
**Core**
|
||||
**النواة**
|
||||
|
||||
* [NestJS](https://nestjs.com/)
|
||||
* [TypeORM](https://typeorm.io/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Bugs, Requests & Pull Requests
|
||||
info: Report issues, request features, and contribute code
|
||||
title: الأخطاء والطلبات وطلبات السحب
|
||||
info: أبلغ عن المشكلات، واطلب الميزات، وساهم بالشفرة البرمجية
|
||||
---
|
||||
|
||||
## الإبلاغ عن الأخطاء
|
||||
@@ -9,70 +9,70 @@ info: Report issues, request features, and contribute code
|
||||
|
||||
يمكنك أيضًا طلب المساعدة عبر [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).
|
||||
تبدأ المساهمة بالشفرة في Twenty بطلب سحب (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)
|
||||
1. تحقق من [المشكلات القائمة](https://github.com/twentyhq/twenty/issues) للعمل ذي الصلة
|
||||
2. بالنسبة للميزات الجديدة، افتح مشكلة أولًا للنقاش
|
||||
3. راجع [مدونة قواعد السلوك](https://github.com/twentyhq/twenty/blob/main/CODE_OF_CONDUCT.md)
|
||||
|
||||
### Fork and Clone
|
||||
### التفريع والاستنساخ
|
||||
|
||||
1. Fork the repository on GitHub
|
||||
2. Clone your fork:
|
||||
1. فرّع المستودع على GitHub
|
||||
2. استنسخ تفريعك:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/twenty.git
|
||||
cd twenty
|
||||
```
|
||||
|
||||
3. Add upstream remote:
|
||||
3. أضف المستودع البعيد upstream:
|
||||
|
||||
```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
|
||||
1. اكتب شفرة نظيفة وموثقة جيدًا
|
||||
2. اتبع أسلوب الشفرة المعمول به
|
||||
3. أضف اختبارات للوظائف الجديدة
|
||||
4. حدّث الوثائق عند الحاجة
|
||||
|
||||
### Submit Your PR
|
||||
### أرسل طلب السحب الخاص بك
|
||||
|
||||
1. Push your branch:
|
||||
1. ادفع فرعك:
|
||||
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
|
||||
2. Open a PR on GitHub
|
||||
3. Fill in the PR template
|
||||
4. Link related issues
|
||||
2. افتح طلب سحب على GitHub
|
||||
3. املأ قالب طلب السحب
|
||||
4. اربط المشكلات ذات الصلة
|
||||
|
||||
### PR Checklist
|
||||
### قائمة التحقق لطلب السحب
|
||||
|
||||
* [ ] Code follows project style guidelines
|
||||
* [ ] Tests pass locally
|
||||
* [ ] Documentation is updated
|
||||
* [ ] PR description explains the changes
|
||||
* [ ] الشفرة تتبع إرشادات أسلوب المشروع
|
||||
* [ ] تجتاز الاختبارات محليًا
|
||||
* [ ] الوثائق محدّثة
|
||||
* [ ] وصف طلب السحب يوضح التغييرات
|
||||
|
||||
@@ -77,8 +77,8 @@ If you feel like you need to add a `useEffect` in your root component, you shoul
|
||||
يمكنك تطبيق نفس الشيء على منطق جلب البيانات، مع الخُطافات Apollo.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, will cause re-renders even if data is not changing,
|
||||
// because useEffect needs to be re-evaluated
|
||||
// ❌ سيّئ، سيتسبب في إعادة التصيير حتى إذا لم تتغير البيانات،
|
||||
// لأن useEffect يحتاج إلى إعادة التقييم
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
@@ -100,8 +100,8 @@ export const App = () => (
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, will not cause re-renders if data is not changing,
|
||||
// because useEffect is re-evaluated in another sibling component
|
||||
// ✅ جيّد، لن يتسبب في إعادة التصيير إذا لم تتغير البيانات،
|
||||
// لأن useEffect يُعاد تقييمه في مكوّن شقيق آخر
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
|
||||
@@ -168,13 +168,13 @@ export const App = () => (
|
||||
الأسماء العامة في البرمجة ليست مثالية لأنها تفتقر إلى التحديد، مما يؤدي إلى الغموض وتقليل قابلية قراءة التعليمات البرمجية. مثل هذه الأسماء تفشل في التعبير عن الغرض من المتغير أو الوظيفة، مما يجعل من الصعب على المطورين فهم نية التعليمات البرمجية دون تحقيق أعمق. يمكن أن يؤدي ذلك إلى زيادة وقت إزالة الأخطاء، وزيادة قابلية التعرض للأخطاء، وصعوبات في الصيانة والتعاون. في الوقت نفسه، تجعل التسمية الوصفية التعليمات البرمجية تفسيرية بذاتها وأسهل في التنقل، مما يعزز جودة التعليمات البرمجية وإنتاجية المطور.
|
||||
|
||||
```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('');
|
||||
```
|
||||
|
||||
@@ -187,14 +187,14 @@ const [email, setEmail] = useState('');
|
||||
يجب أن تبدأ أسماء معالجات الأحداث بكلمة `handle`، بينما يعتبر `on` بادئة تستخدم لتسمية الأحداث في خصائص المكونات.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
// ❌ سيّئ
|
||||
const onEmailChange = (val: string) => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good
|
||||
// ✅ جيّد
|
||||
const handleEmailChange = (val: string) => {
|
||||
// ...
|
||||
};
|
||||
@@ -222,12 +222,12 @@ const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
|
||||
**الاستخدام**
|
||||
|
||||
```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" />;
|
||||
```
|
||||
|
||||
@@ -240,7 +240,7 @@ const Form = () => <EmailField value="username@email.com" />;
|
||||
```tsx
|
||||
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
|
||||
|
||||
// In MyComponent
|
||||
// داخل MyComponent
|
||||
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -285,7 +285,7 @@ const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
|
||||
**الاستخدام**
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, specifies the entire relative path
|
||||
// ❌ سيّئ، يحدد المسار النسبي بالكامل
|
||||
import {
|
||||
CatalogDecorator
|
||||
} from '../../../../../testing/decorators/CatalogDecorator';
|
||||
@@ -295,7 +295,7 @@ import {
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, utilises the designated aliases
|
||||
// ✅ جيّد، يستخدم الأسماء المستعارة المحددة
|
||||
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
```
|
||||
@@ -310,10 +310,10 @@ const validationSchema = z
|
||||
exist: z.boolean(),
|
||||
email: z
|
||||
.string()
|
||||
.email('Email must be a valid email'),
|
||||
.email('يجب أن يكون البريد الإلكتروني صالحًا'),
|
||||
password: z
|
||||
.string()
|
||||
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
|
||||
.regex(PASSWORD_REGEX, 'يجب أن تحتوي كلمة المرور على 8 أحرف على الأقل'),
|
||||
})
|
||||
.required();
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ title: مفاتيح الاختصار
|
||||
|
||||
لمعالجة هذه المشكلة، لدينا خطاف مخصص يمكن من الاستماع لمفاتيح الاختصار دون أي تعارض.
|
||||
|
||||
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?
|
||||
## كيف تستمع إلى مفاتيح الاختصار عمليًا؟
|
||||
|
||||
هناك خطوتان متضمنتان في إعداد الاستماع لمفاتيح الاختصار:
|
||||
|
||||
@@ -73,9 +73,9 @@ const PageListeningEnter = () => {
|
||||
|
||||
### الاستماع لمفاتيح الاختصار في مكون من نوع مودال
|
||||
|
||||
For this example we'll use a modal component that listens for the Escape key to tell its parent to close it.
|
||||
في هذا المثال سنستخدم مكوّن نافذة منبثقة يستمع لمفتاح Escape ليطلب من المكوّن الأب إغلاقه.
|
||||
|
||||
Here the user interaction is changing the scope.
|
||||
هنا يتمثّل تفاعل المستخدم في تغيير النطاق.
|
||||
|
||||
```tsx
|
||||
const ExamplePageWithModal = () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Storybook
|
||||
description: Browse Twenty's UI component library
|
||||
description: تصفح مكتبة مكونات واجهة المستخدم الخاصة بـ Twenty
|
||||
---
|
||||
|
||||
View our complete component library and documentation in Storybook.
|
||||
اطلع على مكتبة المكونات الكاملة لدينا والتوثيق في Storybook.
|
||||
|
||||
[Open Storybook →](https://storybook.twenty.com)
|
||||
[افتح Storybook →](https://storybook.twenty.com)
|
||||
|
||||
@@ -4,7 +4,7 @@ title: دليل الأسلوب
|
||||
|
||||
تشمل هذه الوثيقة القواعد التي يجب اتباعها عند كتابة التعليمات البرمجية.
|
||||
|
||||
The goal here is to have a consistent codebase, which is easy to read and easy to maintain.
|
||||
الهدف هنا هو وجود قاعدة شيفرة متسقة يسهل قراءتها وصيانتها.
|
||||
|
||||
لهذا، من الأفضل أن تكون تفصيلًا أكثر قليلاً بدلاً من أن تكون موجزًا للغاية.
|
||||
|
||||
@@ -67,10 +67,10 @@ const EmailField: React.FC<{
|
||||
```
|
||||
|
||||
```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.
|
||||
/* ✅ - جيد، نوع منفصل (OwnProps) يُعرَّف صراحةً لخصائص
|
||||
* المكون
|
||||
* - هذه الطريقة لا تتضمن تلقائيًا خاصية الأطفال. إذا
|
||||
* كنت تريد تضمينها، يجب أن تحددها في OwnProps.
|
||||
*/
|
||||
type EmailFieldProps = {
|
||||
value: string;
|
||||
@@ -81,7 +81,7 @@ const EmailField = ({ value }: EmailFieldProps) => (
|
||||
);
|
||||
```
|
||||
|
||||
#### No Single Variable Prop Spreading in JSX Elements
|
||||
#### لا تستخدم انتشار متغير فردي للخصائص في عناصر JSX
|
||||
|
||||
تجنب استخدام انتشار متغير فردي للخصائص في عناصر JSX، مثل `{...props}`. غالبًا ما تؤدي هذه الممارسة إلى شكل تعليمي أقل قابلية للقراءة وأصعب في الصيانة لأنه من غير الواضح أي الخصائص يتلقاها المكون.
|
||||
|
||||
@@ -94,8 +94,8 @@ const MyComponent = (props: OwnProps) => {
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* ✅ - Good, Explicitly lists all props
|
||||
* - Enhances readability and maintainability
|
||||
/* ✅ - جيد، يسرد جميع الخصائص بوضوح
|
||||
* - يعزز من قابلية القراءة والصيانة
|
||||
*/
|
||||
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
|
||||
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
|
||||
@@ -110,7 +110,7 @@ const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
|
||||
|
||||
## JavaScript
|
||||
|
||||
### Use nullish-coalescing operator `??`
|
||||
### استخدم معامل دمج القيم الفارغة `??`
|
||||
|
||||
```tsx
|
||||
// ❌ سيء، قد يعيد 'default' حتى إذا كانت القيمة 0 أو ''
|
||||
@@ -120,13 +120,13 @@ const value = process.env.MY_VALUE || 'default';
|
||||
const value = process.env.MY_VALUE ?? 'default';
|
||||
```
|
||||
|
||||
### Use optional chaining `?.`
|
||||
### استخدم معامل الربط الاختياري `?.`
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
// ❌ سيء
|
||||
onClick && onClick();
|
||||
|
||||
// ✅ Good
|
||||
// ✅ جيد
|
||||
onClick?.();
|
||||
```
|
||||
|
||||
@@ -148,7 +148,7 @@ type MyType = {
|
||||
};
|
||||
```
|
||||
|
||||
### Use string literals instead of enums
|
||||
### استخدم السلاسل النصية بدلاً من التعدادات
|
||||
|
||||
[الحروف المشفوعة](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) هي الطريقة المفضلة للتعامل مع القيم الشبيهة بالأعداد المخصصة في TypeScript. من السهل توسيعها باستخدام Pick و Omit، وتقدم تجربة مطور أفضل، خاصة مع إكمال التعليمات البرمجية.
|
||||
|
||||
@@ -190,7 +190,7 @@ setHotkeyScopeAndMemorizePreviousScope(
|
||||
);
|
||||
```
|
||||
|
||||
## Styling
|
||||
## التنسيق
|
||||
|
||||
### استخدام مكونات منسقة
|
||||
|
||||
@@ -224,7 +224,7 @@ const StyledTitle = styled.div`
|
||||
`;
|
||||
```
|
||||
|
||||
### Theming
|
||||
### السمات
|
||||
|
||||
استخدام السمة لتنسيق معظم المكونات هو النهج المفضل.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: العمل مع فيجما
|
||||
info: Learn how you can collaborate with Twenty's Figma
|
||||
info: تعرّف على كيفية التعاون باستخدام Figma الخاص بـ Twenty
|
||||
---
|
||||
|
||||
فيجما هي أداة تصميم واجهات تعاونية تساعد في سد فجوة التواصل بين المصممين والمطورين.
|
||||
@@ -18,7 +18,7 @@ info: Learn how you can collaborate with Twenty's Figma
|
||||
|
||||
## هيكل فيجما
|
||||
|
||||
On the left sidebar, you can access the different pages of Twenty's Figma. هكذا هم مُنظمون:
|
||||
من الشريط الجانبي الأيسر، يمكنك الوصول إلى الصفحات المختلفة في Figma الخاص بـ Twenty. هكذا هم مُنظمون:
|
||||
|
||||
* **صفحة المكونات:** هذه هي الصفحة الأولى. يستخدمها المصمم لإنشاء وتنظيم العناصر التصميمية القابلة لإعادة الاستخدام في ملف التصميم. على سبيل المثال، الأزرار، الأيقونات، الرموز أو أي مكونات أخرى قابلة لإعادة الاستخدام. تعمل على الحفاظ على التناسق عبر التصميم.
|
||||
* **الصفحة الرئيسية:** الصفحة الثانية هي الصفحة الرئيسية التي تظهر واجهة المستخدم الكاملة للمشروع. يمكنك الضغط على ***تشغيل*** لاستخدام النموذج الأولي الكامل للتطبيق.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: الإعداد المحلي
|
||||
description: The guide for contributors (or curious developers) who want to run Twenty locally.
|
||||
description: الدليل للمساهمين (أو المطورين الفضوليين) الذين يرغبون في تشغيل Twenty محلياً.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
## المتطلبات الأساسية
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux و MacOS">
|
||||
@@ -15,7 +15,7 @@ description: The guide for contributors (or curious developers) who want to run
|
||||
* [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.
|
||||
لن يعمل `npm` ، يجب عليك استخدام `yarn` بدلًا من ذلك. يأتي Yarn الآن مع Node.js، لذا لست بحاجة إلى تثبيته بشكل منفصل.
|
||||
عليك فقط تشغيل `corepack enable` لتفعيل Yarn إذا لم تقم بذلك بعد.
|
||||
</Warning>
|
||||
</Tab>
|
||||
@@ -30,7 +30,7 @@ description: The guide for contributors (or curious developers) who want to run
|
||||
|
||||
يجب أن ترى الآن مطالبة لإعادة تشغيل جهاز الكمبيوتر الخاص بك. إذا لم يكن كذلك، فأعد تشغيله يدويًا.
|
||||
|
||||
Upon restart, a powershell window will open and install Ubuntu. قد يستغرق هذا وقتًا طويلاً.
|
||||
عند إعادة التشغيل، ستُفتح نافذة PowerShell وسيتم تثبيت Ubuntu. قد يستغرق هذا وقتًا طويلاً.
|
||||
سترى مطالبة لإنشاء اسم المستخدم وكلمة المرور لتثبيت Ubuntu الخاص بك.
|
||||
|
||||
2. تثبيت وإعداد git
|
||||
@@ -114,7 +114,7 @@ cd twenty
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -173,7 +173,7 @@ cd twenty
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -194,7 +194,7 @@ cd twenty
|
||||
استخدم هذا الخيار فقط إذا كنت مرتاحًا مع الخطوات الإضافية المتضمنة، بما في ذلك تشغيل [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
|
||||
|
||||
```bash
|
||||
make postgres-on-docker
|
||||
make -C packages/twenty-docker postgres-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -213,7 +213,7 @@ cd twenty
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make redis-on-docker
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -230,7 +230,7 @@ cd twenty
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make redis-on-docker
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -241,7 +241,7 @@ cd twenty
|
||||
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
|
||||
|
||||
```bash
|
||||
make redis-on-docker
|
||||
make -C packages/twenty-docker redis-on-docker
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -260,7 +260,7 @@ 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.
|
||||
**وضع تعدد مساحات العمل:** بشكل افتراضي، يعمل Twenty في وضع مساحة عمل واحدة حيث يمكن إنشاء مساحة عمل واحدة فقط. لتمكين دعم تعدد مساحات العمل (مفيد لاختبار الميزات المعتمدة على النطاقات الفرعية)، عيّن `IS_MULTIWORKSPACE_ENABLED=true` في ملف الخادم `.env`. راجع [وضع تعدد مساحات العمل](/l/ar/developers/self-host/capabilities/setup#multi-workspace-mode) للحصول على التفاصيل.
|
||||
</Info>
|
||||
|
||||
## الخطوة 6: تثبيت التبعيات
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Contribute
|
||||
description: Contribute to Twenty's open-source development.
|
||||
title: المساهمة
|
||||
description: ساهم في تطوير Twenty المفتوح المصدر.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -9,24 +9,24 @@ description: Contribute to Twenty's open-source development.
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
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.
|
||||
Twenty مفتوح المصدر ويرحب بمساهمات المجتمع. سواء كنت تقوم بإصلاح الأخطاء، أو إضافة الميزات، أو تحسين التوثيق، فإن مساهماتك تساعد في جعل Twenty أفضل للجميع.
|
||||
|
||||
## 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
|
||||
* **الإبلاغ عن الأخطاء**: ساعد في تحديد المشكلات وتوثيقها
|
||||
* **تقديم الميزات**: اقترح ونفّذ وظائف جديدة
|
||||
* **تحسين التوثيق**: اجعل وثائقنا أوضح وأكثر فائدة
|
||||
* **تطوير الواجهة الأمامية**: اعمل على واجهة المستخدم المبنية على React
|
||||
* **تطوير الواجهة الخلفية**: ساهم في خادم NestJS
|
||||
|
||||
## البدء
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="Bug Reports & Requests" icon="bug" href="/l/ar/developers/contribute/capabilities/bug-and-requests">
|
||||
Report issues or request features
|
||||
<Card title="تقارير الأخطاء والطلبات" icon="bug" href="/l/ar/developers/contribute/capabilities/bug-and-requests">
|
||||
أبلغ عن المشكلات أو اطلب ميزات
|
||||
</Card>
|
||||
|
||||
<Card title="تطوير الواجهة الأمامية" icon="browser" href="/l/ar/developers/contribute/capabilities/frontend-development">
|
||||
Contribute to the UI
|
||||
ساهم في واجهة المستخدم
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: واجهات برمجة التطبيقات
|
||||
description: Query and modify your CRM data programmatically using REST or GraphQL.
|
||||
description: استعلم وعدّل بيانات إدارة علاقات العملاء (CRM) لديك برمجياً باستخدام REST أو GraphQL.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
@@ -9,7 +9,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
## النموذج الأول للمطورين
|
||||
|
||||
Twenty generates APIs specifically for your data model:
|
||||
تقوم Twenty بإنشاء واجهات برمجة التطبيقات خصيصاً لنموذج بياناتك:
|
||||
|
||||
* **لا حاجة إلى معرفات طويلة**: استخدم أسماء الكائنات والحقول مباشرة في نقاط النهاية
|
||||
* **معالجة متساوية للأشياء القياسية والمخصصة**: تحصل أشياؤك المخصصة على نفس معاملة واجهة برمجة التطبيقات كما هو الحال مع الأشياء المضمنة
|
||||
@@ -17,52 +17,52 @@ Twenty generates APIs specifically for your data model:
|
||||
* **وثائق مخصصة**: يتم إنشاؤها خصيصًا لنموذج بيانات مساحة عملك
|
||||
|
||||
<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.
|
||||
وثائق واجهة برمجة التطبيقات المخصصة لك متاحة ضمن **الإعدادات → API & Webhooks** بعد إنشاء مفتاح API. نظرًا لأن Twenty تُنشئ واجهات برمجة تطبيقات تتطابق مع نموذج البيانات المخصص لديك، فإن الوثائق فريدة لمساحة عملك.
|
||||
</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
|
||||
## REST مقابل GraphQL
|
||||
|
||||
Both Core and Metadata APIs are available in REST and GraphQL formats:
|
||||
تتوفر واجهات برمجة التطبيقات الأساسية وواجهات البيانات الوصفية بصيغتي REST وGraphQL:
|
||||
|
||||
| التنسيق | Available Operations |
|
||||
| ----------- | ---------------------------------------------------------- |
|
||||
| **REST** | CRUD, batch operations, upserts |
|
||||
| **GraphQL** | Same + **batch upserts**, relationship queries in one call |
|
||||
| التنسيق | العمليات المتاحة |
|
||||
| ----------- | ----------------------------------------------------------------------------- |
|
||||
| **REST** | CRUD، عمليات الدفعات، إدراج/تحديث |
|
||||
| **GraphQL** | نفس الشيء + **عمليات إدراج/تحديث مجمعة**، واستعلامات العلاقات في استدعاء واحد |
|
||||
|
||||
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}/` |
|
||||
| البيئة | عنوان URL الأساسي |
|
||||
| --------------------- | ------------------------- |
|
||||
| **السحابة** | `https://api.twenty.com/` |
|
||||
| **الاستضافة الذاتية** | `https://{your-domain}/` |
|
||||
|
||||
## المصادقة
|
||||
|
||||
Every API request requires an API key in the header:
|
||||
كل طلب API يتطلب تضمين مفتاح API في رأس الطلب:
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
@@ -70,78 +70,78 @@ 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
|
||||
1. انتقل إلى **الإعدادات → APIs & Webhooks**
|
||||
2. انقر على **+ إنشاء مفتاح**
|
||||
3. التكوين:
|
||||
* **الاسم**: اسم وصفي للمفتاح
|
||||
* **تاريخ الانتهاء**: متى تنتهي صلاحية المفتاح
|
||||
4. انقر على **حفظ**
|
||||
5. **Copy immediately** — the key is only shown once
|
||||
5. **انسخه فوراً** — يظهر المفتاح مرة واحدة فقط
|
||||
|
||||
<VimeoEmbed videoId="928786722" title="Creating API key" />
|
||||
<VimeoEmbed videoId="928786722" title="إنشاء مفتاح API" />
|
||||
|
||||
<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.
|
||||
يمنح مفتاح API الخاص بك الوصول إلى بيانات حساسة. لا تشاركه مع خدمات غير موثوقة. إذا تم اختراقه، عطّلْه فوراً وأنشئ مفتاحاً جديداً.
|
||||
</Warning>
|
||||
|
||||
### Assign a Role to an API Key
|
||||
### تعيين دور لمفتاح API
|
||||
|
||||
For better security, assign a specific role to limit access:
|
||||
لتحسين الأمان، عيّن دوراً محدداً لتقييد الوصول:
|
||||
|
||||
1. اذهب إلى **الإعدادات → الأدوار**
|
||||
2. Click on the role to assign
|
||||
2. انقر على الدور الذي ترغب في تعيينه
|
||||
3. افتح علامة التبويب **التعيين**
|
||||
4. Under **API Keys**, click **+ Assign to API key**
|
||||
5. Select the API key
|
||||
4. ضمن **مفاتيح API**، انقر على **+ تعيين إلى مفتاح API**
|
||||
5. حدد مفتاح API
|
||||
|
||||
The key will inherit that role's permissions. See [Permissions](/l/ar/user-guide/permissions-access/capabilities/permissions) for details.
|
||||
سيرث المفتاح أذونات ذلك الدور. راجع [الأذونات](/l/ar/user-guide/permissions-access/capabilities/permissions) للحصول على التفاصيل.
|
||||
|
||||
### إدارة مفاتيح API
|
||||
|
||||
**Regenerate**: Settings → APIs & Webhooks → Click key → **Regenerate**
|
||||
**إعادة التوليد**: الإعدادات → APIs & Webhooks → انقر على المفتاح → **إعادة التوليد**
|
||||
|
||||
**Delete**: Settings → APIs & Webhooks → Click key → **Delete**
|
||||
**حذف**: الإعدادات → APIs & Webhooks → انقر على المفتاح → **حذف**
|
||||
|
||||
## API Playground
|
||||
## ملعب واجهة برمجة التطبيقات
|
||||
|
||||
Test your APIs directly in the browser with our built-in playground — available for both **REST** and **GraphQL**.
|
||||
اختبر واجهات برمجة التطبيقات لديك مباشرة في المتصفح باستخدام الملعب المدمج لدينا — متاح لكلٍ من **REST** و**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
|
||||
1. انتقل إلى **الإعدادات → APIs & Webhooks**
|
||||
2. أنشئ مفتاح API (مطلوب)
|
||||
3. انقر على **REST API** أو **GraphQL API** لفتح الملعب
|
||||
|
||||
### 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
|
||||
* **وثائق تفاعلية**: يتم إنشاؤها لنموذج البيانات المحدد لديك
|
||||
* **اختبارات حيّة**: تنفيذ استدعاءات API فعلية على مساحة عملك
|
||||
* **مستكشف المخطط**: تصفح الكائنات والحقول والعلاقات المتاحة
|
||||
* **منشئ الطلبات**: أنشئ الاستعلامات مع الإكمال التلقائي
|
||||
|
||||
The playground reflects your custom objects and fields, so documentation is always accurate for your workspace.
|
||||
يعكس الملعب الكائنات والحقول المخصصة لديك، لذا تكون الوثائق دائماً دقيقة لمساحة عملك.
|
||||
|
||||
## عمليات المجموعة
|
||||
|
||||
Both REST and GraphQL support batch operations:
|
||||
كلٌ من REST وGraphQL يدعمان عمليات الدفعات:
|
||||
|
||||
* **حجم المجموعة**: حتى 60 سجل لكل طلب
|
||||
* **Operations**: Create, update, delete multiple records
|
||||
* **العمليات**: إنشاء وتحديث وحذف سجلات متعددة
|
||||
|
||||
**GraphQL-only features:**
|
||||
**ميزات خاصة بـ GraphQL:**
|
||||
|
||||
* **Batch Upsert**: Create or update in one call
|
||||
* Use plural object names (e.g., `CreateCompanies` instead of `CreateCompany`)
|
||||
* **إدراج/تحديث دفعي**: إنشاء أو تحديث في استدعاء واحد
|
||||
* استخدم الأسماء الجمع للكائنات (على سبيل المثال، `CreateCompanies` بدلاً من `CreateCompany`)
|
||||
|
||||
## Rate Limits
|
||||
## حدود المعدل
|
||||
|
||||
API requests are throttled to ensure platform stability:
|
||||
يتم تنظيم طلبات API لضمان استقرار المنصة:
|
||||
|
||||
| Limit | القيمة |
|
||||
| -------------- | -------------------- |
|
||||
| **Requests** | 100 calls per minute |
|
||||
| **Batch size** | 60 records per call |
|
||||
| الحد | القيمة |
|
||||
| -------------- | ---------------------- |
|
||||
| **الطلبات** | 100 استدعاء في الدقيقة |
|
||||
| **حجم الدفعة** | 60 سجل لكل استدعاء |
|
||||
|
||||
<Tip>
|
||||
Use batch operations to maximize throughput — process up to 60 records in a single API call instead of making individual requests.
|
||||
استخدم عمليات الدفعات لزيادة الإنتاجية — عالج ما يصل إلى 60 سجلًا في استدعاء API واحد بدلاً من إجراء طلبات فردية.
|
||||
</Tip>
|
||||
|
||||
@@ -36,32 +36,35 @@ npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# Authenticate using your API key (you'll be prompted)
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn dev
|
||||
yarn app:dev
|
||||
```
|
||||
|
||||
من هنا يمكنك:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new entity to your application (guided)
|
||||
yarn create-entity
|
||||
yarn app:create-entity
|
||||
|
||||
# Generate a typed Twenty client and workspace entity types
|
||||
yarn generate
|
||||
yarn app:generate
|
||||
|
||||
# Run a one‑time sync (instead of watch mode)
|
||||
yarn sync
|
||||
yarn app:sync
|
||||
|
||||
# Watch your application's functions logs
|
||||
yarn logs
|
||||
yarn function:logs
|
||||
|
||||
# Execute a function by name
|
||||
yarn function:execute -n my-function -p '{"name": "test"}'
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
yarn uninstall
|
||||
yarn app:uninstall
|
||||
|
||||
# Display commands' help
|
||||
yarn help
|
||||
yarn app:help
|
||||
```
|
||||
|
||||
راجع أيضًا: صفحات مرجع CLI لـ [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) و[twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
|
||||
@@ -164,12 +167,13 @@ src/app/
|
||||
|
||||
ستضيف الأوامر اللاحقة مزيدًا من الملفات والمجلدات:
|
||||
|
||||
* `yarn generate` سيُنشئ مجلدًا `generated/` (عميل Twenty مضبوط الأنواع + أنواع مساحة العمل).
|
||||
* `yarn create-entity` سيضيف ملفات تعريف الكيانات تحت `src/app/` لكائناتك المخصصة أو الوظائف أو الأدوار.
|
||||
* `yarn app:generate` will create a `generated/` folder (typed Twenty client + workspace types).
|
||||
* `yarn app:create-entity` will add entity definition files under `src/app/` for your custom objects, functions, or roles.
|
||||
l
|
||||
|
||||
## المصادقة
|
||||
|
||||
في المرة الأولى التي تشغل فيها `yarn auth`، سيُطلب منك إدخال:
|
||||
The first time you run `yarn auth:login`, you'll be prompted for:
|
||||
|
||||
* عنوان URL لواجهة برمجة التطبيقات (الافتراضي http://localhost:3000 أو ملف تعريف مساحة العمل الحالية لديك)
|
||||
* مفتاح واجهة برمجة التطبيقات
|
||||
@@ -180,10 +184,10 @@ src/app/
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Login interactively (recommended)
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
|
||||
# Use a specific workspace profile
|
||||
yarn auth --workspace my-custom-workspace
|
||||
yarn auth:login --workspace my-custom-workspace
|
||||
```
|
||||
|
||||
## استخدم موارد SDK (الأنواع والتكوين)
|
||||
@@ -282,7 +286,7 @@ export default defineObject({
|
||||
* `universalIdentifier` يجب أن يكون فريدًا وثابتًا عبر عمليات النشر.
|
||||
* يتطلب كل حقل `name` و`type` و`label` ومعرّف `universalIdentifier` ثابتًا خاصًا به.
|
||||
* المصفوفة `fields` اختيارية — يمكنك تعريف كائنات بدون حقول مخصصة.
|
||||
* يمكنك إنشاء كائنات جديدة باستخدام `yarn create-entity`، والذي يرشدك خلال التسمية والحقول والعلاقات.
|
||||
* You can scaffold new objects using `yarn app:create-entity`, which guides you through naming, fields, and relationships.
|
||||
|
||||
<Note>
|
||||
**يتم إنشاء الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، يضيف Twenty تلقائيًا حقولًا قياسية مثل `name` و`createdAt` و`updatedAt` و`createdBy` و`position` و`deletedAt`. لا تحتاج إلى تعريف هذه في مصفوفة `fields` — أضف فقط حقولك المخصصة.
|
||||
@@ -448,18 +452,18 @@ export default defineRole({
|
||||
```typescript
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '../../generated';
|
||||
|
||||
const handler = async (
|
||||
params:
|
||||
| { name?: string }
|
||||
| RoutePayload
|
||||
| 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'
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
|
||||
const result = await client.mutation({
|
||||
@@ -496,7 +500,8 @@ export default defineFunction({
|
||||
{
|
||||
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
|
||||
type: 'databaseEvent',
|
||||
eventName: 'person.created',
|
||||
eventName: 'person.updated',
|
||||
updatedFields: ['name'],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -509,23 +514,115 @@ export default defineFunction({
|
||||
> مثال: `path: '/post-card/create',` -> الاستدعاء على `<APP_URL>/s/post-card/create`
|
||||
|
||||
* **cron**: يشغّل وظيفتك على جدول باستخدام تعبير CRON.
|
||||
* **databaseEvent**: يعمل على أحداث دورة حياة كائنات مساحة العمل
|
||||
* **databaseEvent**: Runs on workspace object lifecycle events. When the event operation is `updated`, specific fields to listen to can be specified in the `updatedFields` array. If left undefined or empty, any update will trigger the function.
|
||||
|
||||
> مثال: `person.created`
|
||||
> e.g. `person.updated`
|
||||
|
||||
الملاحظات:
|
||||
|
||||
* المصفوفة `triggers` اختيارية. يمكن استخدام الوظائف بدون مشغلات كوظائف مساعدة تُستدعى بواسطة وظائف أخرى.
|
||||
* يمكنك مزج أنواع متعددة من المشغلات في وظيفة واحدة.
|
||||
|
||||
### Route trigger payload
|
||||
|
||||
<Warning>
|
||||
**Breaking change (v1.16, January 2026):** The route trigger payload format has changed. Prior to v1.16, query parameters, path parameters, and body were sent directly as the payload. Starting with v1.16, they are nested inside a structured `RoutePayload` object.
|
||||
|
||||
**Before v1.16:**
|
||||
|
||||
```typescript
|
||||
const handler = async (params) => {
|
||||
const { param1, param2 } = params; // Direct access
|
||||
};
|
||||
```
|
||||
|
||||
**After v1.16:**
|
||||
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const { param1, param2 } = event.body; // Access via .body
|
||||
const { queryParam } = event.queryStringParameters;
|
||||
const { id } = event.pathParameters;
|
||||
};
|
||||
```
|
||||
|
||||
**To migrate existing functions:** Update your handler to destructure from `event.body`, `event.queryStringParameters`, or `event.pathParameters` instead of directly from the params object.
|
||||
</Warning>
|
||||
|
||||
When a route trigger invokes your function, it receives a `RoutePayload` object that follows the AWS HTTP API v2 format. Import the type from `twenty-sdk`:
|
||||
|
||||
```typescript
|
||||
import { defineFunction, type RoutePayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (event: RoutePayload) => {
|
||||
// Access request data
|
||||
const { headers, queryStringParameters, pathParameters, body } = event;
|
||||
|
||||
// HTTP method and path are available in requestContext
|
||||
const { method, path } = event.requestContext.http;
|
||||
|
||||
return { message: 'Success' };
|
||||
};
|
||||
```
|
||||
|
||||
The `RoutePayload` type has the following structure:
|
||||
|
||||
| Property | النوع | الوصف |
|
||||
| ---------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `headers` | `Record<string, string \| undefined>` | HTTP headers (only those listed in `forwardedRequestHeaders`) |
|
||||
| `queryStringParameters` | `Record<string, string \| undefined>` | Query string parameters (multiple values joined with commas) |
|
||||
| `pathParameters` | `Record<string, string \| undefined>` | Path parameters extracted from the route pattern (e.g., `/users/:id` → `{ id: '123' }`) |
|
||||
| `المحتوى` | `object \| null` | Parsed request body (JSON) |
|
||||
| `isBase64Encoded` | `قيمة منطقية` | Whether the body is base64 encoded |
|
||||
| `requestContext.http.method` | `string` | HTTP method (GET, POST, PUT, PATCH, DELETE) |
|
||||
| `requestContext.http.path` | `string` | Raw request path |
|
||||
|
||||
### Forwarding HTTP headers
|
||||
|
||||
By default, HTTP headers from incoming requests are **not** passed to your serverless function for security reasons. To access specific headers, explicitly list them in the `forwardedRequestHeaders` array:
|
||||
|
||||
```typescript
|
||||
export default defineFunction({
|
||||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||||
name: 'webhook-handler',
|
||||
handler,
|
||||
triggers: [
|
||||
{
|
||||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||||
type: 'route',
|
||||
path: '/webhook',
|
||||
httpMethod: 'POST',
|
||||
isAuthRequired: false,
|
||||
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
In your handler, you can then access these headers:
|
||||
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const signature = event.headers['x-webhook-signature'];
|
||||
const contentType = event.headers['content-type'];
|
||||
|
||||
// Validate webhook signature...
|
||||
return { received: true };
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
Header names are normalized to lowercase. Access them using lowercase keys (for example, `event.headers['content-type']`).
|
||||
</Note>
|
||||
|
||||
يمكنك إنشاء وظائف جديدة بطريقتين:
|
||||
|
||||
* **مُنشأ بالقالب**: شغّل `yarn create-entity` واختر خيار إضافة وظيفة جديدة. يُولّد هذا ملفًا مبدئيًا مع معالج وتكوين.
|
||||
* **Scaffolded**: Run `yarn app:create-entity` and choose the option to add a new function. يُولّد هذا ملفًا مبدئيًا مع معالج وتكوين.
|
||||
* **يدوي**: أنشئ ملفًا جديدًا `*.function.ts` واستخدم `defineFunction()` مع اتباع النمط نفسه.
|
||||
|
||||
### عميل مُولَّد مضبوط الأنواع
|
||||
|
||||
شغّل yarn generate لإنشاء عميل محلي مضبوط الأنواع في generated/ استنادًا إلى مخطط مساحة العمل لديك. استخدمه في وظائفك:
|
||||
Run yarn app:generate to create a local typed client in generated/ based on your workspace schema. استخدمه في وظائفك:
|
||||
|
||||
```typescript
|
||||
import Twenty from './generated';
|
||||
@@ -534,7 +631,7 @@ const client = new Twenty();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
يُعاد توليد العميل بواسطة `yarn generate`. أعِد تشغيله بعد تغيير كائناتك وتشغيل `yarn sync` أو عند الانضمام إلى مساحة عمل جديدة.
|
||||
The client is re-generated by `yarn app:generate`. Re-run after changing your objects and `yarn app:sync` or when onboarding to a new workspace.
|
||||
|
||||
#### بيانات الاعتماد في وقت التشغيل في الوظائف بلا خادم
|
||||
|
||||
@@ -578,13 +675,13 @@ yarn add -D twenty-sdk
|
||||
}
|
||||
```
|
||||
|
||||
يمكنك الآن تشغيل الأوامر نفسها عبر Yarn، مثل `yarn dev` و`yarn sync`، إلخ.
|
||||
Now you can run the same commands via Yarn, e.g. `yarn app:dev`, `yarn app:sync`, etc.
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
* أخطاء المصادقة: شغّل `yarn auth` وتأكد من أن مفتاح واجهة برمجة التطبيقات لديك يمتلك الأذونات المطلوبة.
|
||||
* Authentication errors: run `yarn auth:login` and ensure your API key has the required permissions.
|
||||
* يتعذّر الاتصال بالخادم: تحقق من عنوان URL لواجهة البرمجة وأن خادم Twenty قابل للوصول.
|
||||
* الأنواع أو العميل مفقود/قديم: شغّل `yarn generate` ثم `yarn dev`.
|
||||
* وضع التطوير لا يزامن: تأكد من أن `yarn dev` قيد التشغيل وأن التغييرات ليست متجاهلة من بيئتك.
|
||||
* Types or client missing/outdated: run `yarn app:generate` and then `yarn app:dev`.
|
||||
* Dev mode not syncing: ensure `yarn app:dev` is running and that changes are not ignored by your environment.
|
||||
|
||||
قناة المساعدة على Discord: https://discord.com/channels/1130383047699738754/1130386664812982322
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
---
|
||||
title: الويب هوكس
|
||||
description: Receive real-time notifications when events occur in your CRM.
|
||||
description: استقبل إشعارات في الوقت الفعلي عند وقوع أحداث في نظام إدارة علاقات العملاء (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.
|
||||
تدفع خطافات الويب البيانات إلى أنظمتك في الوقت الفعلي عند وقوع أحداث في Twenty — دون الحاجة إلى الاستطلاع الدوري. استخدمها للحفاظ على تزامن الأنظمة الخارجية، وتشغيل الأتمتة، أو إرسال التنبيهات.
|
||||
|
||||
## إنشاء ربط ويب
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks → Webhooks**
|
||||
1. انتقل إلى **الإعدادات → APIs & Webhooks → Webhooks**
|
||||
2. انقر على **+ إنشاء ربط ويب**
|
||||
3. Enter your webhook URL (must be publicly accessible)
|
||||
3. أدخل عنوان URL لخطاف الويب الخاص بك (يجب أن يكون قابلاً للوصول علنًا)
|
||||
4. انقر على **حفظ**
|
||||
|
||||
The webhook activates immediately and starts sending notifications.
|
||||
يتم تفعيل خطاف الويب فورًا ويبدأ في إرسال الإشعارات.
|
||||
|
||||
<VimeoEmbed videoId="928786708" title="Creating a webhook" />
|
||||
<VimeoEmbed videoId="928786708" title="إنشاء خطاف ويب" />
|
||||
|
||||
### إدارة Webhooks
|
||||
|
||||
**Edit**: Click the webhook → Update URL → **Save**
|
||||
**تحرير**: انقر على خطاف الويب → تحديث عنوان URL → **حفظ**
|
||||
|
||||
**Delete**: Click the webhook → **Delete** → Confirm
|
||||
**حذف**: انقر على خطاف الويب → **حذف** → تأكيد
|
||||
|
||||
## الأحداث
|
||||
|
||||
Twenty sends webhooks for these event types:
|
||||
يرسل Twenty خطافات الويب لأنواع الأحداث التالية:
|
||||
|
||||
| حدث | مثال |
|
||||
| ------------------ | ---------------------------------------------------------- |
|
||||
| **Record Created** | `person.created`, `company.created`, `note.created` |
|
||||
| **Record Updated** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **Record Deleted** | `person.deleted`, `company.deleted` |
|
||||
| حدث | مثال |
|
||||
| --------------- | ---------------------------------------------------------- |
|
||||
| **إنشاء سجل** | `person.created`, `company.created`, `note.created` |
|
||||
| **تحديث السجل** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **حذف السجل** | `person.deleted`, `company.deleted` |
|
||||
|
||||
All event types are sent to your webhook URL. Event filtering may be added in future releases.
|
||||
يتم إرسال جميع أنواع الأحداث إلى عنوان URL لخطاف الويب الخاص بك. قد تتم إضافة تصفية الأحداث في الإصدارات المستقبلية.
|
||||
|
||||
## Payload Format
|
||||
## تنسيق الحمولة
|
||||
|
||||
Each webhook sends an HTTP POST with a JSON body:
|
||||
يرسل كل خطاف ويب طلب HTTP من نوع POST يتضمن جسمًا بصيغة JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -55,35 +55,35 @@ Each webhook sends an HTTP POST with a JSON body:
|
||||
}
|
||||
```
|
||||
|
||||
| الحقل | الوصف |
|
||||
| --------------- | ------------------------------------------------ |
|
||||
| `حدث` | What happened (e.g., `person.created`) |
|
||||
| `بيانات` | The full record that was created/updated/deleted |
|
||||
| `الطابع الزمني` | When the event occurred (UTC) |
|
||||
| الحقل | الوصف |
|
||||
| --------------- | ----------------------------------------------- |
|
||||
| `حدث` | ما الذي حدث (على سبيل المثال، `person.created`) |
|
||||
| `بيانات` | السجل الكامل الذي تم إنشاؤه/تحديثه/حذفه |
|
||||
| `الطابع الزمني` | وقت حدوث الحدث (UTC) |
|
||||
|
||||
<Note>
|
||||
Respond with a **2xx HTTP status** (200-299) to acknowledge receipt. Non-2xx responses are logged as delivery failures.
|
||||
استجب بحالة **HTTP 2xx** (200-299) لتأكيد الاستلام. تُسجَّل الاستجابات غير 2xx كإخفاقات في التسليم.
|
||||
</Note>
|
||||
|
||||
## Webhook Validation
|
||||
## التحقق من صحة خطاف الويب
|
||||
|
||||
Twenty signs each webhook request for security. Validate signatures to ensure requests are authentic.
|
||||
يقوم Twenty بتوقيع كل طلب خطاف ويب لأغراض الأمان. تحقّق من التواقيع للتأكد من أن الطلبات أصيلة.
|
||||
|
||||
### Headers
|
||||
### الرؤوس
|
||||
|
||||
| رأس الصفحة | الوصف |
|
||||
| ---------------------------- | --------------------- |
|
||||
| `X-Twenty-Webhook-Signature` | HMAC SHA256 signature |
|
||||
| `X-Twenty-Webhook-Timestamp` | Request timestamp |
|
||||
| رأس الصفحة | الوصف |
|
||||
| ---------------------------- | ------------------- |
|
||||
| `X-Twenty-Webhook-Signature` | توقيع HMAC SHA256 |
|
||||
| `X-Twenty-Webhook-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`
|
||||
1. احصل على الطابع الزمني من `X-Twenty-Webhook-Timestamp`
|
||||
2. أنشئ السلسلة: `{timestamp}:{JSON payload}`
|
||||
3. احسب HMAC SHA256 باستخدام سر خطاف الويب الخاص بك
|
||||
4. قارِن مع `X-Twenty-Webhook-Signature`
|
||||
|
||||
### Example (Node.js)
|
||||
### مثال (Node.js)
|
||||
|
||||
```javascript
|
||||
const crypto = require("crypto");
|
||||
@@ -101,12 +101,12 @@ const expectedSignature = crypto
|
||||
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 |
|
||||
| طريقة | الاتجاه | حالة الاستخدام |
|
||||
| ----------------------------- | ------- | ----------------------------------------------------------- |
|
||||
| **خطافات الويب** | OUT | إخطار الأنظمة الخارجية تلقائيًا بأي تغيير في السجل |
|
||||
| **سير العمل + طلب HTTP** | OUT | إرسال البيانات إلى الخارج بمنطق مخصص (عوامل تصفية، تحويلات) |
|
||||
| **مشغّل خطاف ويب لسير العمل** | IN | استقبال البيانات في Twenty من الأنظمة الخارجية |
|
||||
|
||||
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).
|
||||
لاستقبال البيانات الخارجية، راجع [إعداد مشغّل خطاف الويب](/l/ar/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Extend
|
||||
description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
|
||||
title: التوسيع
|
||||
description: وسّع وظائف Twenty باستخدام واجهات برمجة التطبيقات، وخطافات الويب، والتطبيقات المخصصة.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -9,26 +9,26 @@ description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty is designed to be extensible. Use our APIs, webhooks, and app framework to integrate with your existing tools and build custom functionality.
|
||||
تم تصميم Twenty ليكون قابلاً للتوسعة. استخدم واجهات برمجة التطبيقات وخطافات الويب وإطار عمل التطبيقات لدينا للتكامل مع أدواتك الحالية وبناء وظائف مخصصة.
|
||||
|
||||
## 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!
|
||||
* **واجهات برمجة التطبيقات**: استعلم وعدّل بيانات إدارة علاقات العملاء (CRM) لديك برمجياً باستخدام REST أو GraphQL
|
||||
* **خطافات الويب**: استقبل إشعارات في الوقت الفعلي عند وقوع أحداث في Twenty
|
||||
* **التطبيقات**: أنشئ تطبيقات مخصصة توسّع قدرات Twenty - قريباً!
|
||||
|
||||
## البدء
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="واجهات برمجة التطبيقات" icon="كود" href="/l/ar/developers/extend/capabilities/apis">
|
||||
Connect to Twenty programmatically
|
||||
اتصل بـ Twenty برمجياً
|
||||
</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 title="التطبيقات" icon="puzzle-piece" href="/l/ar/developers/extend/capabilities/apps">
|
||||
أنشئ تخصيصات كرمز برمجي (ألفا)
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
---
|
||||
title: البدء
|
||||
description: Welcome to Twenty Developer Documentation, your resources for extending, self-hosting, and contributing to Twenty.
|
||||
description: مرحبًا بك في وثائق المطوّرين الخاصة بـ Twenty، مرجعك للتوسيع والاستضافة الذاتية والمساهمة في 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.
|
||||
<CardTitle>التوسيع</CardTitle>
|
||||
أنشئ عمليات تكامل مع واجهات برمجة التطبيقات وخطافات الويب والتطبيقات المخصصة.
|
||||
</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.
|
||||
<CardTitle>الاستضافة الذاتية</CardTitle>
|
||||
قم بنشر Twenty وإدارته على البنية التحتية الخاصة بك.
|
||||
</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.
|
||||
<CardTitle>المساهمة</CardTitle>
|
||||
انضم إلى مجتمعنا مفتوح المصدر وساهم في Twenty.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ title: طرق أخرى
|
||||
|
||||
## Kubernetes عبر Terraform والمخططات
|
||||
|
||||
Community-led documentation for Kubernetes deployment is available [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-docker/k8s)
|
||||
يتوفر توثيق يقوده المجتمع لعملية نشر Kubernetes [هنا](https://github.com/twentyhq/twenty/tree/main/packages/twenty-docker/k8s)
|
||||
|
||||
### Coolify
|
||||
|
||||
@@ -42,4 +42,4 @@ Community-led documentation for Kubernetes deployment is available [here](https:
|
||||
|
||||
## أخرى
|
||||
|
||||
Please feel free to Open a PR to add more Cloud Provider options.
|
||||
لا تتردد في فتح طلب سحب لإضافة المزيد من خيارات موفّري السحابة.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 1-Click w/ Docker Compose
|
||||
title: بنقرة واحدة مع Docker Compose
|
||||
---
|
||||
|
||||
<Warning>
|
||||
@@ -44,7 +44,7 @@ VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.
|
||||
|
||||
1. **إنشاء ملف .env**
|
||||
|
||||
Copy the example environment file to a new .env file in your working directory:
|
||||
انسخ ملف البيئة النموذجي إلى ملف .env جديد في دليل العمل لديك:
|
||||
|
||||
```bash
|
||||
curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example
|
||||
@@ -86,7 +86,7 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/ref
|
||||
|
||||
### الخطوة 3: إطلاق التطبيق
|
||||
|
||||
Start the Docker containers:
|
||||
بدء حاويات Docker:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
@@ -94,9 +94,9 @@ 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).
|
||||
إذا كنت تستضيف twentyCRM على جهازك، فافتح المتصفح وانتقل إلى [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
|
||||
@@ -186,66 +186,66 @@ SSL (HTTPS) مطلوب لعمل ميزات معينة في المتصفح بشك
|
||||
|
||||
* **إعدادات الجدار الناري:**
|
||||
|
||||
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.
|
||||
يستخدم تكوين Docker Compose وحدات التخزين للاحتفاظ بالبيانات لقاعدة البيانات وتخزين الخادم.
|
||||
|
||||
* **البيئات غير المرتبطة بالحالة:**
|
||||
|
||||
إذا كنت تقوم بالنشر إلى بيئة غير مرتبطة بالحالة (مثل بعض خدمات السحابة)، فقم بتكوين التخزين الخارجي لحفظ البيانات.
|
||||
|
||||
## 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`):
|
||||
أضف إلى 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:
|
||||
1. إيقاف التطبيق:
|
||||
|
||||
```bash
|
||||
docker compose stop twenty-server twenty-front
|
||||
```
|
||||
|
||||
2. Restore the database:
|
||||
2. استعادة قاعدة البيانات:
|
||||
|
||||
```bash
|
||||
docker exec -i twenty-postgres psql -U postgres twenty < backup_20240115.sql
|
||||
```
|
||||
|
||||
3. Restart services:
|
||||
3. إعادة تشغيل الخدمات:
|
||||
|
||||
```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
|
||||
* **اختبر عمليات الاستعادة بانتظام** — تحقق من أن النسخ الاحتياطية تعمل فعلاً
|
||||
* **خزّن النسخ الاحتياطية خارج الموقع** — استخدم التخزين السحابي (S3، GCS، إلخ.)
|
||||
* **شفّر البيانات الحساسة** — قم بحماية النسخ الاحتياطية بالتشفير
|
||||
* **احتفِظ بنسخ متعددة** — احتفِظ بنسخ احتياطية يومية وأسبوعية وشهرية
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
|
||||
@@ -60,61 +60,61 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=false
|
||||
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.
|
||||
افتراضيًا، تعمل Twenty في **وضع مساحة عمل واحدة** — وهو مثالي لمعظم عمليات النشر المستضافة ذاتيًا حيث تحتاج إلى مثيل واحد من نظام إدارة علاقات العملاء لمؤسستك.
|
||||
|
||||
### 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`
|
||||
* مساحة عمل واحدة لكل مثيل من Twenty
|
||||
* أول مستخدم يصبح تلقائيًا مشرفًا مع صلاحيات كاملة (`canImpersonate` و`canAccessFullAdminPanel`)
|
||||
* يتم تعطيل عمليات التسجيل الجديدة بعد إنشاء مساحة العمل الأولى
|
||||
* بنية URL بسيطة: `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.
|
||||
قم بتمكين وضع تعدد مساحات العمل لعمليات النشر المشابهة لـ SaaS حيث تحتاج عدة فرق مستقلة إلى مساحات عمل خاصة بها على نفس مثيل Twenty.
|
||||
|
||||
**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
|
||||
* يمكن إنشاء مساحات عمل متعددة على نفس المثيل
|
||||
* تحصل كل مساحة عمل على نطاق فرعي خاص بها (مثل: `sales.your-domain.com`، `marketing.your-domain.com`)
|
||||
* يسجل المستخدمون ويقومون بتسجيل الدخول عبر `{DEFAULT_SUBDOMAIN}.your-domain.com` (مثل: `app.your-domain.com`)
|
||||
* لا توجد صلاحيات مشرف تلقائية — أول مستخدم في كل مساحة عمل يكون مستخدمًا عاديًا
|
||||
* إعدادات خاصة بمساحة العمل مثل النطاق الفرعي والنطاق المخصص تصبح متاحة ضمن إعدادات مساحة العمل
|
||||
|
||||
<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.
|
||||
**إعداد خاص بالبيئة فقط:** لا يمكن تكوين `IS_MULTIWORKSPACE_ENABLED` إلا عبر ملف `.env` ويتطلب إعادة تشغيل. لا يمكن تغييره عبر لوحة الإدارة.
|
||||
</Warning>
|
||||
|
||||
### DNS Configuration for Multi-Workspace
|
||||
### تكوين DNS لوضع تعدد مساحات العمل
|
||||
|
||||
When using multi-workspace mode, configure your DNS with a wildcard record to allow dynamic subdomain creation:
|
||||
عند استخدام وضع تعدد مساحات العمل، قم بتكوين DNS لديك بسجل wildcard للسماح بإنشاء النطاقات الفرعية ديناميكيًا:
|
||||
|
||||
```
|
||||
*.your-domain.com -> your-server-ip
|
||||
```
|
||||
|
||||
This enables automatic subdomain routing for new workspaces without manual DNS configuration.
|
||||
يُمكّن هذا توجيه النطاقات الفرعية تلقائيًا لمساحات العمل الجديدة دون تكوين DNS يدوي.
|
||||
|
||||
### 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.
|
||||
عند التمكين، يمكن فقط للمستخدمين الذين لديهم `canAccessFullAdminPanel` إنشاء مساحات عمل إضافية. لا يزال بإمكان المستخدمين إنشاء مساحة العمل الأولى الخاصة بهم أثناء التسجيل الأولي.
|
||||
|
||||
## تكامل Gmail و Google Calendar
|
||||
|
||||
@@ -133,8 +133,8 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
|
||||
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)
|
||||
* `https://{your-domain}/auth/google/redirect` (لتسجيل الدخول الفردي)
|
||||
* `https://{your-domain}/auth/google-apis/get-access-token` (للتكاملات)
|
||||
|
||||
### الإعداد في Twenty
|
||||
|
||||
@@ -194,13 +194,13 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
|
||||
|
||||
ستحتاج إلى إضافة URIs التالية لإعادة التوجيه إلى مشروعك:
|
||||
|
||||
* `https://{your-domain}/auth/microsoft/redirect` if you want to use Microsoft SSO
|
||||
* `https://{your-domain}/auth/microsoft/redirect` إذا كنت تريد استخدام تسجيل الدخول الفردي في Microsoft
|
||||
* `https://{your-domain}/auth/microsoft-apis/get-access-token`
|
||||
|
||||
### الإعداد في Twenty
|
||||
|
||||
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
|
||||
2. Find the **Microsoft Auth** section
|
||||
2. ابحث عن قسم **Microsoft Auth**
|
||||
3. حدد هذه المتغيرات:
|
||||
* `MESSAGING_PROVIDER_MICROSOFT_ENABLED=true`
|
||||
* `CALENDAR_PROVIDER_MICROSOFT_ENABLED=true`
|
||||
@@ -214,7 +214,7 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `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)
|
||||
|
||||
@@ -232,7 +232,7 @@ When enabled, only users with `canAccessFullAdminPanel` can create additional wo
|
||||
|
||||
أضف مستخدمي الاختبار إلى قسم "المستخدمون والمجموعات".
|
||||
|
||||
## Background Jobs for Calendar & Messaging
|
||||
## مهام الخلفية للتقويم والمراسلة
|
||||
|
||||
بعد إعداد تكامل Gmail، أو Google Calendar، أو Microsoft 365، تحتاج إلى بدء وظائف الخلفية التي تقوم بمزامنة البيانات.
|
||||
|
||||
@@ -252,7 +252,7 @@ yarn command:prod cron:workflow:automated-cron-trigger
|
||||
## تكوين البريد الإلكتروني
|
||||
|
||||
1. اذهب إلى **الإعدادات → لوحة الإدارة → متغيرات التكوين**
|
||||
2. Find the **Email** section
|
||||
2. ابحث عن قسم **Email**
|
||||
3. قم بضبط إعدادات SMTP الخاصة بك:
|
||||
|
||||
<ArticleTabs label1="جيميل" label2="أوفيس 365" label3="Smtp4dev">
|
||||
@@ -291,3 +291,47 @@ yarn command:prod cron:workflow:automated-cron-trigger
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
## Serverless Functions
|
||||
|
||||
Twenty supports serverless functions for workflows and custom logic. The execution environment is configured via the `SERVERLESS_TYPE` environment variable.
|
||||
|
||||
<Warning>
|
||||
**Security Notice:** The local serverless driver (`SERVERLESS_TYPE=LOCAL`) runs code directly on the host in a Node.js process with no sandboxing. It should only be used for trusted code in development. For production deployments handling untrusted code, we highly recommend using `SERVERLESS_TYPE=LAMBDA` or `SERVERLESS_TYPE=DISABLED`.
|
||||
</Warning>
|
||||
|
||||
### Available Drivers
|
||||
|
||||
| Driver | Environment Variable | حالة الاستخدام | Security Level |
|
||||
| -------- | -------------------------- | ------------------------------------- | ------------------------------- |
|
||||
| Disabled | `SERVERLESS_TYPE=DISABLED` | Disable serverless functions entirely | N/A |
|
||||
| Local | `SERVERLESS_TYPE=LOCAL` | Development and trusted environments | Low (no sandboxing) |
|
||||
| Lambda | `SERVERLESS_TYPE=LAMBDA` | Production with untrusted code | High (hardware-level isolation) |
|
||||
|
||||
### Recommended Configuration
|
||||
|
||||
**For development:**
|
||||
|
||||
```bash
|
||||
SERVERLESS_TYPE=LOCAL # default
|
||||
```
|
||||
|
||||
**For production (AWS):**
|
||||
|
||||
```bash
|
||||
SERVERLESS_TYPE=LAMBDA
|
||||
SERVERLESS_LAMBDA_REGION=us-east-1
|
||||
SERVERLESS_LAMBDA_ROLE=arn:aws:iam::123456789:role/your-lambda-role
|
||||
SERVERLESS_LAMBDA_ACCESS_KEY_ID=your-access-key
|
||||
SERVERLESS_LAMBDA_SECRET_ACCESS_KEY=your-secret-key
|
||||
```
|
||||
|
||||
**To disable serverless functions:**
|
||||
|
||||
```bash
|
||||
SERVERLESS_TYPE=DISABLED
|
||||
```
|
||||
|
||||
<Note>
|
||||
When using `SERVERLESS_TYPE=DISABLED`, any attempt to execute a serverless function will return an error. This is useful if you want to run Twenty without serverless function capabilities.
|
||||
</Note>
|
||||
|
||||
@@ -37,7 +37,7 @@ git config --global core.autocrlf false
|
||||
|
||||
ثم قم بحذف المستودع واستنساخه مرة أخرى.
|
||||
|
||||
#### Missing metadata schema
|
||||
#### مخطط البيانات الوصفية مفقود
|
||||
|
||||
أثناء تثبيت Twenty، تحتاج إلى توفير قاعدة بيانات بوستجريس الخاصة بك بالمخططات والإضافات والمستخدمين الصحيحة.
|
||||
إذا نجح تشغيل هذا التخصيص، يجب أن تحتوي قاعدة البيانات لديك على المخططات `default` و`metadata`.
|
||||
@@ -47,7 +47,7 @@ git config --global core.autocrlf false
|
||||
|
||||
عليك بناء حزمة `twenty-emails` قبل تشغيل تهيئة قاعدة البيانات باستخدام `npx nx run twenty-emails:build`.
|
||||
|
||||
#### Missing twenty-x package
|
||||
#### حزمة twenty-x مفقودة
|
||||
|
||||
تأكد من تشغيل yarn في الدليل الجذر ثم تشغيل `npx nx server:dev twenty-server`. إذا لم يعمل ذلك، حاول بناء الحزمة المفقودة يدوياً.
|
||||
|
||||
@@ -67,11 +67,11 @@ git config --global core.autocrlf false
|
||||
|
||||
في `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` فقط
|
||||
**إذا لم يعمل ذلك:**
|
||||
قم بتشغيل الخدمات التي تحتاجها فقط، بدلاً من `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:
|
||||
**إذا لم يعمل ذلك:**
|
||||
إذا حاولت تشغيل `npx nx run twenty-server:start` فقط على WSL وفشل مع خطأ الذاكرة أدناه:
|
||||
|
||||
`FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`
|
||||
|
||||
@@ -82,11 +82,11 @@ If you tried to run only `npx nx run twenty-server:start` on WSL and it's failin
|
||||
علامة --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 كانت تستهلك الكثير من الذاكرة لذا قمنا بتعطيلها مؤقتًا.
|
||||
**إذا لم يعمل ذلك:**
|
||||
تحقق من العمليات التي تستهلك معظم ذاكرة جهازك. في Twenty، لاحظنا أن بعض إضافات VScode كانت تستهلك الكثير من الذاكرة لذا قمنا بتعطيلها مؤقتًا.
|
||||
|
||||
**If it does not work:**
|
||||
Restart your machine helps to clean up ghost processes.
|
||||
**إذا لم يعمل ذلك:**
|
||||
إعادة تشغيل جهازك تساعد في تنظيف العمليات الشبحية.
|
||||
|
||||
#### أثناء تشغيل `npx nx start` تظهر سجلات غريبة [0] و [1]
|
||||
|
||||
@@ -124,7 +124,7 @@ npx nx worker twenty-server
|
||||
|
||||
* افتح تطبيق Twenty الخاص بك، انتقل إلى `/settings`، وفعل التبديل المتقدم في الجزء السفلي الأيسر من الشاشة.
|
||||
* إنشاء ويب هوك جديد.
|
||||
* Paste **Your Unique Webhook URL** in the **Endpoint Url** field in Twenty. Set the **Filters** to `Companies` and `Created`.
|
||||
* الصق **عنوان URL الفريد لخطّاف الويب الخاص بك** في الحقل **عنوان URL لنقطة النهاية** في Twenty. اضبط **عوامل التصفية** على `Companies` و`Created`.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/developers/self-hosting/webhook-settings.jpg" alt="إعدادات الويب هوك" />
|
||||
|
||||
@@ -4,9 +4,9 @@ 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`.
|
||||
**تأكد دائماً من عمل نسخة احتياطية لقاعدة بياناتك قبل بدء عملية الترقية** عن طريق تشغيل `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}`.
|
||||
لاستعادة النسخة الاحتياطية، قم بتشغيل `cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}`.
|
||||
|
||||
إذا كنت تستخدم Docker Compose، اتبع الخطوات التالية:
|
||||
|
||||
@@ -46,7 +46,7 @@ yarn command:prod cache:flush
|
||||
|
||||
لم تعد بحاجة إلى تشغيل أي أمر، الصورة الجديدة ستعتني بتشغيل جميع الترحيلات المطلوبة تلقائيًا.
|
||||
|
||||
### `User does not have permission` error
|
||||
### خطأ: `User does not have permission`
|
||||
|
||||
إذا واجهت أخطاء في الأذونات في معظم الطلبات بعد الترقية، فقد تحتاج إلى مسح التخزين المؤقت لإعادة حساب أحدث الأذونات.
|
||||
|
||||
@@ -316,8 +316,8 @@ yarn command:prod upgrade-0.31
|
||||
|
||||
قم بترقية مثيل 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) لتعكس ذلك.
|
||||
**تغيير كبير**:
|
||||
لتحسين الأداء، يتطلب Twenty الآن تكوين Redis للتخزين المؤقت. قمنا بتحديث [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml) لتعكس ذلك.
|
||||
تأكد من تحديث إعدادات التكوين الخاصة بك وتحديث المتغيرات البيئية الخاصة بك وفقًا لذلك:
|
||||
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Self-Host
|
||||
description: Deploy and manage Twenty on your own infrastructure.
|
||||
title: الاستضافة الذاتية
|
||||
description: قم بنشر Twenty وإدارته على البنية التحتية الخاصة بك.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -9,22 +9,22 @@ description: Deploy and manage Twenty on your own infrastructure.
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
Twenty can be self-hosted on your own infrastructure, giving you full control over your data and deployment.
|
||||
يمكن استضافة Twenty ذاتيًا على البنية التحتية الخاصة بك، مما يمنحك تحكمًا كاملاً في بياناتك وعمليات النشر.
|
||||
|
||||
## 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
|
||||
* **ملكية البيانات**: احتفظ بجميع بيانات إدارة علاقات العملاء (CRM) على خوادمك الخاصة
|
||||
* **الامتثال**: تلبية المتطلبات التنظيمية الخاصة بإقامة البيانات
|
||||
* **التخصيص**: وصول كامل إلى تعديل المنصة وتوسيعها
|
||||
|
||||
## البدء
|
||||
|
||||
<CardGroup cols={٢}>
|
||||
<Card title="Docker Compose" icon="docker" href="/l/ar/developers/self-host/capabilities/docker-compose">
|
||||
Quick setup with Docker
|
||||
إعداد سريع باستخدام Docker
|
||||
</Card>
|
||||
|
||||
<Card title="Cloud Providers" icon="cloud" href="/l/ar/developers/self-host/capabilities/cloud-providers">
|
||||
Deploy on AWS, GCP, or Azure
|
||||
<Card title="مزودو الخدمات السحابية" icon="cloud" href="/l/ar/developers/self-host/capabilities/cloud-providers">
|
||||
انشر على AWS أو GCP أو Azure
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"tabs": {
|
||||
"userGuide": {
|
||||
"label": "User Guide",
|
||||
"label": "دليل المستخدم",
|
||||
"groups": {
|
||||
"discoverTwenty": {
|
||||
"label": "Discover Twenty",
|
||||
"label": "اكتشف Twenty",
|
||||
"groups": {
|
||||
"gettingStartedCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"gettingStartedHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18,32 +18,32 @@
|
||||
"label": "نموذج البيانات",
|
||||
"groups": {
|
||||
"dataModelCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"dataModelHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataMigration": {
|
||||
"label": "Data Migration",
|
||||
"label": "ترحيل البيانات",
|
||||
"groups": {
|
||||
"dataMigrationCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"dataMigrationHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"calendarEmails": {
|
||||
"label": "Calendar & Emails",
|
||||
"label": "التقويم والبريد الإلكتروني",
|
||||
"groups": {
|
||||
"calendarEmailsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"calendarEmailsHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -51,22 +51,22 @@
|
||||
"label": "سير العمل",
|
||||
"groups": {
|
||||
"workflowsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"workflowsHowTos": {
|
||||
"label": "How-Tos",
|
||||
"label": "الإرشادات",
|
||||
"groups": {
|
||||
"crmAutomations": {
|
||||
"label": "CRM Automations"
|
||||
"label": "أتمتة إدارة علاقات العملاء"
|
||||
},
|
||||
"connectToOtherTools": {
|
||||
"label": "Connect to Other Tools"
|
||||
"label": "الاتصال بأدوات أخرى"
|
||||
},
|
||||
"advancedConfigurations": {
|
||||
"label": "Advanced Configurations"
|
||||
"label": "التكوينات المتقدمة"
|
||||
},
|
||||
"needMoreHelp": {
|
||||
"label": "Need More Help"
|
||||
"label": "المزيد من المساعدة"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,21 +76,21 @@
|
||||
"label": "الذكاء الاصطناعي",
|
||||
"groups": {
|
||||
"aiCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"aiHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewsPipelines": {
|
||||
"label": "Views & Pipelines",
|
||||
"label": "طرق العرض والمسارات",
|
||||
"groups": {
|
||||
"viewsPipelinesCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"viewsPipelinesHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -98,21 +98,21 @@
|
||||
"label": "لوحات القيادة",
|
||||
"groups": {
|
||||
"dashboardsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"dashboardsHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"permissionsAccess": {
|
||||
"label": "Permissions & Access",
|
||||
"label": "الصلاحيات والوصول",
|
||||
"groups": {
|
||||
"permissionsAccessCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"permissionsAccessHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -120,10 +120,10 @@
|
||||
"label": "الفوترة",
|
||||
"groups": {
|
||||
"billingCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"billingHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -131,10 +131,10 @@
|
||||
"label": "\\ا\\ل\\إ\\ع\\د\\ا\\د\\ا\\ت",
|
||||
"groups": {
|
||||
"settingsCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
},
|
||||
"settingsHowTos": {
|
||||
"label": "How-Tos"
|
||||
"label": "الإرشادات"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,26 +147,26 @@
|
||||
"label": "المطورون"
|
||||
},
|
||||
"extend": {
|
||||
"label": "Extend",
|
||||
"label": "التوسيع",
|
||||
"groups": {
|
||||
"extendCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selfHost": {
|
||||
"label": "Self-Host",
|
||||
"label": "الاستضافة الذاتية",
|
||||
"groups": {
|
||||
"selfHostCapabilities": {
|
||||
"label": "Capabilities"
|
||||
"label": "القدرات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contribute": {
|
||||
"label": "Contribute",
|
||||
"label": "المساهمة",
|
||||
"groups": {
|
||||
"contributeCapabilities": {
|
||||
"label": "Capabilities",
|
||||
"label": "القدرات",
|
||||
"groups": {
|
||||
"frontendDevelopment": {
|
||||
"label": "تطوير الواجهة الأمامية",
|
||||
@@ -184,7 +184,7 @@
|
||||
"label": "إدخال"
|
||||
},
|
||||
"navigation": {
|
||||
"label": "Navigation"
|
||||
"label": "التنقل"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ image: /images/user-guide/tips/light-bulb.png
|
||||
return (
|
||||
<>
|
||||
<p id="hoverText" style={{ display: "inline-block" }}>
|
||||
Customer Insights
|
||||
رؤى العملاء
|
||||
</p>
|
||||
<AppTooltip
|
||||
className
|
||||
anchorSelect="#hoverText"
|
||||
content="Explore customer behavior and preferences"
|
||||
content="استكشاف سلوك العملاء وتفضيلاتهم"
|
||||
delayHide={0}
|
||||
offset={6}
|
||||
noArrow={false}
|
||||
@@ -38,21 +38,21 @@ image: /images/user-guide/tips/light-bulb.png
|
||||
</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` |
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
|
||||
| اختيار الربط | محدد CSS | المحدِّد لمرساة التلميح (العنصر الذي يُفعّل التلميح) |
|
||||
| المحتوى | نص | المحتوى الذي تريد عرضه داخل التلميح |
|
||||
| تأخير الإخفاء | رقم | التأخير بالثواني قبل إخفاء التلميح بعد مغادرة المؤشر للمرساة |
|
||||
| الإزاحة | رقم | الإزاحة بالبكسل لتحديد موضع التلميح |
|
||||
| بدون سهم | قيمة منطقية | إذا كانت القيمة `صحيح`, سيتم إخفاء السهم في المربط التنبيهي |
|
||||
| مفتوح | قيمة منطقية | إذا كانت القيمة `صحيح`, يكون المربط التنبيهي مفتوحًا افتراضيًا |
|
||||
| المكان | نص `PlacesType` من `react-tooltip` | يحدد موضع المربط التنبيهي. تتضمن القيم `bottom`، `left`، `right`، `top`، `top-start`، `top-end`، `right-start`، `right-end`، `bottom-start`، `bottom-end`، `left-start`، و`left-end` |
|
||||
| استراتيجية الوضعية | نص `PositionStrategy` من `react-tooltip` | استراتيجية وضعية للمربط التنبيهي. له قيمتان: `absolute` و`fixed` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Overflowing Text with Tooltip
|
||||
## نص متجاوز مع تلميح
|
||||
|
||||
يعالج النص الزائد ويعرض مربط تنبيهي عند فيضان النص.
|
||||
|
||||
@@ -63,7 +63,7 @@ image: /images/user-guide/tips/light-bulb.png
|
||||
|
||||
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.';
|
||||
'المتابعة مع العميل بشأن استفساره الأخير عن المنتج. مناقشة خيارات التسعير، ومعالجة أي مخاوف، وتقديم معلومات إضافية عن المنتج. تسجيل تفاصيل المحادثة في نظام إدارة علاقات العملاء (CRM) للرجوع إليها لاحقاً.';
|
||||
|
||||
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
|
||||
};
|
||||
@@ -71,8 +71,8 @@ image: /images/user-guide/tips/light-bulb.png
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------- | ------ | ------------------------------------------------------------ |
|
||||
| نص | string | The content you want to display in the overflowing text area |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------- | ------ | --------------------------------------------- |
|
||||
| نص | string | المحتوى الذي تريد عرضه في منطقة النص المتجاوز |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -51,8 +51,8 @@ image: /images/user-guide/tasks/tasks_header.png
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | ------------------------------------- | ----------------- |
|
||||
| isAnimating | قيمة منطقية | يتحكم فيما إذا كانت علامة صحيح متحركة | خاطئ |
|
||||
| اللون | string | Color of the checkmark | |
|
||||
| اللون | string | لون علامة الاختيار | |
|
||||
| المدة | رقم | مدة الحركة بالثواني | 0.5 ثانية |
|
||||
| الحجم | رقم | The size of the checkmark | 28 بكسل |
|
||||
| الحجم | رقم | حجم علامة الاختيار | 28 بكسل |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -9,7 +9,7 @@ image: /images/user-guide/objects/objects.png
|
||||
|
||||
قائمة بالأيقونات المستخدمة في جميع أنحاء تطبيقنا.
|
||||
|
||||
## Tabler Icons
|
||||
## أيقونات Tabler
|
||||
|
||||
نستخدم أيقونات Tabler لـ React في جميع أنحاء التطبيق.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Soon Pill
|
||||
title: شارة قريبًا
|
||||
image: /images/user-guide/kanban-views/kanban.png
|
||||
---
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ image: /images/user-guide/table-views/table.png
|
||||
<img src="/images/user-guide/table-views/table.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
Component to visually categorize or label content.
|
||||
مكوّن لتصنيف المحتوى أو وسمه بصريًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
|
||||
@@ -38,20 +38,20 @@ image: /images/user-guide/views/filter.png
|
||||
</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` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون رمز اختياري يُعرض داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| عرض كامل | قيمة منطقية | يُحدد إذا كان الزر يجب أن يمتد ليغطي العرض الكامل للحاوية الخاصة به |
|
||||
| التنوع | string | النمط المرئي للزر. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `default`، `blue`، `danger` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -116,13 +116,13 @@ image: /images/user-guide/views/filter.png
|
||||
</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 تمثل الأزرار الفردية داخل المجموعة |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | --------- | -------------------------------------------------------------------------------------- |
|
||||
| التنوع | string | النمط المرئي للأزرار داخل المجموعة. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
|
||||
| الحجم | string | حجم الأزرار داخل المجموعة. يوجد خياران: `medium` و`small`. |
|
||||
| accent | نص | لون تمييز الأزرار داخل المجموعة. تشمل الخيارات `default`، `blue` و`danger`. |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -153,17 +153,17 @@ image: /images/user-guide/views/filter.png
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `middle`, `right` |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | --------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `middle`، `right`. |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -242,18 +242,18 @@ image: /images/user-guide/views/filter.png
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | -------------------------------------------------------------------------------------- |
|
||||
| className | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | نص | موقع الزر بالنسبة لأخوته. Options include: `standalone`, `left`, `right`, and `middle` |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | نص | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -299,7 +299,7 @@ image: /images/user-guide/views/filter.png
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Light Button
|
||||
## زر خفيف
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
@@ -31,14 +31,14 @@ image: /images/user-guide/tasks/tasks_header.png
|
||||
</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` |
|
||||
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------- | ----------------------------------------------------------------------------- |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار محددًا |
|
||||
| غير محدد | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار في حالة غير محددة (لا هو محدد ولا غير محدد) |
|
||||
| عند التغيير | دالة | الدالة التي ترغب في تفعيلها عند تغيير حالة مربع الاختيار |
|
||||
| عند تغيير الحالة المحددة | دالة | الدالة التي ترغب في تفعيلها عند تغيّر حالة `checked` |
|
||||
| نموذج | نص | النمط البصري للصندوق. تتضمن الخيارات: 'أساسي'، 'ثانوي'، و 'ثالثي' |
|
||||
| الحجم | نص | حجم مربع الاختيار. له خياران: `small` و `large` |
|
||||
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -55,9 +55,9 @@ image: /images/user-guide/fields/field.png
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ----------- | ------------------- | ---------------------------------------------------------------------------- |
|
||||
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
|
||||
| عند التغيير | function | The callback function you want to trigger when a user selects a color scheme |
|
||||
| خصائص | النوع | الوصف |
|
||||
| ----------- | ------------------- | ------------------------------------------------------------- |
|
||||
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
|
||||
| عند التغيير | function | الدالة التي ترغب في تفعيلها عندما يختار المستخدم نظام الألوان |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -47,6 +47,6 @@ image: /images/user-guide/github/github-header.png
|
||||
| النقر بالخارج | دالة | الدالة الارتجاعية التي تُفعل عندما ينقر المستخدم خارج القائمة المنسدلة |
|
||||
| عند الإغلاق | دالة | الدالة الارتجاعية التي تُفعل عند إغلاق القائمة المنسدلة |
|
||||
| عند الفتح | دالة | الدالة الارتجاعية التي تُفعل عند فتح القائمة المنسدلة |
|
||||
| التنوع | نص | The visual style variant of the clickable icon. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
|
||||
| التنوع | نص | متغير النمط البصري للأيقونة القابلة للنقر. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -21,14 +21,14 @@ image: /images/user-guide/objects/objects.png
|
||||
</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 |
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------ | ----------- | --------------------------------------------------------------------------------- |
|
||||
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
|
||||
| onUpload | دالة | الدالة التي تُستدعى عند قيام المستخدم بتحميل صورة جديدة. تستقبل كائن `File` كوسيط |
|
||||
| onRemove | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإزالة |
|
||||
| onAbort | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإلغاء أثناء تحميل الصورة |
|
||||
| isUploading | قيمة منطقية | يشير إلى ما إذا كان يتم تحميل صورة حاليًا |
|
||||
| errorMessage | نص | رسالة خطأ اختيارية لعرضها أسفل حقل إدخال الصورة |
|
||||
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون حقل الإدخال بأكمله معطلاً، ولن تكون الأزرار قابلة للنقر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -37,22 +37,22 @@ image: /images/user-guide/create-workspace/workspace-cover.png
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
},{
|
||||
```
|
||||
</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` |
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------------- | ------------------------------------------------------------------ |
|
||||
| النمط | خصائص `React.CSS` | أنماط إضافية مضمنة للمكون |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية لتصميم إضافي |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان زر الراديو محددًا |
|
||||
| القيمة | نص | التسمية أو النص المرتبط بزر الراديو |
|
||||
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير زر الاختيار المحدد. |
|
||||
| عند تغيير الحالة المحددة | دالة | الدالة التي تُستدعى عند تغيير حالة `checked` لزر الاختيار. |
|
||||
| الحجم | نص | حجم زر الراديو. تشمل الخيارات: `large` و `small` |
|
||||
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون زر الاختيار معطلاً وغير قابل للنقر |
|
||||
| موضع التسمية | نص | موضع نص التسمية بالنسبة لزر الراديو. يوجد خياران: `left` و `right` |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -87,11 +87,11 @@ image: /images/user-guide/create-workspace/workspace-cover.png
|
||||
</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 |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------- | ----------------- | --------------------------------------------------------------------- |
|
||||
| القيمة | string | قيمة زر الراديو المحدد حاليًا |
|
||||
| عند التغيير | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير زر الاختيار. |
|
||||
| onValueChange | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير القيمة المحددة في المجموعة. |
|
||||
| الأبناء | `React.ReactNode` | يتيح لك تمرير مكوّنات React (مثل Radio) كعناصر فرعية إلى Radio Group |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -43,8 +43,8 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
|
||||
| معطل | قيمة منطقية | عند ضبطها على `true`، يتم تعطيل تفاعل المستخدم مع المكون |
|
||||
| التسمية | نص | The label to describe the purpose of the `Select` component |
|
||||
| عند التغيير | دالة | The function called when the selected values change |
|
||||
| التسمية | نص | التسمية التي تصف غرض مكوّن `Select` |
|
||||
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير القيم المحددة |
|
||||
| خيارات | مصفوفة | تمثل الخيارات المتاحة في مكون `الاختيار`. إنها مصفوفة من الكائنات حيث يحتوي كل كائن على `قيمة` (معرف فريد)، `تسمية` (معرف فريد)، و`أيقونة` اختيارية |
|
||||
| القيمة | نص | تمثل القيمة المحددة حاليًا. يجب أن تطابق إحدى خصائص `القيمة` في مصفوفة `الخيارات` |
|
||||
</Tab>
|
||||
|
||||
@@ -19,29 +19,29 @@ image: /images/user-guide/notes/notes_header.png
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("Input changed:", text);
|
||||
console.log("تم تغيير الإدخال:", text);
|
||||
};
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("Key pressed:", event.key);
|
||||
console.log("تم ضغط المفتاح:", event.key);
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<TextInput
|
||||
className
|
||||
label="Username"
|
||||
label="اسم المستخدم"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="Invalid username"
|
||||
error="اسم مستخدم غير صالح"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
},{
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -75,17 +75,17 @@ image: /images/user-guide/notes/notes_header.png
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("onValidate function fired")}
|
||||
onValidate={() => console.log("تم تشغيل الدالة onValidate")}
|
||||
minRows={1}
|
||||
placeholder="Write a comment"
|
||||
onFocus={() => console.log("onFocus function fired")}
|
||||
placeholder="اكتب تعليقًا"
|
||||
onFocus={() => console.log("تم تشغيل الدالة onFocus")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="Task: "
|
||||
value="المهمة: "
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
};},{
|
||||
```
|
||||
</Tab>
|
||||
|
||||
@@ -116,8 +116,8 @@ image: /images/user-guide/notes/notes_header.png
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('On change function fired')}
|
||||
placeholder="Enter text here"
|
||||
onChange={()=>console.log('تم تشغيل الدالة onChange')}
|
||||
placeholder="أدخل النص هنا"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ image: /images/user-guide/table-views/table.png
|
||||
return (
|
||||
<Toggle
|
||||
value = {true}
|
||||
onChange = {()=>console.log('On Change event')}
|
||||
onChange = {()=>console.log('تم تشغيل حدث onChange')}
|
||||
color="green"
|
||||
toggleSize = "medium"
|
||||
/>
|
||||
@@ -28,8 +28,8 @@ image: /images/user-guide/table-views/table.png
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| القيمة | قيمة منطقية | The current state of the toggle | `خاطئ` |
|
||||
| عند التغيير | دالة | Callback function triggered when the toggle state changes | |
|
||||
| القيمة | قيمة منطقية | الحالة الحالية لمفتاح التبديل | `خاطئ` |
|
||||
| عند التغيير | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل | |
|
||||
| اللون | string | لون التبديل عند كونه | لون أزرق |
|
||||
| حجم التبديل | نص | حجم التبديل الذي يؤثر على كل من الطول والوزن. لديها خياران: `صغير` و`متوسط` | متوسط |
|
||||
</Tab>
|
||||
|
||||
@@ -9,22 +9,22 @@ 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
|
||||
<CardTitle>العرض</CardTitle>
|
||||
مكونات العرض لعرض المعلومات بصريًا
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/twenty-ui/progress-bar" img="/images/user-guide/fields/field.png">
|
||||
<CardTitle>Feedback</CardTitle>
|
||||
Feedback components for user notifications
|
||||
<CardTitle>التغذية الراجعة</CardTitle>
|
||||
مكونات التغذية الراجعة لإبلاغ المستخدمين
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/twenty-ui/input" img="/images/user-guide/tips/light-bulb.png">
|
||||
<CardTitle>Input</CardTitle>
|
||||
Input components for user interaction
|
||||
<CardTitle>الإدخال</CardTitle>
|
||||
مكونات الإدخال لتفاعل المستخدم
|
||||
</Card>
|
||||
|
||||
<Card href="/l/ar/twenty-ui/navigation" img="/images/user-guide/tasks/tasks_header.png">
|
||||
<CardTitle>Navigation</CardTitle>
|
||||
Navigation components for user interface
|
||||
<CardTitle>التنقل</CardTitle>
|
||||
مكونات التنقل لواجهة المستخدم
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Navigation
|
||||
title: التنقل
|
||||
image: /images/user-guide/tasks/tasks_header.png
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Breadcrumb
|
||||
title: مسار التنقّل
|
||||
image: /images/user-guide/fields/field.png
|
||||
---
|
||||
|
||||
@@ -7,7 +7,7 @@ image: /images/user-guide/fields/field.png
|
||||
<img src="/images/user-guide/fields/field.png" alt="رأس الصفحة" />
|
||||
</Frame>
|
||||
|
||||
Renders a breadcrumb navigation bar.
|
||||
يعرض شريط مسار التنقّل.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
@@ -33,9 +33,9 @@ Renders a breadcrumb navigation bar.
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| روابط | مصفوفة | An array of objects, each representing a breadcrumb link. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| روابط | مصفوفة | مصفوفة من الكائنات، يمثّل كلٌّ منها رابطًا في مسار التنقّل. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -20,7 +20,7 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('Contact link clicked!', event);
|
||||
console.log('تم النقر على رابط الاتصال!', event);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -34,7 +34,7 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
};},{
|
||||
```
|
||||
</Tab>
|
||||
|
||||
|
||||
@@ -42,16 +42,16 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
</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 | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------------- | ------------- | ---------------------------------------------------------------------------- |
|
||||
| أيقونة اليسار | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | يحدد لون الإبراز لعنصر القائمة. تشمل الخيارات: `افتراضي`, `خطر`, `موضع مؤقت` |
|
||||
| نص | نص | المحتوى النصي لعنصر القائمة |
|
||||
| أزرار الأيقونة | array | مجموعة من الكائنات التي تمثل أيقونات إضافية مرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
|
||||
| معرف الفحص | نص | السمة data-testid لأغراض الاختبار |
|
||||
| عند النقر | function | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -90,15 +90,15 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | -------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
|
||||
| secondHotKey | string | The second keyboard shortcut associated with the command |
|
||||
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
|
||||
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ----------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
|
||||
| secondHotKey | string | اختصار لوحة المفاتيح الثاني المرتبط بالأمر |
|
||||
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
|
||||
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -135,16 +135,16 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
</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 | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | لون العنصر لهجة القائمة. يمكن أن يكون إحدى القيم: `default`، `placeholder`، و`danger` |
|
||||
| أزرار الأيقونات | array | مصفوفة الكائنات التي تمثل أزرار الأيقونات الإضافية المرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
|
||||
| عند_النقر | دالة | وظيفة استدعاء ليتم تشغيلها عند النقر فوق الرابط |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| isDragDisabled | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -183,7 +183,7 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Multi Select Avatar
|
||||
### التحديد المتعدد للصورة الرمزية
|
||||
|
||||
عنصر قائمة متعدد الخيارات مع صورة رمزية، ومربع اختيار للتحديد، ومحتوى نصي.
|
||||
|
||||
@@ -221,7 +221,7 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
|
||||
### التنقل
|
||||
|
||||
A menu item featuring an optional left icon, textual content, and a right-chevron icon.
|
||||
عنصر قائمة يتضمن أيقونة يسارية اختيارية، محتوى نصي، وأيقونة سهم إلى اليمين.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
@@ -299,9 +299,9 @@ A menu item featuring an optional left icon, textual content, and a right-chevro
|
||||
</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="الاستخدام">
|
||||
@@ -310,16 +310,16 @@ A selectable menu item with an avatar, featuring optional left content (avatar a
|
||||
|
||||
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=";
|
||||
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6OnqvLz9PX29/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");
|
||||
console.log("تم تحديد عنصر القائمة");
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
avatar={<img src={imageUrl} alt="الصورة الرمزية" />}
|
||||
text="الخيار الأول"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
@@ -349,7 +349,7 @@ A selectable menu item with an avatar, featuring optional left content (avatar a
|
||||
|
||||
### اختيار اللون
|
||||
|
||||
A selectable menu item with a color sample for scenarios where you want users to choose a color from a menu.
|
||||
عنصر قائمة قابل للتحديد يتضمن عينة لون للسيناريوهات التي تريد فيها من المستخدمين اختيار لون من القائمة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
@@ -383,7 +383,7 @@ A selectable menu item with a color sample for scenarios where you want users to
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| البديل | نص | The variant of the color sample. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
|
||||
| البديل | نص | المتغير لعينة اللون. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
</Tab>
|
||||
|
||||