snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
---
title: أفضل الممارسات
---
تحدد هذه الوثيقة أفضل الممارسات التي يجب اتباعها عند العمل في الواجهة الخلفية.
## اتبع نهجًا معياريًا
الواجهة الخلفية تتبع نهجًا معياريًا، وهو مبدأ أساسي عند العمل مع NestJS. تأكد من تقسيم الكود إلى وحدات قابلة لإعادة الاستخدام للحفاظ على كود مرتب ومنظم.
يجب أن تحتوي كل وحدة على ميزة أو وظيفة معينة وأن يكون لها نطاق محدد بوضوح. يتيح هذا النهج المعياري فصل الاهتمامات بوضوح ويزيل التعقيدات غير الضرورية.
## إتاحة الخدمات لاستخدامها في الوحدات
قم دائمًا بإنشاء خدمات ذات مسؤولية واضحة ووحيدة، مما يعزز من قابلية قراءة وصيانة الكود. قم بتسمية الخدمات بأسماء وصفية وبطريقة متسقة.
يجب أيضًا إتاحة الخدمات التي تريد استخدامها في وحدات أخرى. إتاحة الخدمات للوحدات الأخرى ممكنة عبر نظام حقن التبعيات القوي في NestJS، كما يعزز ذلك الاقتران الضعيف بين المكونات.
## تجنب استخدام نوع `أي`
عند إعلان متغير كـ `أي`، فإن مدقق الأنواع في TypeScript لا يقوم بأي عملية تدقيق للنوع، مما يجعله ممكنًا لتعيين أي نوع من القيم للمتغير. يستخدم TypeScript الاستدلال النوعي لتحديد نوع المتغير بناءً على القيمة. من خلال استخدام `أي`، لم يعد بإمكان TypeScript استنتاج النوع. هذا يجعل من الصعب ضبط أخطاء النوع أثناء التطوير، مما يؤدي إلى حدوث أخطاء في وقت التشغيل ويجعل الكود أقل قابلية للصيانة وأقل موثوقية ويصعب فهمه على الآخرين.
لهذا السبب يجب أن يكون لكل شيء نوع. لذا إن قمت بإنشاء كائن جديد يحتوي على الاسم الأول واسم العائلة، يجب عليك إنشاء واجهة أو نوع يحتوي على الاسم الأول واسم العائلة الذي يحدد شكل الكائن الذي تتعامل معه.

View File

@@ -0,0 +1,39 @@
---
title: كائنات مخصصة
---
الكائنات هي هياكل تسمح لك بتخزين البيانات (السجلات، السمات والقيم) الخاصة بالمؤسسة. توفر Twenty كلاً من الكائنات القياسية والمخصصة.
الكائنات القياسية هي كائنات مدمجة مع مجموعة من السمات المتاحة لجميع المستخدمين. تتضمن الأمثلة على الكائنات القياسية في Twenty الشركات والأشخاص. الكائنات القياسية تحتوي على حقول قياسية تتوفر أيضاً لجميع مستخدمي Twenty، مثل Company.displayName.
الكائنات المخصصة هي كائنات يمكنك إنشاؤها لتخزين المعلومات التي تخص مؤسستك. هذه الكائنات ليست مدمجة؛ يمكن لأعضاء مساحة العمل الخاصة بك إنشاء وتخصيص الكائنات المخصصة للاحتفاظ بالمعلومات التي لا تناسبها الكائنات القياسية.
## مخطط على مستوى عالي
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="مخطط على مستوى عالي" />
</div>
<br />
## كيف يعمل
الكائنات المخصصة تأتي من جداول الميتاداتا التي تحدد شكل، اسم، ونوع الكائنات. كل هذه المعلومات موجودة في قاعدة بيانات المخطط البياني للبيانات الوصفية، والتي تتكون من جداول:
* **مصدر البيانات**: يشير إلى مكان وجود البيانات.
* **الكائن**: يصف الكائن ويرتبط بمصدر البيانات.
* **الحقل**: يحدد حقول الكائن ويرتبط بالكائن.
لإضافة كائن مخصص، سيقوم عضو مساحة العمل بالاستعلام عن واجهة برمجة التطبيقات /metadata. يقوم هذا بتحديث البيانات الوصفية وفقًا لذلك ويحسب مخطط GraphQL استنادًا إلى البيانات الوصفية، ويخزنها في ذاكرة GQL للاستخدام لاحقًا.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/add-custom-objects.jpeg" alt="استعلام واجهة برمجة التطبيقات /metadata لإضافة الكائنات المخصصة" />
</div>
<br />
لجلب البيانات، تتضمن العملية إجراء استعلامات من خلال نقطة النهاية /graphql وتمريرها من خلال محلل الاستعلام.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="استعلام نقطة النهاية /graphql لجلب البيانات" />
</div>

View File

@@ -0,0 +1,46 @@
---
title: أعلام الميزة
---
تُستخدم أعلام الميزات لإخفاء الميزات التجريبية. بالنسبة إلى Twenty، يتم ضبطها على مستوى مساحة العمل وليس على مستوى المستخدم.
## إضافة علم ميزة جديد
في ملف `FeatureFlagKey.ts` أضف علم الميزة:
```ts
type FeatureFlagKey =
| 'IS_FEATURENAME_ENABLED'
| ...;
```
أيضًا أضفه إلى التعداد في `feature-flag.entity.ts`:
```ts
enum FeatureFlagKeys {
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
...
}
```
لتطبيق علم ميزة على ميزة **الخلفية** استخدم:
```ts
@Gate({
featureFlag: 'IS_FEATURENAME_ENABLED',
})
```
لتطبيق علم ميزة على ميزة **الواجهة الأمامية** استخدم:
```ts
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
```
## تكوين أعلام الميزات للنشر
تغيير السجل المعني في جدول `core.featureFlag`:
| المُعرّف | المفتاح | معرف مساحة العمل | القيمة |
| -------- | ------------------------ | ---------------- | ------ |
| عشوائي | `IS_FEATURENAME_ENABLED` | معرف مساحة العمل | `صحيح` |

View File

@@ -0,0 +1,125 @@
---
title: هيكلية المجلدات
info: نظرة تفصيلية داخل هيكلية مجلدات الخادم
---
الهيكلية الدليلية للمكونات الخلفية كالتالي:
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
```
## قدرات
تعريف الأذونات وتتضمن معالجات لكل كيان.
## زخارف
تعريف الزخارف المخصصة في NestJS لإضافة وظائف جديدة.
شاهد [زخارف مخصصة](https://docs.nestjs.com/custom-decorators) لمزيد من التفاصيل.
## الفلاتر
تتضمن فلاتر استثناء لمعالجة الحالات الطارئة التي قد تحدث في نقاط GraphQL النهائية.
## حمايات
شاهد [الحمايات](https://docs.nestjs.com/guards) لمزيد من التفاصيل.
## الصحة
تتضمن واجهة برمجية متاحة للجميع (healthz) تعيد جيسون لتأكيد ما إذا كانت قاعدة البيانات تعمل كما هو متوقع.
## البيانات الوصفية
تعريف الأشياء المخصصة وتوفر واجهة برمجية GraphQL (graphql/metadata).
## مساحة العمل
توليد وتقديم مخطط GraphQL مخصص بناءً على البيانات الوصفية.
### هيكلية دليل مساحة العمل
```
workspace
└───workspace-schema-builder
└───factories
└───graphql-types
└───database
└───interfaces
└───object-definitions
└───services
└───storage
└───utils
└───workspace-resolver-builder
└───factories
└───interfaces
└───workspace-query-builder
└───factories
└───interfaces
└───workspace-query-runner
└───interfaces
└───utils
└───workspace-datasource
└───workspace-manager
└───workspace-migration-runner
└───utils
└───workspace.module.ts
└───workspace.factory.spec.ts
└───workspace.factory.ts
```
يحتوي الجذر في دليل مساحة العمل على `workspace.factory.ts`، ملف يحتوي على وظيفة `createGraphQLSchema`. تولّد هذه الوظيفة مخططًا خاصًا بمساحة العمل باستخدام البيانات الوصفية لتخصيص المخطط لمساحات العمل الفردية. من خلال فصل بناء المخطط والمستعرض، نستخدم وظيفة `makeExecutableSchema`، التي تجمع بين هذه العناصر المنفصلة.
تعتمد هذه الاستراتيجية على التنظيم ولا تساعد فقط في التنظيم، لكنها أيضًا تحسن الأداء، مثل تخزين تعريفات النوع المولدة مؤقتًا لتعزيز الأداء والتوسع.
### منشئ مخطط مساحة العمل
يولد مخطط GraphQL، ويتضمن:
#### مصانع:
مصانع متخصصة لتوليد الإنشاءات المتعلقة بـ GraphQL.
* المصنع النوعي يترجم بيانات الحقول الوصفية إلى أنواع GraphQL باستخدام `TypeMapperService`.
* ينشئ type-definition.factory كائنات إدخال أو إخراج لـ GraphQL مشتقة من `objectMetadata`.
#### أنواع GraphQL
يتضمن تعدادات، إدخالات، كائنات، وأشكال بسيطة، ويعمل كنقاط بناء للمخطط.
#### واجهات وتعريفات الكائنات
تحتوي على مخططات للكيانات GraphQL، وتشتمل على أنواع معرفة مسبقًا ومخصصة مثل `MONEY` أو `URL`.
#### خدمات
تحتوي على الخدمة المسؤولة عن ربط FieldMetadataType بنوع GraphQL المناسب أو أدوات التعديل الاستعلامية.
#### التخزين
يتضمن فئة `TypeDefinitionsStorage` التي تحتوي على تعريفات نوع قابلة لإعادة الاستخدام، مما يمنع تكرار أنواع GraphQL.
### منشئ محلل مساحة العمل
إنشاء وظائف المعالجات للاستعلام وتعديل مخطط GraphQL.
كل مصنع في هذا الدليل مسؤول عن إنتاج نوع محلل مميز، مثل `FindManyResolverFactory`، المصمم للتطبيق المتكيف عبر جداول مختلفة.
### مشغل استعلامات مساحة العمل
يشغل الاستعلامات المولدة على قاعدة البيانات ويحلل النتيجة.

View File

@@ -0,0 +1,41 @@
---
title: قائمة انتظار الرسائل
---
تسهل القوائم العمليات غير المتزامنة. يمكن استخدامها لأداء مهام الخلفية مثل إرسال بريد ترحيبي عند التسجيل.
سيكون لكل حالة استخدام فئة قائمة خاصة بها ممتدة من `MessageQueueServiceBase`.
حاليًا، ندعم `bull-mq`[bull-mq](https://bullmq.io/) فقط كبرنامج تشغيل القائمة.
## خطوات إنشاء واستخدام قائمة جديدة
1. أضف اسم قائمة لقائمة جديدة تحت التعداد `MESSAGE_QUEUES`.
2. وفّر تنفيذ المصنع لقائمة الانتظار مع استخدام اسم قائمة الانتظار بوصفه رمز التبعية.
3. قم بإدراج القائمة التي أنشأتها في الوحدة/الخدمة المطلوبة مع اسم القائمة كرمز تبعية.
4. أضِف صنف عامل مع حقن قائم على الرمز تمامًا مثل المنتِج.
### نموذج للاستخدام
```ts
class Resolver {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {}
async onSomeAction() {
//business logic
await this.queue.add(someData);
}
}
//async worker
class CustomWorker {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {
this.initWorker();
}
async initWorker() {
await this.queue.work(async ({ id, data }) => {
//worker logic
});
}
}
```

View File

@@ -0,0 +1,87 @@
---
title: الأوامر الخلفية
icon: terminal
---
## الأوامر المفيدة
يجب تنفيذ هذه الأوامر من مجلد packages/twenty-server.
من أي مجلد آخر يمكنك تشغيل `npx nx {command} twenty-server` (أو `npx nx run twenty-server:{command}`).
### إعداد المرة الأولى
```
npx nx database:reset twenty-server # setup the database with dev seeds
```
### بدء الخادم
```
npx nx run twenty-server:start
```
### Lint
```
npx nx run twenty-server:lint # pass --fix to fix lint errors
```
### تجربة
```
npx nx run twenty-server:test:unit # run unit tests
npx nx run twenty-server:test:integration # run integration tests
```
ملاحظة: يمكنك تشغيل `npx nx run twenty-server:test:integration:with-db-reset` في حالة احتياجك لإعادة تعيين قاعدة البيانات قبل تشغيل اختبارات التكامل.
### إعادة تعيين قاعدة البيانات
إذا أردت إعادة تعيين قاعدة البيانات وزرع بعض البيانات فيها، يمكنك تشغيل الأمر التالي:
```bash
npx nx run twenty-server:database:reset
```
### عمليات الترحيل
#### للكائنات داخل مخططات Core/Metadata (TypeORM)
```bash
npx nx run twenty-server:database:migrate:generate
```
## "التقنية المستخدمة"
تستخدم Twenty بشكل أساسي NestJS للواجهة الخلفية.
Prisma كان أول ORM استخدمناه. ولكن للسماح للمستخدمين بإنشاء الحقول والعناصر المخصصة، كان من المنطقي استخدام مستوى أقل حيث نحتاج إلى تحكم دقيق. الآن يستخدم المشروع TypeORM.
إليك شكل العناصر التقنية الآن.
**النواة**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
**قاعدة البيانات**
* [Postgres](https://www.postgresql.org/)
**التكاملات مع جهات خارجية**
* [Sentry](https://sentry.io/welcome/) لتتبع الأخطاء
**الاختبار**
* [Jest](https://jestjs.io/)
**الأدوات**
* [Yarn](https://yarnpkg.com/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
**التطوير**
* [AWS EKS](https://aws.amazon.com/eks/)

View File

@@ -0,0 +1,85 @@
---
title: تطبيق Zapier
---
قم بمزامنة Twenty مع أكثر من 3000 تطبيق باستخدام [Zapier](https://zapier.com/) بسهولة. قم بأتمتة المهام، وزيادة الإنتاجية، وتعزيز علاقاتك مع العملاء!
## حول Zapier
Zapier هي أداة تتيح لك أتمتة سير العمل من خلال ربط التطبيقات التي يستخدمها فريقك يوميًا. المفهوم الأساسي ل Zapier هو سير العمل الآلي، المسمى Zaps، والذي يتضمن محفزات وإجراءات.
يمكنك معرفة المزيد حول كيفية عمل Zapier [هنا](https://zapier.com/how-it-works).
## إعداد
### الخطوة 1: تثبيت حزم Zapier
```bash
cd packages/twenty-zapier
yarn
```
### الخطوة 2: تسجيل الدخول باستخدام CLI
استخدم بيانات اعتماد Zapier الخاصة بك لتسجيل الدخول باستخدام CLI:
```bash
zapier login
```
### الخطوة 3: تعيين متغيرات البيئة
من مجلد `packages/twenty-zapier`، قم بتشغيل:
```bash
cp .env.example .env
```
شغل التطبيق محليًا، اذهب إلى [http://localhost:3000/settings/api-webhooks](http://localhost:3000/settings/api-webhooks)، وولد مفتاح API.
استبدل قيمة **YOUR_API_KEY** في ملف `.env` بمفتاح API الذي قمت بتوليده للتو.
## التطوير
<Warning>
تأكد من تشغيل `yarn build` قبل أي أمر `zapier`.
</Warning>
### تجربة
```bash
yarn test
```
### Lint
```bash
yarn format
```
### راقب وقم بالتحويل عند تحرير الكود
```bash
yarn watch
```
### تحقق من صحة تطبيق Zapier الخاص بك
```bash
yarn validate
```
### قم بنشر تطبيق Zapier الخاص بك
```bash
yarn deploy
```
### اعرض جميع أوامر Zapier CLI
```bash
zapier
```

View File

@@ -0,0 +1,79 @@
---
title: الأخطاء والطلبات وطلبات السحب
icon: bug
info: أبلغ عن المشكلات، واطلب الميزات، وساهم بالشفرة البرمجية
---
## الإبلاغ عن الأخطاء
للإبلاغ عن خطأ، يرجى [إنشاء مشكلة على GitHub](https://github.com/twentyhq/twenty/issues/new).
يمكنك أيضًا طلب المساعدة عبر [Discord](https://discord.gg/cx5n4Jzs57).
## طلبات الميزات
إذا لم تكن متأكدًا مما إذا كانت مشكلة أو إذا كنت تشعر بأنها مجرد طلب ميزة، فيمكنك على الأرجح [فتح نقاش بدلاً من ذلك](https://github.com/twentyhq/twenty/discussions/new).
## أرسل طلب سحب
تبدأ المساهمة بالشفرة في Twenty بطلب سحب (PR).
### قبل أن تبدأ
1. تحقق من [المشكلات القائمة](https://github.com/twentyhq/twenty/issues) للعمل ذي الصلة
2. بالنسبة للميزات الجديدة، افتح مشكلة أولًا للنقاش
3. راجع [مدونة قواعد السلوك](https://github.com/twentyhq/twenty/blob/main/.github/CODE_OF_CONDUCT.md)
### التفريع والاستنساخ
1. فرّع المستودع على GitHub
2. استنسخ تفريعك:
```bash
git clone https://github.com/YOUR_USERNAME/twenty.git
cd twenty
```
3. أضف المستودع البعيد upstream:
```bash
git remote add upstream https://github.com/twentyhq/twenty.git
```
### إنشاء فرع
```bash
git checkout -b feature/your-feature-name
```
استخدم أسماء فروع وصفية:
* `feature/add-export-button`
* `fix/login-redirect-issue`
* `docs/update-api-guide`
### أجرِ تعديلاتك
1. اكتب شفرة نظيفة وموثقة جيدًا
2. اتبع أسلوب الشفرة المعمول به
3. أضف اختبارات للوظائف الجديدة
4. حدّث الوثائق عند الحاجة
### أرسل طلب السحب الخاص بك
1. ادفع فرعك:
```bash
git push origin feature/your-feature-name
```
2. افتح طلب سحب على GitHub
3. املأ قالب طلب السحب
4. اربط المشكلات ذات الصلة
### قائمة التحقق لطلب السحب
* [ ] الشفرة تتبع إرشادات أسلوب المشروع
* [ ] تجتاز الاختبارات محليًا
* [ ] الوثائق محدّثة
* [ ] وصف طلب السحب يوضح التغييرات

View File

@@ -0,0 +1,329 @@
---
title: أفضل الممارسات
icon: star
---
تحدد هذه الوثيقة أفضل الممارسات التي يجب اتباعها عند العمل في الواجهة الأمامية.
## إدارة الحالة
تقوم React و Jotai بإدارة الحالة في قاعدة الشيفرة.
### استخدم ذرات Jotai لتخزين الحالة
من الجيد إنشاء أكبر عدد ممكن من الذرات لتخزين الحالة الخاصة بك.
<Warning>
من الأفضل استخدام ذرات إضافية بدلاً من محاولة أن تكون مقتضبًا باستخدام تمرير الخصائص.
</Warning>
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
export const myAtomState = createAtomState<string>({
key: 'myAtomState',
defaultValue: 'default value',
});
export const MyComponent = () => {
const [myAtom, setMyAtom] = useAtomState(myAtomState);
return (
<div>
<input
value={myAtom}
onChange={(e) => setMyAtom(e.target.value)}
/>
</div>
);
}
```
### لا تستخدم `useRef` لتخزين الحالة
تجنب استخدام `useRef` لتخزين الحالة.
إذا كنت ترغب في تخزين الحالة، يجب أن تستخدم `useState` أو ذرات Jotai مع `useAtomState`.
انظر [كيفية إدارة إعادة العرض](#managing-re-renders) إذا شعرت أنك بحاجة إلى `useRef` لمنع بعض إعادة العرض من الحدوث.
## إدارة إعادة العرض
يمكن أن تكون إعادة العرض صعبة الإدارة في React.
إليك بعض القواعد التي يجب اتباعها لتجنب إعادة العرض غير الضرورية.
تذكر أنه يمكنك **دائمًا** تجنب إعادة العرض من خلال فهم سببها.
### العمل على المستوى الجذري
تجنب إعادة العرض في الميزات الجديدة أصبح سهلاً الآن عن طريق إزالتها على المستوى الجذري.
يحتوي المكون الجانبي "PageChangeEffect" على "useEffect" واحد فقط يحمل كل المنطق لتنفيذه على تغيير الصفحة.
بهذه الطريقة، تعرف أن هناك مكان واحد فقط يمكنه تحفيز إعادة العرض.
### تفكير دائماً مرتين قبل إضافة `useEffect` في الرمز الخاص بك
وكثيرا ما تكون عمليات إعادة التسليم ناجمة عن `فعالية الاستخدام` غير الضرورية.
يجب أن تفكر فيما إذا كنت بحاجة إلى \`useEffect'، أو إذا كنت تستطيع نقل المنطق في وظيفة معالج الأحداث.
ستجد أنه من السهل عموما نقل المنطق في دالة "handleClick" أو "handleChange".
يمكنك أيضًا العثور عليها في المكتبات مثل Apollo: `onCompleted`، `onError`، إلخ.
### استخدم مكون أخت لاستخراج \`تأثير الاستخدام' أو منطق جلب البيانات
إذا كنت تشعر أنك بحاجة إلى إضافة 'useEffect' في مكون الجذر الخاص بك، فيجب عليك النظر في استخراجه في مكون جانبي.
يمكنك تطبيق نفس الشيء على منطق جلب البيانات، مع الخُطافات Apollo.
```tsx
// ❌ Bad, will cause re-renders even if data is not changing,
// because useEffect needs to be re-evaluated
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <div>{data}</div>;
};
export const App = () => (
<PageComponent />
);
```
```tsx
// ✅ Good, will not cause re-renders if data is not changing,
// because useEffect is re-evaluated in another sibling component
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
return <div>{data}</div>;
};
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <></>;
};
export const App = () => (
<>
<PageData />
<PageComponent />
</>
);
```
### استخدم حالات عائلة الذرات والمحددات
تُعد حالات عائلة الذرات والمحددات طريقة رائعة لتجنّب عمليات إعادة التصيير.
إنها مفيدة عندما تحتاج إلى تخزين قائمة من العناصر.
### يجب ألا تستخدم `React.memo(MyComponent)`
تجنب استخدام `React.memo()` لأنه لا يحل سبب إعادة العرض، بل يكسر سلسلة إعادة العرض، مما قد يؤدي إلى سلوك غير متوقع ويجعل التعليمات البرمجية صعبة التعديل.
### حدد استخدام `useCallback` أو `useMemo`
غالبًا ما لا تكون ضرورية وستجعل التعليمات البرمجية أصعب في القراءة والصيانة لأداء غير ملحوظ.
## Console.logs
تصريحات `console.log` ذات قيمة أثناء التطوير، حيث تقدم رؤى في الوقت الفعلي عن قيمة المتغيرات وتدفق التعليمات البرمجية. ولكن، تركها في التعليمات البرمجية في الإنتاج قد يؤدي إلى عدة مشكلات:
1. **الأداء**: تسجيل كثير قد يؤثر على أداء وقت التشغيل، خاصة في التطبيقات على الجانب العميل.
2. **الأمان**: تسجيل البيانات الحساسة قد يكشف المعلومات الحرجة لأي شخص يقوم بتفتيش وحدة التحكم في المتصفح.
3. **النظافة**: ملء وحدة التحكم بالسجلات قد يحجب التحذيرات أو الأخطاء الهامة التي يحتاج المطورون أو الأدوات إلى رؤيتها.
4. **الاحترافية**: المستخدمون النهائيون أو العملاء الذين يفحصون وحدة التحكم ويجدون الكثير من تصريحات السجلات قد يشككون في جودة وتأنق التعليمات البرمجية.
تأكد من إزالة جميع تصريحات `console.log` قبل دفع التعليمات البرمجية إلى الإنتاج.
## التسمية
### تسمية المتغيرات
يجب أن تعبر أسماء المتغيرات بدقة عن الغرض أو وظيفة المتغير.
#### المشكلة مع الأسماء العامة
الأسماء العامة في البرمجة ليست مثالية لأنها تفتقر إلى التحديد، مما يؤدي إلى الغموض وتقليل قابلية قراءة التعليمات البرمجية. مثل هذه الأسماء تفشل في التعبير عن الغرض من المتغير أو الوظيفة، مما يجعل من الصعب على المطورين فهم نية التعليمات البرمجية دون تحقيق أعمق. يمكن أن يؤدي ذلك إلى زيادة وقت إزالة الأخطاء، وزيادة قابلية التعرض للأخطاء، وصعوبات في الصيانة والتعاون. في الوقت نفسه، تجعل التسمية الوصفية التعليمات البرمجية تفسيرية بذاتها وأسهل في التنقل، مما يعزز جودة التعليمات البرمجية وإنتاجية المطور.
```tsx
// ❌ Bad, uses a generic name that doesn't communicate its
// purpose or content clearly
const [value, setValue] = useState('');
```
```tsx
// ✅ Good, uses a descriptive name
const [email, setEmail] = useState('');
```
#### بعض الكلمات يجب تجنبها في أسماء المتغيرات
* dummy
### معالجات الأحداث
يجب أن تبدأ أسماء معالجات الأحداث بكلمة `handle`، بينما يعتبر `on` بادئة تستخدم لتسمية الأحداث في خصائص المكونات.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## الخصائص الاختيارية
تجنب تمرير القيمة الافتراضية لخاصية اختيارية.
**مثال**
خذ مكون`EmailField` المحدد أدناه:
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
<TextInput value={value} disabled={disabled} fullWidth />
);
```
**الاستخدام**
```tsx
// ❌ Bad, passing in the same value as the default value adds no value
const Form = () => <EmailField value="username@email.com" disabled={false} />;
```
```tsx
// ✅ Good, assumes the default value
const Form = () => <EmailField value="username@email.com" />;
```
## المكون كخصائص
حاول قدر الإمكان تمرير المكونات غير المنشأة كمكونات، بحيث يمكن للأطفال تحديد ما يحتاجون لتمريره.
المثال الأكثر شيوعا لذلك هو مكونات الأيقونات:
```tsx
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
// In MyComponent
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
const theme = useTheme();
return (
<div>
<MyIcon size={theme.icon.size.md}>
</div>
)
};
```
لفهم React أن المكون هو مكون، يجب عليك استخدام PascalCase، للتمكن من معاملته لاحقًا كـ `<MyIcon>`
## تمرير الخصائص: اجعلها محدودة
يشير تمرير الخصائص في سياق React إلى ممارسة تمرير متغيرات الحالة وأدوات تحكمها عبر العديد من طبقات المكونات، حتى لو لم تستخدمها المكونات الوسيطة. رغم أنها تكون ضرورية في بعض الأحيان، إلا أن تمرير الخصائص الزائد يمكن أن يؤدي إلى:
1. **انخفاض القابلية للقراءة**: يمكن أن يصبح تتبع مصدر الخاصية أو مكان استخدامها معقدا في هيكل مكون معقد.
2. **تحديات الصيانة**: قد تتطلب التغييرات في هيكل خاصية أحد المكونات تعديلات في عدة مكونات، حتى لو لم تستخدم الخاصية مباشرة.
3. **تقليل إعادة استخدام المكونات**: يصبح المكون الذي يتلقى الكثير من الخصائص لتمريرها فقط أقل شمولية وأصعب في إعادة استخدامه في سياقات مختلفة.
إذا شعرت أنك تستخدم تمرير الخصائص بشكل مفرط، راجع [أفضل ممارسات إدارة الحالة](#state-management).
## استيرادات
عند الاستيراد، اختر الأسماء المستعارة المتعينة بدلا من تحديد المسارات كاملة أو نسبية.
**الأسماء المستعارة**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**الاستخدام**
```tsx
// ❌ Bad, specifies the entire relative path
import {
CatalogDecorator
} from '../../../../../testing/decorators/CatalogDecorator';
import {
ComponentDecorator
} from '../../../../../testing/decorators/ComponentDecorator';
```
```tsx
// ✅ Good, utilises the designated aliases
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from 'twenty-ui/testing';
```
## التحقق من المخططات
[Zod](https://github.com/colinhacks/zod) هو مدقق المخططات للكائنات غير المTyped:
```js
const validationSchema = z
.object({
exist: z.boolean(),
email: z
.string()
.email('Email must be a valid email'),
password: z
.string()
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
})
.required();
type Form = z.infer<typeof validationSchema>;
```
## التغييرات الجذرية
قم دائمًا بإجراء اختبارات يدوية شاملة قبل المتابعة لضمان أن التعديلات لم تسبب تعطيلًا في أماكن أخرى، نظرًا لأن الاختبارات لم تدمج حتى الآن بشكل كبير.

View File

@@ -0,0 +1,110 @@
---
title: هيكلية المجلدات
icon: folder-tree
info: نظرة مفصلة على هيكل المجلدات الخاصة بنا
---
في هذا الدليل، ستستكشف تفاصيل هيكل دليل المشروع وكيف يساهم في تنظيم Twenty وسهولة صيانتها.
باتباع اصطلاح هيكل المجلدات هذا، يسهل العثور على الملفات المتعلقة بميزات محددة وضمان أن يكون التطبيق قابلاً للتوسّع والصيانة.
```
front
└───modules
│ └───module1
│ │ └───submodule1
│ └───module2
│ └───ui
│ │ └───display
│ │ └───inputs
│ │ │ └───buttons
│ │ └───...
└───pages
└───...
```
## الصفحات
تشمل المكونات عالية المستوى المحددة من قبل مسارات التطبيق. وتستورد مكوّنات منخفضة المستوى من مجلد الوحدات (تفاصيل إضافية أدناه).
## الوحدات
تمثّل كل وحدة ميزة أو مجموعة من الميزات، وتضم مكوّناتها الخاصة وحالاتها ومنطقها التشغيلي.
يجب أن تتبع جميعها البنية المذكورة أدناه. يمكنك تعشيق الوحدات داخل الوحدات (تُعرف بالوحدات الفرعية) وستطبق نفس القواعد.
```
module1
└───components
│ └───component1
│ └───component2
└───constants
└───contexts
└───graphql
│ └───fragments
│ └───queries
│ └───mutations
└───hooks
│ └───internal
└───states
│ └───selectors
└───types
└───utils
```
### السياقات
السياق هو وسيلة لتمرير البيانات عبر شجرة المكوّنات دون الحاجة إلى تمرير الخصائص يدويًا في كل مستوى.
انظر [React Context](https://react.dev/reference/react#context-hooks) لمزيد من التفاصيل.
### جراف كيو إل
تشمل الأجزاء، الاستعلامات والتحويرات.
انظر [GraphQL](https://graphql.org/learn/) لمزيد من التفاصيل.
* الأجزاء
الجزء هو جزء قابل لإعادة الاستخدام من استعلام، والذي يمكنك استخدامه في أماكن مختلفة. باستخدام الأجزاء، يصبح من الأسهل تجنب تكرار التعليمات البرمجية.
انظر [GraphQL Fragments](https://graphql.org/learn/queries/#fragments) لمزيد من التفاصيل.
* الاستعلامات
انظر [GraphQL Queries](https://graphql.org/learn/queries/) لمزيد من التفاصيل.
* التحويرات
انظر [GraphQL Mutations](https://graphql.org/learn/queries/#mutations) لمزيد من التفاصيل.
### الخطافات
انظر [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) لمزيد من التفاصيل.
### الحالات
تشمل منطق إدارة الحالة. [Jotai](https://jotai.org) يتولّى ذلك.
* المحددات: الذرات المشتقة (باستخدام `createAtomSelector`) تحسب قيماً من ذرات أخرى وتخزن نتائجها في الذاكرة مؤقتاً تلقائياً.
لا تزال إدارة الحالة المدمجة في React تتولّى الحالة داخل المكوّن.
### الأدوات
يجب أن تحتوي فقط على دوال نقية قابلة لإعادة الاستخدام. بخلاف ذلك، أنشئ خطافات مخصصة في مجلد `hooks`.
## واجهة المستخدم
تحتوي على جميع مكونات واجهة المستخدم القابلة لإعادة الاستخدام المستخدمة في التطبيق.
يمكن أن يحتوي هذا المجلد على مجلّدات فرعية، مثل `data`، `display`، `feedback`، و `input` لأنواع معينة من المكوّنات. يجب أن يكون كل مكون مستقلًا وقابلًا لإعادة الاستخدام، حتى تتمكن من استخدامه في أجزاء مختلفة من التطبيق.
من خلال فصل مكونات واجهة المستخدم عن المكونات الأخرى في مجلد `modules`، يسهل الحفاظ على تصميم متسق وإجراء تغييرات على واجهة المستخدم دون التأثير على الأجزاء الأخرى (منطق الأعمال) من قاعدة الشيفرة.
## الواجهة والتبعيات
يمكنك استيراد شيفرة وحدة أخرى من أي وحدة باستثناء مجلد `ui`. سيجعل هذا شيفرته سهلة الاختبار.
### داخلي
كل جزء (خطافات، حالات، ...) من الممكن أن يحتوي جزء من الوحدة على مجلد `internal`، والذي يحتوي على الأجزاء التي تُستخدم فقط داخل الوحدة.

View File

@@ -0,0 +1,91 @@
---
title: أوامر الواجهة الأمامية
icon: terminal
---
## الأوامر المفيدة
### "بدء التطبيق"
```bash
npx nx start twenty-front
```
### "إعادة توليد مخطط graphql بناءً على مخطط API graphql"
```bash
npx nx run twenty-front:graphql:generate --configuration=metadata
```
"أو"
```bash
npx nx run twenty-front:graphql:generate
```
### Lint
```bash
npx nx run twenty-front:lint # pass --fix to fix lint errors
```
## "الترجمات"
```bash
npx nx run twenty-front:lingui:extract
npx nx run twenty-front:lingui:compile
```
### تجربة
```bash
npx nx run twenty-front:test # run jest tests
npx nx run twenty-front:storybook:serve:dev # run storybook
npx nx run twenty-front:storybook:test # run tests # (needs yarn storybook:serve:dev to be running)
npx nx run twenty-front:storybook:coverage # (needs yarn storybook:serve:dev to be running)
```
## "التقنية المستخدمة"
"المشروع يحتوي على تقنية بسيطة ونظيفة، مع الحد الأدنى من كود البويلر بليت."
"**التطبيق**"
* "[React](https://react.dev/)"
* "[Apollo](https://www.apollographql.com/docs/)"
* "[GraphQL Codegen](https://the-guild.dev/graphql/codegen)"
* [Jotai](https://jotai.org/)
* "[TypeScript](https://www.typescriptlang.org/)"
**الاختبار**
* [Jest](https://jestjs.io/)
* "[Storybook](https://storybook.js.org/)"
**الأدوات**
* [Yarn](https://yarnpkg.com/)
* "[Craco](https://craco.js.org/docs/)"
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
## "الهيكلية"
### "التوجيه"
"[React Router](https://reactrouter.com/) يتعامل مع التوجيه."
لتجنب غير ضروري [re-renders](/l/ar/developers/contribute/capabilities/frontend-development/best-practices-front#managing-re-renders) كل منطق المسارات في 'useEffect' في 'PageChangeEffect'.
### "إدارة الحالة"
[Jotai](https://jotai.org/) يتعامل مع إدارة الحالة.
"راجع [أفضل الممارسات](/l/ar/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) لمزيد من المعلومات حول إدارة الحالة."
## "الاختبار"
"[Jest](https://jestjs.io/) يعمل كأداة لاختبار الوحدات بينما يعمل [Storybook](https://storybook.js.org/) لاختبار المكونات."
"يستخدم Jest بشكل رئيسي لاختبار دوال الأدوات وليس المكونات نفسها."
"Storybook لاختبار سلوك المكونات المعزولة، وكذلك عرض نظام التصميم."

View File

@@ -0,0 +1,178 @@
---
title: مفاتيح الاختصار
---
## مقدمة
عندما تحتاج إلى الاستماع إلى مفتاح اختصار، فإنه عادةً ما تستخدم مستمع الحدث `onKeyDown`.
في `twenty-front`، قد تواجه تعارضات بين نفس مفاتيح الاختصار المستخدمة في مكونات مختلفة، مركبة في الوقت نفسه.
على سبيل المثال، إذا كان لديك صفحة تستمع لمفتاح Enter ومودال يستمع لمفتاح Enter، ولكن به مكون Select يستمع لمفتاح Enter، فقد تواجه تعارضًا عند تركيب الجميع في الوقت نفسه.
## الخطاف `useScopedHotkeys`
لمعالجة هذه المشكلة، لدينا خطاف مخصص يمكن من الاستماع لمفاتيح الاختصار دون أي تعارض.
تضعه داخل مكوّن، ولن يستمع إلى مفاتيح الاختصار إلا عندما يكون المكوّن مُركّبًا وعندما يكون **نطاق مفاتيح الاختصار** المحدّد نشطًا.
## كيف تستمع إلى مفاتيح الاختصار عمليًا؟
هناك خطوتان متضمنتان في إعداد الاستماع لمفاتيح الاختصار:
1. تعيين [نطاق المفتاح](#what-is-a-hotkey-scope-) الذي سيستمع لمفاتيح الاختصار
2. استخدام الخطاف `useScopedHotkeys` للاستماع لمفاتيح الاختصار
إعداد نطاقات مفاتيح الاختصار ضروري حتى في الصفحات البسيطة، لأن عناصر أخرى في واجهة المستخدم مثل القائمة اليسرى أو قائمة الأوامر قد تستمع أيضًا لمفاتيح الاختصار.
## حالات الاستخدام لمفاتيح الاختصار
بشكل عام، سيكون لديك حالتا استخدام تتطلبان مفاتيح الاختصار:
1. في صفحة أو مكون مركب في صفحة
2. في مكون من نوع مودال يتخذ التركيز بسبب إجراء من المستخدم
يمكن حدوث الحالة الثانية بشكل متكرر: على سبيل المثال، في قائمة منسدلة في مودال.
### الاستماع لمفاتيح الاختصار في صفحة
مثال:
```tsx
const PageListeningEnter = () => {
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
// 1. Set the hotkey scope in a useEffect
useEffect(() => {
setHotkeyScopeAndMemorizePreviousScope(
ExampleHotkeyScopes.ExampleEnterPage,
);
// Revert to the previous hotkey scope when the component is unmounted
return () => {
goBackToPreviousHotkeyScope();
};
}, [goBackToPreviousHotkeyScope, setHotkeyScopeAndMemorizePreviousScope]);
// 2. Use the useScopedHotkeys hook
useScopedHotkeys(
Key.Enter,
() => {
// Some logic executed on this page when the user presses Enter
// ...
},
ExampleHotkeyScopes.ExampleEnterPage,
);
return <div>My page that listens for Enter</div>;
};
```
### الاستماع لمفاتيح الاختصار في مكون من نوع مودال
في هذا المثال سنستخدم مكوّن نافذة منبثقة يستمع لمفتاح Escape ليطلب من المكوّن الأب إغلاقه.
هنا يتمثّل تفاعل المستخدم في تغيير النطاق.
```tsx
const ExamplePageWithModal = () => {
const [showModal, setShowModal] = useState(false);
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
const handleOpenModalClick = () => {
// 1. Set the hotkey scope when user opens the modal
setShowModal(true);
setHotkeyScopeAndMemorizePreviousScope(
ExampleHotkeyScopes.ExampleModal,
);
};
const handleModalClose = () => {
// 1. Revert to the previous hotkey scope when the modal is closed
setShowModal(false);
goBackToPreviousHotkeyScope();
};
return <div>
<h1>My page with a modal</h1>
<button onClick={handleOpenModalClick}>Open modal</button>
{showModal && <MyModalComponent onClose={handleModalClose} />}
</div>;
};
```
ثم في مكون المودال:
```tsx
const MyDropdownComponent = ({ onClose }: { onClose: () => void }) => {
// 2. Use the useScopedHotkeys hook to listen for Escape.
// Note that escape is a common hotkey that could be used by many other components
// So it's important to use a hotkey scope to avoid conflicts
useScopedHotkeys(
Key.Escape,
() => {
onClose()
},
ExampleHotkeyScopes.ExampleModal,
);
return <div>My modal component</div>;
};
```
من المهم استخدام هذا النمط عندما لست متأكدا من أن استخدام تأثير الاستخدام مع التركيب/إلغاء التثبيت سيكون كافيا لتجنب الصراعات.
ويمكن أن يكون من الصعب تصحيح تلك الصراعات، وقد يحدث ذلك في كثير من الأحيان بآثار استعمالية.
## ما هو نطاق المفتاح؟
نطاق المفتاح هو سلسلة تمثل السياق الذي تكون فيه مفاتيح الاختصار نشطة. عادة ما تكون مشفرة كنوع مفصل.
عندما تقوم بتغيير نطاق المفتاح، سيتم تمكين مفاتيح الاختصار المستمعة لهذا النطاق وتعطيل المفاتيح المستمعة لنطاقات أخرى.
يمكنك تعيين نطاق واحد فقط في كل مرة.
على سبيل المثال، يتم تعريف نطاقات مفاتيح الاختصار لكل صفحة في نوع "PageHotkeyScope" المفصل:
```tsx
export enum PageHotkeyScope {
Settings = 'settings',
CreateWorkspace = 'create-workspace',
SignInUp = 'sign-in-up',
CreateProfile = 'create-profile',
PlanRequired = 'plan-required',
ShowPage = 'show-page',
PersonShowPage = 'person-show-page',
CompanyShowPage = 'company-show-page',
CompaniesPage = 'companies-page',
PeoplePage = 'people-page',
OpportunitiesPage = 'opportunities-page',
ProfilePage = 'profile-page',
WorkspaceMemberPage = 'workspace-member-page',
TaskPage = 'task-page',
}
```
داخليًا، يتم تخزين النطاق المحدد حاليًا في ذرة Jotai مشتركة عبر التطبيق:
```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
key: 'currentHotkeyScopeState',
defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```
لكن لا يجب التعامل مع هذه الذرة يدويًا! سنرى كيف يمكن استخدامها في القسم التالي.
## كيف يعمل داخليًا؟
قمنا بإنشاء غلاف رقيق فوق [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) والذي يجعله أكثر كفاءة ويتجنب عمليات إعادة التقديم غير الضرورية.
ونقوم أيضًا بإنشاء ذرة Jotai للتعامل مع حالة نطاق مفاتيح الاختصار وجعلها متاحة في جميع أنحاء التطبيق.

View File

@@ -0,0 +1,8 @@
---
title: Storybook
description: تصفح مكتبة مكونات واجهة المستخدم الخاصة بـ Twenty
---
اطلع على مكتبة المكونات الكاملة لدينا والتوثيق في Storybook.
[افتح Storybook →](https://storybook.twenty.com)

View File

@@ -0,0 +1,291 @@
---
title: دليل الأسلوب
icon: paintbrush
---
تشمل هذه الوثيقة القواعد التي يجب اتباعها عند كتابة التعليمات البرمجية.
الهدف هنا هو وجود قاعدة شيفرة متسقة يسهل قراءتها وصيانتها.
لهذا، من الأفضل أن تكون تفصيلًا أكثر قليلاً بدلاً من أن تكون موجزًا للغاية.
دائمًا ضع في اعتبارك أن الناس يقرؤون التعليمات البرمجية أكثر مما يكتبونها، وخاصة في مشروع مفتوح المصدر، حيث يمكن لأي شخص المساهمة.
هناك العديد من القواعد التي لم يتم تعريفها هنا، ولكن يتم التحقق منها تلقائيًا بواسطة أدوات الفحص.
## React
### استخدام المكونات الوظيفية
استخدم دائمًا مكونات TSX الوظيفية.
لا تستخدم `import` الافتراضي مع `const`، لأنه أصعب من حيث القراءة والدمج باستخدام إكمال التعليمات البرمجية.
```tsx
// ❌ Bad, harder to read, harder to import with code completion
const MyComponent = () => {
return <div>Hello World</div>;
};
export default MyComponent;
// ✅ Good, easy to read, easy to import with code completion
export function MyComponent() {
return <div>Hello World</div>;
};
```
### الإزاحة
قم بإنشاء نوع الخصائص واطلق عليه `(ComponentName)Props` إذا لم يكن هناك حاجة لتصديره.
استخدام تفكيك الخصائص.
```tsx
// ❌ Bad, no type
export const MyComponent = (props) => <div>Hello {props.name}</div>;
// ✅ Good, type
type MyComponentProps = {
name: string;
};
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
#### امتنع عن استخدام `React.FC` أو `React.FunctionComponent` لتحديد أنواع الخصائص
```tsx
/* ❌ - Bad, defines the component type annotations with `FC`
* - With `React.FC`, the component implicitly accepts a `children` prop
* even if it's not defined in the prop type. This might not always be
* desirable, especially if the component doesn't intend to render
* children.
*/
const EmailField: React.FC<{
value: string;
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
```
```tsx
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
* component's props
* - This method doesn't automatically include the children prop. If
* you want to include it, you have to specify it in OwnProps.
*/
type EmailFieldProps = {
value: string;
};
const EmailField = ({ value }: EmailFieldProps) => (
<TextInput value={value} disabled fullWidth />
);
```
#### لا تستخدم انتشار متغير فردي للخصائص في عناصر JSX
تجنب استخدام انتشار متغير فردي للخصائص في عناصر JSX، مثل `{...props}`. غالبًا ما تؤدي هذه الممارسة إلى شكل تعليمي أقل قابلية للقراءة وأصعب في الصيانة لأنه من غير الواضح أي الخصائص يتلقاها المكون.
```tsx
/* ❌ - Bad, spreads a single variable prop into the underlying component
*/
const MyComponent = (props: OwnProps) => {
return <OtherComponent {...props} />;
}
```
```tsx
/* ✅ - Good, Explicitly lists all props
* - Enhances readability and maintainability
*/
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
};
```
المبرر:
* نظرة سريعة تسهل معرفة الخصائص التي تمررها التعليمات البرمجية، مما يسهل من الفهم والصيانة.
* يساعد على منع نشوء تعقيدات كبيرة بين المكونات من خلال خصائصها.
* أدوات الفحص تجعل من السهل تحديد الخصائص التي بها أخطاء إملائية أو غير مستخدمة عندما تسرد الخصائص بشكل صريح.
## JavaScript
### استخدم معامل دمج القيم الفارغة `??`
```tsx
// ❌ Bad, can return 'default' even if value is 0 or ''
const value = process.env.MY_VALUE || 'default';
// ✅ Good, will return 'default' only if value is null or undefined
const value = process.env.MY_VALUE ?? 'default';
```
### استخدم معامل الربط الاختياري `?.`
```tsx
// ❌ Bad
onClick && onClick();
// ✅ Good
onClick?.();
```
## TypeScript
### استخدام `type` بدلاً من `interface`
استخدم دائمًا `type` بدلاً من `interface`، لأنهما تقريبًا دائمًا متداخلين، و `type` أكثر مرونة.
```tsx
// ❌ Bad
interface MyInterface {
name: string;
}
// ✅ Good
type MyType = {
name: string;
};
```
### استخدم السلاسل النصية بدلاً من التعدادات
[الحروف المشفوعة](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) هي الطريقة المفضلة للتعامل مع القيم الشبيهة بالأعداد المخصصة في TypeScript. من السهل توسيعها باستخدام Pick و Omit، وتقدم تجربة مطور أفضل، خاصة مع إكمال التعليمات البرمجية.
يمكنك معرفة السبب في أن TypeScript توصي بتجنب الأعداد [هنا](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums).
```tsx
// ❌ Bad, utilizes an enum
enum Color {
Red = "red",
Green = "green",
Blue = "blue",
}
let color = Color.Red;
```
```tsx
// ✅ Good, utilizes a string literal
let color: "red" | "green" | "blue" = "red";
```
#### GraphQL والمكتبات الداخلية
يجب عليك استخدام الأعداد التي يقوم بإنشائها GraphQL codegen.
من الأفضل أيضًا استخدام عدد مخصص عند استخدام مكتبة داخلية، بحيث لا تضطر المكتبة الداخلية إلى تعريض نوع حرف مشفوع غير متعلق بـ API الداخلي.
مثال:
```TSX
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
```
## التنسيق
### استخدام مكونات منسقة
قم بتنسيق المكونات باستخدام [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
<div className="my-class">Hello World</div>
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
قم بإضافة بادئة للمكونات المنسقة بـ "Styled" لتمييزها عن المكونات "الحقيقية".
```tsx
// ❌ Bad
const Title = styled.div`
color: red;
`;
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
### السمات
استخدام السمة لتنسيق معظم المكونات هو النهج المفضل.
#### وحدات القياس
تجنب استخدام قيم `px` أو `rem` مباشرة داخل المكونات المنسقة. بشكل عام، عادة ما تكون القيم المحددة مسبقًا موجودة بالفعل في السمة، لذا يفضل استخدام السمة لتحقيق هذا الغرض.
#### ألوان
امتنع عن تقديم ألوان جديدة، بدلاً من ذلك، استخدم اللوحة الموجودة في السمة. إذا كانت هناك حالة لا تتطابق فيها اللوحة، يرجى ترك تعليق لكي تتمكن الفريق من تصحيحها.
```tsx
// ❌ Bad, directly specifies style values without utilizing the theme
const StyledButton = styled.button`
color: #333333;
font-size: 1rem;
font-weight: 400;
margin-left: 4px;
border-radius: 50px;
`;
```
```tsx
// ✅ Good, utilizes the theme
const StyledButton = styled.button`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
margin-left: ${({ theme }) => theme.spacing(1)};
border-radius: ${({ theme }) => theme.border.rounded};
`;
```
## تطبيق قاعدة "عدم استيراد الأنواع"
تجنب استيراد الأنواع. لفرض هذا المعيار، تتحقق قاعدة Oxlint وتبلغ عن أي استيرادات للأنواع. يساعد هذا على الحفاظ على الاتساق وقابلية القراءة في كود TypeScript.
```tsx
// ❌ Bad
import { type Meta, type StoryObj } from '@storybook/react';
// ❌ Bad
import type { Meta, StoryObj } from '@storybook/react';
// ✅ Good
import { Meta, StoryObj } from '@storybook/react';
```
### لماذا لا نوع استيرادات
* **الاتساق**: من خلال تجنب استيراد الأنواع واستخدام أسلوب استيراد واحد لكل من الأنواع والقيم، يبقى الكود موحدًا في أسلوب استيراد الوحدة.
* **القراءة**: استيرادات بلا نوع تحسن من قابلية القراءة للرمز من خلال توضيح عندما تقوم باستيراد القيم أو الأنواع. يقلل هذا من الغموض ويجعل من الأسهل فهم الهدف من الرموز المستوردة.
* **الصيانة**: يعزز الصيانة داخل قاعدة الكود لأن المطوّرين يمكنهم تحديد مواقع استيرادات الأنواع فقط عند استعراض أو تعديل الكود.
### قاعدة Oxlint
تفرض قاعدة Oxlint، `typescript/consistent-type-imports`, معيار عدم استيراد الأنواع. ستولد هذه القاعدة تحذيرات أو أخطاء عن أي انتهاكات لاستيراد الأنواع.
يرجى ملاحظة أن هذه القاعدة تتناول بشكل خاص حالات الحافة النادرة حيث تحدث استيرادات الأنواع دون قصد. يمنع TypeScript نفسه هذه الممارسة، كما هو موضح في [ملاحظات إصدار TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). في معظم الحالات، لا ينبغي لك أن تستخدم استيرادات الأنواع وحدها.
لضمان امتثال الكود الخاص بك لهذه القاعدة، تأكد من تشغيل Oxlint كجزء من سير العمل الخاص بالتطوير لديك.

View File

@@ -0,0 +1,60 @@
---
title: العمل مع فيجما
info: تعرّف على كيفية التعاون باستخدام Figma الخاص بـ Twenty
---
فيجما هي أداة تصميم واجهات تعاونية تساعد في سد فجوة التواصل بين المصممين والمطورين.
يشرح هذا الدليل كيف يمكنك التعاون مع فيجما.
## الوصول
1. **الوصول إلى الرابط المشترك:** يمكنك الوصول إلى ملف فيجما الخاص بالمشروع [هنا](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty).
2. **تسجيل الدخول:** إذا لم تكن قد سجلت دخولك بالفعل، سيطلب منك فيجما القيام بذلك.
تتوفر المميزات الرئيسية فقط للمستخدمين الذين قاموا بتسجيل الدخول، مثل وضع المطور والقدرة على اختيار إطار مخصص.
<Warning>
لن تتمكن من التعاون بفعالية بدون حساب.
</Warning>
## هيكل فيجما
من الشريط الجانبي الأيسر، يمكنك الوصول إلى الصفحات المختلفة في Figma الخاص بـ Twenty. هكذا هم مُنظمون:
* **صفحة المكونات:** هذه هي الصفحة الأولى. يستخدمها المصمم لإنشاء وتنظيم العناصر التصميمية القابلة لإعادة الاستخدام في ملف التصميم. على سبيل المثال، الأزرار، الأيقونات، الرموز أو أي مكونات أخرى قابلة لإعادة الاستخدام. تعمل على الحفاظ على التناسق عبر التصميم.
* **الصفحة الرئيسية:** الصفحة الثانية هي الصفحة الرئيسية التي تظهر واجهة المستخدم الكاملة للمشروع. يمكنك الضغط على ***تشغيل*** لاستخدام النموذج الأولي الكامل للتطبيق.
* **صفحات الميزات:** الصفحات الأخرى تكون مخصصة عادة للميزات قيد التقدم. تحتوي على تصميم الميزات أو الوحدات المحددة للتطبيق أو الموقع الإلكتروني. عادة ما تكون لا تزال قيد التقدم.
## نصائح مفيدة
مع الوصول لعرض فقط، لا يمكنك تحرير التصميم، ولكن يمكنك الوصول إلى جميع الميزات التي ستكون مفيدة لتحويل التصميمات إلى كود.
### استخدم وضع المطور
يعزز وضع المطور في فيجما إنتاجية المطورين من خلال توفير التنقل السهل في التصميم، إدارة فعالة للموارد، أدوات اتصال فعالة، تكاملات طقم الأدوات، مقتطفات كود سريعة، ومعلومات رئيسية عن الطبقات، مما يسد الفجوة بين التصميم والتطوير. يمكنك معرفة المزيد عن وضع المطور [هنا](https://www.figma.com/dev-mode/).
قم بالتبديل إلى وضع "المطور" في الجزء الأيمن من شريط الأدوات لتشاهد مواصفات التصميم، نسخ CSS، والوصول إلى الموارد.
### استخدم النموذج الأولي
انقر على أي عنصر على اللوحة واضغط على زر “تشغيل” في نهاية الحافة العلوية لواجهة المستخدم للوصول إلى عرض النموذج الأولي. يسمح لك وضع النموذج الأولي بالتفاعل مع التصميم كما لو كان المنتج النهائي. يوضح التدفق بين الشاشات وكيف تتصرف عناصر الواجهة مثل الأزرار، الروابط، أو القوائم عند التفاعل معها.
1. **فهم الانتقالات والرسوم المتحركة:** في وضع النموذج الأولي، يمكنك مشاهدة أي انتقالات أو رسوم متحركة أضافها المصمم بين الشاشات أو عناصر واجهة المستخدم، مما يوفر تعليمات بصرية واضحة للمطورين حول السلوك والنمط المقصود.
2. **توضيح التنفيذ:** يمكن أن يساعد النموذج الأولي أيضًا في تقليل الغموض. يمكن للمطورين التفاعل معه لاكتساب فهم أفضل لوظيفة أو مظهر عناصر معينة.
للحصول على تفاصيل شاملة وإرشادات لتعلم منصة فيجما، يمكنك زيارة [وثائق فيجما الرسمية](https://help.figma.com/hc/en-us).
### قياس المسافات
حدد عنصرًا، اضغط مع الاستمرار على مفتاح `Option` (لماك) أو مفتاح `Alt` (لويندوز)، ثم مرر فوق عنصر آخر لرؤية المسافة بينهما.
### إضافة فيجما لـ VSCode (موصى به)
[فيجما لـ VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension) يتيح لك التنقل ومعاينة ملفات التصميم، التعاون مع المصممين، تتبع التغييرات، وتسريع التنفيذ - دون مغادرة محرر النصوص الخاص بك.
إنها جزء من الإضافات الموصى بها لدينا.
## التعاون
1. **استخدام التعليقات:** يمكنك استخدام ميزة التعليق بالنقر على أيقونة الفقاعة في الجزء الأيسر من شريط الأدوات.
2. **دردشة المؤشر:** ميزة لطيفة في فيجما هي دردشة المؤشر. فقط اضغط على `;` على ماك و`/` على ويندوز لإرسال رسالة إذا رأيت شخصًا آخر يستخدم فيجما في نفس الوقت الذي تستخدمه فيه.

View File

@@ -0,0 +1,318 @@
---
title: الإعداد المحلي
icon: laptop-code
description: الدليل للمساهمين (أو المطورين الفضوليين) الذين يرغبون في تشغيل Twenty محلياً.
---
## المتطلبات الأساسية
<Tabs>
<Tab title="Linux و macOS">
قبل أن تتمكن من تثبيت واستخدام Twenty، تأكد من تثبيت الأمور التالية على جهاز الكمبيوتر الخاص بك:
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Node v24.5.0](https://nodejs.org/en/download)
* [yarn v4](https://yarnpkg.com/getting-started/install)
* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
<Warning>
لن يعمل `npm` ، يجب عليك استخدام `yarn` بدلًا من ذلك. يأتي Yarn الآن مع Node.js، لذا لست بحاجة إلى تثبيته بشكل منفصل.
عليك فقط تشغيل `corepack enable` لتفعيل Yarn إذا لم تقم بذلك بعد.
</Warning>
</Tab>
<Tab title="ويندوز (WSL)">
1. ثبّت WSL
افتح PowerShell كمسؤول ثم نفّذ:
```powershell
wsl --install
```
يجب أن ترى الآن مطالبة لإعادة تشغيل جهاز الكمبيوتر الخاص بك. إذا لم يكن كذلك، فأعد تشغيله يدويًا.
عند إعادة التشغيل، ستُفتح نافذة PowerShell وسيتم تثبيت Ubuntu. قد يستغرق هذا وقتًا طويلاً.
سترى مطالبة لإنشاء اسم المستخدم وكلمة المرور لتثبيت Ubuntu الخاص بك.
2. تثبيت وإعداد git
```bash
sudo apt-get install git
git config --global user.name "Your Name"
git config --global user.email "youremail@domain.com"
```
3. تثبيت nvm و node.js و yarn
<Warning>
استخدم `nvm` لتثبيت نسخة `node` الصحيحة. الملف `.nvmrc` يضمن استخدام جميع المشاركين لنفس النسخة.
</Warning>
```bash
sudo apt-get install curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
```
أغلق وأعد فتح برنامجك الطرفي لاستخدام nvm. ثم قم بتشغيل الأوامر التالية.
```bash
nvm install # installs recommended node version
nvm use # use recommended node version
corepack enable
```
</Tab>
</Tabs>
---
## الخطوة 1: استنساخ Git
في الطرفية الخاصة بك، قم بتشغيل الأمر التالي.
<Tabs>
<Tab title="SSH (موصى به)">
إذا لم تكن قد أعددت مفاتيح SSH بالفعل، يمكنك معرفة كيفية القيام بذلك [هنا](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh).
```bash
git clone git@github.com:twentyhq/twenty.git
```
</Tab>
<Tab title="HTTPS">
```bash
git clone https://github.com/twentyhq/twenty.git
```
</Tab>
</Tabs>
## الخطوة 2: انتقل إلى جذر المشروع
```bash
cd twenty
```
يجب تشغيل جميع الأوامر في الخطوات التالية من جذر المشروع.
## الخطوة 3: إعداد قاعدة بيانات PostgreSQL
<Tabs>
<Tab title="Linux">
**الخيار 1 (المفضل):** لتوفير قاعدة بياناتك محليًا:
استخدم الرابط التالي لتثبيت PostgreSQL على جهاز Linux الخاص بك: [تثبيت PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
ملاحظة: قد تحتاج إلى إضافة `sudo -u postgres` إلى الأمر قبل `psql` لتجنب أخطاء الإذن.
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="نظام Mac OS">
**الخيار 1 (المفضل):** لتوفير قاعدة بياناتك محليًا مع `brew`:
```bash
brew install postgresql@16
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
brew services start postgresql@16
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
يمكنك التحقق مما إذا كان خادم PostgreSQL يعمل بتنفيذ:
```bash
brew services list
```
قد لا يقوم المُثبِّت بإنشاء المستخدم `postgres` افتراضيًا عند التثبيت
عبر Homebrew على macOS. بدلاً من ذلك، فإنه ينشئ دور PostgreSQL يطابق
اسم المستخدم الخاص بك في MacOS (مثل "john").
للتحقق وإنشاء المستخدم `postgres` إذا لزم الأمر، اتبع هذه الخطوات:
```bash
# Connect to PostgreSQL
psql postgres
or
psql -U $(whoami) -d postgres
```
بمجرد أن تكون عند مطالبة psql (postgres=#)، قم بتشغيل:
```bash
# List existing PostgreSQL roles
\du
```
سترى مخرجات مشابهة ل:
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
john | Superuser | {}
```
إذا لم ترَ دور `postgres` مدرجًا، انتقل إلى الخطوة التالية.
قم بإنشاء دور `postgres` يدويًا:
```bash
CREATE ROLE postgres WITH SUPERUSER LOGIN;
```
يقوم هذا بإنشاء دور مشرف نظام باسم `postgres` مع إمكانية تسجيل الدخول.
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
postgres | Superuser | {}
john | Superuser | {}
```
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="ويندوز (WSL)">
يجب أن تُنفذ جميع الخطوات التالية في تيرمينال WSL (داخل جهازك الافتراضي)
**الخيار 1:** لتوفير قاعدة بيانات PostgreSQL الخاصة بك محليًا:
استخدم الرابط التالي لتثبيت PostgreSQL على جهاز Linux الافتراضي الخاص بك: [تثبيت PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
ملاحظة: قد تحتاج إلى إضافة `sudo -u postgres` إلى الأمر قبل `psql` لتجنب أخطاء الإذن.
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
تشغيل Docker على WSL يضيف طبقة إضافية من التعقيد.
استخدم هذا الخيار فقط إذا كنت مرتاحًا مع الخطوات الإضافية المتضمنة، بما في ذلك تشغيل [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
يمكنك الآن الوصول إلى قاعدة البيانات على `localhost:5432`.
إذا استخدمت خيار Docker أعلاه، فإن بيانات الاعتماد الافتراضية هي اسم المستخدم `postgres` وكلمة المرور `postgres`. بالنسبة لتثبيتات PostgreSQL الأصلية، استخدم بيانات الاعتماد والأدوار المُكوَّنة على جهازك.
## الخطوة 4: إعداد قاعدة بيانات Redis (للتخزين المؤقت)
يتطلب Twenty مخزن بيانات Redis لتقديم أفضل أداء.
<Tabs>
<Tab title="Linux">
**الخيار 1:** لتوفير Redis الخاص بك محليًا:
استخدم الرابط التالي لتثبيت Redis على جهاز Linux: [تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Mac OS">
**الخيار 1 (المفضل):** لتوفير Redis الخاص بك محليًا مع `brew`:
```bash
brew install redis
```
ابدأ تشغيل خادم Redis:
```bash
brew services start redis
```
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="ويندوز (WSL)">
**الخيار 1:** لتوفير Redis الخاص بك محليًا:
استخدم الرابط التالي لتثبيت Redis على جهاز Linux الافتراضي الخاص بك: [تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**الخيار 2:** إذا كنت قد قمت بتثبيت docker:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
إذا كنت بحاجة إلى واجهة رسومية للعميل، نوصي بـ [Redis Insight](https://redis.io/insight/) (يتوفر إصدار مجاني).
## الخطوة 5: إعداد متغيرات البيئة
استخدم متغيرات البيئة أو ملفات `.env` لتكوين مشروعك. المزيد من المعلومات [هنا](/l/ar/developers/self-host/capabilities/setup).
انسخ ملفات `.env.example` الموجودة في `/front` و`/server`:
```bash
cp ./packages/twenty-front/.env.example ./packages/twenty-front/.env
cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
```
<Info>
**وضع تعدد مساحات العمل:** بشكل افتراضي، يعمل Twenty في وضع مساحة عمل واحدة حيث يمكن إنشاء مساحة عمل واحدة فقط. لتمكين دعم تعدد مساحات العمل (مفيد لاختبار الميزات المعتمدة على النطاقات الفرعية)، عيّن `IS_MULTIWORKSPACE_ENABLED=true` في ملف الخادم `.env`. راجع [وضع تعدد مساحات العمل](/l/ar/developers/self-host/capabilities/setup#multi-workspace-mode) للحصول على التفاصيل.
</Info>
## الخطوة 6: تثبيت التبعيات
لبناء خادم Twenty وزرع بعض البيانات في قاعدة البيانات الخاصة بك، قم بتشغيل الأمر التالي:
```bash
yarn
```
لاحظ أن `npm` أو `pnpm` لن تعملا
## الخطوة 7: تشغيل المشروع
<Tabs>
<Tab title="Linux">
اعتمادًا على توزيعة Linux الخاصة بك، قد يتم بدء خادم Redis تلقائيًا.
إذا لم يكن كذلك، تحقق من [دليل تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) لتوزيعتك.
</Tab>
<Tab title="نظام Mac OS">
من المفترض أن يكون Redis قد تم تشغيله بالفعل. إذا لم يكن كذلك، قم بتشغيل:
```bash
brew services start redis
```
</Tab>
<Tab title="ويندوز (WSL)">
اعتمادًا على توزيعة Linux الخاصة بك، قد يتم بدء خادم Redis تلقائيًا.
إذا لم يكن كذلك، تحقق من [دليل تثبيت ريديس](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) لتوزيعتك.
</Tab>
</Tabs>
قم بضبط قاعدة بياناتك بالأمر التالي:
```bash
npx nx database:reset twenty-server
```
ابدأ الخادم والخادم الثانوي وخدمات الواجهة الأمامية:
```bash
npx nx start twenty-server
npx nx worker twenty-server
npx nx start twenty-front
```
بدلاً من ذلك، يمكنك بدء جميع الخدمات مرة واحدة:
```bash
npx nx start
```
## الخطوة الثامنة: استخدم Twenty
**الواجهة الأمامية**
ستكون واجهة Twenty الأمامية تعمل على [http://localhost:3001](http://localhost:3001).
يمكنك تسجيل الدخول باستخدام حساب العرض التوضيحي الافتراضي: `tim@apple.dev` (كلمة المرور: `tim@apple.dev`)
**الخلفية**
* سيكون خادم Twenty متصلاً ويعمل على [http://localhost:3000](http://localhost:3000)
* يمكن الوصول إلى واجهة برمجة التطبيقات GraphQL في [http://localhost:3000/graphql](http://localhost:3000/graphql)
* يمكن الوصول إلى واجهة برمجة التطبيقات REST في [http://localhost:3000/rest](http://localhost:3000/rest)
## استكشاف الأخطاء وإصلاحها
إذا واجهت أي مشكلة، فارجع إلى [استكشاف الأخطاء وإصلاحها](/l/ar/developers/self-host/capabilities/troubleshooting) للحصول على الحلول.

View File

@@ -0,0 +1,77 @@
---
title: أوامر
icon: terminal
description: أوامر مفيدة لتطوير Twenty.
---
يمكن تشغيل الأوامر من جذر المستودع باستخدام `npx nx`. استخدم `npx nx run {project}:{command}` للاستهداف الصريح.
## بدء التطبيق
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## قاعدة البيانات
```bash
npx nx database:reset twenty-server # Reset and seed database
npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## فحص الشيفرة
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## التحقق من الأنواع
```bash
npx nx typecheck twenty-front
npx nx typecheck twenty-server
```
## الاختبار
```bash
# Frontend
npx nx test twenty-front # Jest unit tests
npx nx storybook:build twenty-front # Build Storybook
npx nx storybook:test twenty-front # Storybook tests
# Backend
npx nx run twenty-server:test:unit # Unit tests
npx nx run twenty-server:test:integration # Integration tests
npx nx run twenty-server:test:integration:with-db-reset # Integration with DB reset
# Single file (fastest)
npx jest path/to/test.test.ts --config=packages/{project}/jest.config.mjs
```
## جراف كيو إل
```bash
npx nx run twenty-front:graphql:generate # Regenerate types
npx nx run twenty-front:graphql:generate --configuration=metadata # Metadata schema
```
## الترجمات
```bash
npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## البناء
```bash
npx nx build twenty-shared # Must be built first
npx nx build twenty-front
npx nx build twenty-server
```

View File

@@ -0,0 +1,31 @@
---
title: المساهمة
description: ساهم في تطوير Twenty المفتوح المصدر.
---
<Frame>
<img src="/images/user-guide/github/github-header.png" alt="الذكاء الاصطناعي" />
</Frame>
## نظرة عامة
Twenty مفتوح المصدر ويرحب بمساهمات المجتمع. سواء كنت تقوم بإصلاح الأخطاء، أو إضافة الميزات، أو تحسين التوثيق، فإن مساهماتك تساعد في جعل Twenty أفضل للجميع.
## طرق المساهمة
* **الإبلاغ عن الأخطاء**: ساعد في تحديد المشكلات وتوثيقها
* **تقديم الميزات**: اقترح ونفّذ وظائف جديدة
* **تحسين التوثيق**: اجعل وثائقنا أوضح وأكثر فائدة
* **تطوير الواجهة الأمامية**: اعمل على واجهة المستخدم المبنية على React
* **تطوير الواجهة الخلفية**: ساهم في خادم NestJS
## البدء
<CardGroup cols={2}>
<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/folder-architecture-front">
ساهم في واجهة المستخدم
</Card>
</CardGroup>

View File

@@ -0,0 +1,176 @@
---
title: دليل الأسلوب
icon: paintbrush
description: اتفاقيات الشيفرة وأفضل الممارسات للمساهمة في Twenty.
---
## React
### المكوّنات الوظيفية فقط
استخدم دائمًا مكوّنات TSX الوظيفية مع تصديرات مسمّاة.
```tsx
// ❌ Bad
const MyComponent = () => {
return <div>Hello World</div>;
};
export default MyComponent;
// ✅ Good
export function MyComponent() {
return <div>Hello World</div>;
};
```
### الخصائص
أنشئ نوعًا باسم `{ComponentName}Props`. استخدم التفكيك. لا تستخدم `React.FC`.
```tsx
type MyComponentProps = {
name: string;
};
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
### لا تستخدم نشر الخصائص من متغير واحد
```tsx
// ❌ Bad
const MyComponent = (props: MyComponentProps) => <Other {...props} />;
// ✅ Good
const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1, prop2 }} />;
```
## إدارة الحالة
### ذرات Jotai للحالة العامة
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
export const myAtomState = createAtomState<string>({
key: 'myAtomState',
defaultValue: 'default value',
});
```
* فضّل الذرات على تمرير الخصائص عبر المستويات (prop drilling)
* لا تستخدم `useRef` للحالة — استخدم `useState` أو الذرات
* استخدم عائلات الذرات والمحددات للقوائم
### تجنّب عمليات إعادة التصيير غير الضرورية
* انقل `useEffect` وجلب البيانات إلى مكوّنات جانبية شقيقة (sidecar)
* فضّل معالِجات الأحداث (`handleClick`, `handleChange`) على `useEffect`
* لا تستخدم `React.memo()` — أصلِح السبب الجذري بدلًا من ذلك
* حدّد استخدام `useCallback` / `useMemo`
```tsx
// ❌ Bad — useEffect in the same component causes re-renders
export const Page = () => {
const [data, setData] = useAtomState(dataState);
const [dep] = useAtomState(depState);
useEffect(() => { setData(dep); }, [dep]);
return <div>{data}</div>;
};
// ✅ Good — extract into sibling
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [dep] = useAtomState(depState);
useEffect(() => { setData(dep); }, [dep]);
return <></>;
};
export const Page = () => {
const [data] = useAtomState(dataState);
return <div>{data}</div>;
};
```
## TypeScript
* **`type` بدلًا من `interface`** — أكثر مرونة وأسهل في التركيب
* **النصوص الحرفية بدل التعدادات** — باستثناء تعدادات codegen الخاصة بـ GraphQL وواجهات برمجة تطبيقات المكتبة الداخلية
* **بدون `any`** — يتم فرض TypeScript الصارم
* **عدم استيراد الأنواع** — استخدم استيرادات عادية (مفروض بواسطة Oxlint `typescript/consistent-type-imports`)
* **استخدم [Zod](https://github.com/colinhacks/zod)** للتحقق وقت التشغيل من الكائنات غير محددة النوع
## JavaScript
```tsx
// Use nullish-coalescing (??) instead of ||
const value = process.env.MY_VALUE ?? 'default';
// Use optional chaining
onClick?.();
```
## التسمية
* **المتغيرات**: camelCase، وصفية (`email` وليس `value`، `fieldMetadata` وليس `fm`)
* **الثوابت**: SCREAMING_SNAKE_CASE
* **الأنواع/الفئات**: PascalCase
* **الملفات/المجلدات**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **معالجات الأحداث**: `handleClick` (وليس `onClick` لدالة المعالج)
* **خصائص المكوّن**: ابدأ باسم المكوّن (`ButtonProps`)
* **مكوّنات Styled**: ابدأ بـ `Styled` (`StyledTitle`)
## التنسيق
استخدم مكوّنات [Linaria](https://github.com/callstack/linaria) المنسقة. استخدم قيم السمة — وتجنّب القيم المضمّنة صراحة مثل `px` و`rem` أو الألوان.
```tsx
// ❌ Bad
const StyledButton = styled.button`
color: #333333;
font-size: 1rem;
margin-left: 4px;
`;
// ✅ Good
const StyledButton = styled.button`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
```
## استيرادات
استخدم الأسماء المستعارة بدل المسارات النسبية:
```tsx
// ❌ Bad
import { Foo } from '../../../../../testing/decorators/Foo';
// ✅ Good
import { Foo } from '~/testing/decorators/Foo';
import { Bar } from '@/modules/bar/components/Bar';
```
## هيكلية المجلدات
```
front
└── modules/ # Feature modules
│ └── module1/
│ ├── components/
│ ├── constants/
│ ├── contexts/
│ ├── graphql/ (fragments, queries, mutations)
│ ├── hooks/
│ ├── states/ (atoms, selectors)
│ ├── types/
│ └── utils/
└── pages/ # Route-level components
└── ui/ # Reusable UI components (display, input, feedback, ...)
```
* يمكن للوحدات الاستيراد من وحدات أخرى، لكن يجب أن يبقى `ui/` خاليًا من التبعيات
* استخدم المجلدات الفرعية `internal/` للشيفرة الخاصة بالوحدة
* المكوّنات أقل من 300 سطر، والخدمات أقل من 500 سطر