Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3311ca1a58 | |||
| 718a57172a | |||
| cb128c4158 | |||
| 0288545d7b | |||
| 996947a3cb | |||
| 05917e23a9 | |||
| 3f0cfe66d1 | |||
| 30dc70a943 | |||
| 7221b45ff7 | |||
| 49d05f3ca7 | |||
| 0e44e07fcf | |||
| 9fe2a07c55 | |||
| 63f17eec2c | |||
| e806d36099 | |||
| 9f5a8735c9 | |||
| c0e6aa1c0b | |||
| 9342b16aad | |||
| cfad24da48 | |||
| 86fbf69e95 | |||
| 9667b3f369 | |||
| 76c7639eb3 | |||
| 4ed09a3feb | |||
| def5ea5764 | |||
| 81698ff32c | |||
| c0c51f2ef5 | |||
| 3bbaff801a | |||
| 2d2fc06265 | |||
| 8a7a19f312 | |||
| 2f0103faa5 | |||
| 08bfbfda45 | |||
| c025a0c2b8 | |||
| 5e19361494 | |||
| f11d76d6cf | |||
| 3aedce9af7 | |||
| 8e8ecfb8a3 | |||
| 120096346a | |||
| fde8168a85 | |||
| 2674589b44 | |||
| f325509d96 | |||
| a9649b06d5 | |||
| 0b4bf97f35 | |||
| 4ea8245387 | |||
| 0fa8063054 | |||
| 546114d07f | |||
| 92824c6533 | |||
| bc4ae35bc4 | |||
| 1b805a36f3 | |||
| f7ea1d9500 | |||
| cbb0f212cf | |||
| 0af980a783 | |||
| e01b641a05 | |||
| 50be97422d | |||
| 4ae8381ffc | |||
| 856859f9f3 | |||
| 98d67e0f6b | |||
| 1fa55bfd02 | |||
| 001c2097a3 | |||
| 9107f5bbc7 | |||
| 435e21d23f | |||
| 448241540d | |||
| e7f958c9cf | |||
| 98e73791a4 | |||
| f3857c41b8 | |||
| 2c02f2c499 | |||
| 121788c42f | |||
| ecb7270a7b | |||
| e365b546d9 | |||
| f080b952eb | |||
| b84a8588be | |||
| e6a415b438 | |||
| d81f006979 | |||
| eaf9fe27b2 | |||
| b339c93699 | |||
| 887371054a | |||
| b56f85f36a | |||
| 1ed13182d0 | |||
| 20d997bc5a | |||
| 0f2af6a6cb | |||
| c6ec764b23 | |||
| 959cbfb725 | |||
| 458a61ed30 | |||
| 9a3852bf04 | |||
| c369baf63a | |||
| 9e8024a07a | |||
| df196b4a03 |
@@ -22,7 +22,7 @@ This directory contains Twenty's development guidelines and best practices in th
|
||||
|
||||
### React Development
|
||||
- **react-general-guidelines.mdc** - Core React development principles (Auto-attached to React files)
|
||||
- **react-state-management.mdc** - State management approaches with Recoil (Auto-attached to state files)
|
||||
- **react-state-management.mdc** - State management approaches with Jotai (Auto-attached to state files)
|
||||
|
||||
### Testing & Quality
|
||||
- **testing-guidelines.mdc** - Testing strategies and best practices (Auto-attached to test files)
|
||||
|
||||
@@ -7,7 +7,7 @@ alwaysApply: true
|
||||
# Twenty Architecture
|
||||
|
||||
## Tech Stack
|
||||
- **Frontend**: React 18, TypeScript, Recoil, Styled Components, Vite
|
||||
- **Frontend**: React 18, TypeScript, Jotai, Styled Components, Vite
|
||||
- **Backend**: NestJS, TypeORM, PostgreSQL, Redis, GraphQL
|
||||
- **Monorepo**: Nx workspace with yarn
|
||||
|
||||
|
||||
@@ -4,16 +4,20 @@ alwaysApply: false
|
||||
---
|
||||
# React State Management
|
||||
|
||||
## Recoil Patterns
|
||||
## Jotai Patterns
|
||||
```typescript
|
||||
// ✅ Atoms for primitive state
|
||||
export const currentUserState = atom<User | null>({
|
||||
// ✅ Atoms for primitive state (use createAtomState for keyed state with optional persistence)
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
export const currentUserState = createAtomState<User | null>({
|
||||
key: 'currentUserState',
|
||||
default: null,
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
// ✅ Selectors for derived state
|
||||
export const userDisplayNameSelector = selector({
|
||||
// ✅ Derived atoms for computed state (use createAtomSelector)
|
||||
import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector';
|
||||
|
||||
export const userDisplayNameSelector = createAtomSelector({
|
||||
key: 'userDisplayNameSelector',
|
||||
get: ({ get }) => {
|
||||
const user = get(currentUserState);
|
||||
@@ -21,13 +25,30 @@ export const userDisplayNameSelector = selector({
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ Atom families for dynamic atoms
|
||||
export const userByIdState = atomFamily<User | null, string>({
|
||||
// ✅ Atom factory pattern for dynamic atoms (use createAtomFamilyState)
|
||||
import { createAtomFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomFamilyState';
|
||||
|
||||
export const userByIdState = createAtomFamilyState<User | null, string>({
|
||||
key: 'userByIdState',
|
||||
default: null,
|
||||
defaultValue: null,
|
||||
});
|
||||
```
|
||||
|
||||
## Jotai Hooks
|
||||
```typescript
|
||||
// useAtomState - read and write
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
|
||||
// useAtomStateValue - read only
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
// useSetAtomState - write only
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
```
|
||||
|
||||
## Provider
|
||||
Jotai works without a Provider by default. For scoped stores or testing, use `Provider` from `jotai`.
|
||||
|
||||
## Local State Guidelines
|
||||
```typescript
|
||||
// ✅ Multiple useState for unrelated state
|
||||
@@ -74,7 +95,7 @@ const increment = useCallback(() => {
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
- Use atom families for dynamic data collections
|
||||
- Implement proper selector caching
|
||||
- Avoid heavy computations in selectors
|
||||
- Use atom factory pattern (createAtomFamilyState) for dynamic data collections
|
||||
- Derived atoms (createAtomSelector) are automatically memoized by Jotai
|
||||
- Avoid heavy computations in derived atoms
|
||||
- Batch state updates when possible
|
||||
|
||||
@@ -771,16 +771,16 @@ jobs:
|
||||
kill $(cat /tmp/main-server.pid) || true
|
||||
fi
|
||||
|
||||
- name: Upload API specifications and diffs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api-specifications-and-diffs
|
||||
path: |
|
||||
/tmp/main-server.log
|
||||
/tmp/current-server.log
|
||||
*-api.json
|
||||
*-schema-introspection.json
|
||||
*-diff.md
|
||||
*-diff.json
|
||||
# - name: Upload API specifications and diffs
|
||||
# if: always()
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: api-specifications-and-diffs
|
||||
# path: |
|
||||
# /tmp/main-server.log
|
||||
# /tmp/current-server.log
|
||||
# *-api.json
|
||||
# *-schema-introspection.json
|
||||
# *-diff.md
|
||||
# *-diff.json
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
needs: changed-files-check
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
steps:
|
||||
- name: Fetch custom Github Actions and base branch history
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -14,8 +14,8 @@ concurrency:
|
||||
|
||||
env:
|
||||
# restore-cache action adds 'v4-' prefix and '-<branch>-<sha>' suffix to the key
|
||||
STORYBOOK_BUILD_CACHE_KEY_FOR_RESTORE_ACTION: storybook-build-ubuntu-latest-8-cores-runner
|
||||
STORYBOOK_BUILD_CACHE_KEY_FOR_SAVE_ACTION: v4-storybook-build-ubuntu-latest-8-cores-runner-${{ github.ref_name }}-${{ github.sha }}
|
||||
STORYBOOK_BUILD_CACHE_KEY_FOR_RESTORE_ACTION: storybook-build-depot-ubuntu-24.04-8-runner
|
||||
STORYBOOK_BUILD_CACHE_KEY_FOR_SAVE_ACTION: v4-storybook-build-depot-ubuntu-24.04-8-runner-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
jobs:
|
||||
changed-files-check:
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
needs: changed-files-check
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
env:
|
||||
REACT_APP_SERVER_BASE_URL: http://localhost:3000
|
||||
steps:
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
key: ${{ env.STORYBOOK_BUILD_CACHE_KEY_FOR_SAVE_ACTION }}
|
||||
front-sb-test:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
needs: front-sb-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -98,52 +98,52 @@ jobs:
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Run storybook tests
|
||||
run: npx nx storybook:test twenty-front --configuration=${{ matrix.storybook_scope }} --shard=${{ matrix.shard }}/${{ env.SHARD_COUNTER }}
|
||||
- name: Rename coverage file
|
||||
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:
|
||||
retention-days: 1
|
||||
name: coverage-artifacts-${{ matrix.storybook_scope }}-${{ github.run_id }}-${{ matrix.shard }}
|
||||
path: packages/twenty-front/coverage/storybook/coverage-shard-${{matrix.shard}}.json
|
||||
merge-reports-and-check-coverage:
|
||||
timeout-minutes: 30
|
||||
runs-on: depot-ubuntu-24.04
|
||||
needs: front-sb-test
|
||||
env:
|
||||
PATH_TO_COVERAGE: packages/twenty-front/coverage/storybook
|
||||
strategy:
|
||||
matrix:
|
||||
storybook_scope: [modules, pages, performance]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: coverage-artifacts-${{ matrix.storybook_scope }}-${{ github.run_id }}-*
|
||||
merge-multiple: true
|
||||
path: coverage-artifacts
|
||||
- name: Merge coverage reports
|
||||
run: |
|
||||
mkdir -p ${{ env.PATH_TO_COVERAGE }}
|
||||
npx nyc merge coverage-artifacts ${{ env.PATH_TO_COVERAGE }}/coverage-storybook.json
|
||||
- name: Checking coverage
|
||||
run: npx nx storybook:coverage twenty-front --checkCoverage=true --configuration=${{ matrix.storybook_scope }}
|
||||
# - name: Rename coverage file
|
||||
# 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:
|
||||
# retention-days: 1
|
||||
# name: coverage-artifacts-${{ matrix.storybook_scope }}-${{ github.run_id }}-${{ matrix.shard }}
|
||||
# path: packages/twenty-front/coverage/storybook/coverage-shard-${{matrix.shard}}.json
|
||||
# merge-reports-and-check-coverage:
|
||||
# timeout-minutes: 30
|
||||
# runs-on: depot-ubuntu-24.04
|
||||
# needs: front-sb-test
|
||||
# env:
|
||||
# PATH_TO_COVERAGE: packages/twenty-front/coverage/storybook
|
||||
# strategy:
|
||||
# matrix:
|
||||
# storybook_scope: [modules, pages, performance]
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
# - name: Install dependencies
|
||||
# uses: ./.github/actions/yarn-install
|
||||
# - uses: actions/download-artifact@v4
|
||||
# with:
|
||||
# pattern: coverage-artifacts-${{ matrix.storybook_scope }}-${{ github.run_id }}-*
|
||||
# merge-multiple: true
|
||||
# path: coverage-artifacts
|
||||
# - name: Merge coverage reports
|
||||
# run: |
|
||||
# mkdir -p ${{ env.PATH_TO_COVERAGE }}
|
||||
# npx nyc merge coverage-artifacts ${{ env.PATH_TO_COVERAGE }}/coverage-storybook.json
|
||||
# - name: Checking coverage
|
||||
# run: npx nx storybook:coverage twenty-front --checkCoverage=true --configuration=${{ matrix.storybook_scope }}
|
||||
front-chromatic-deployment:
|
||||
timeout-minutes: 30
|
||||
if: false
|
||||
needs: front-sb-build
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
env:
|
||||
REACT_APP_SERVER_BASE_URL: http://127.0.0.1:3000
|
||||
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
needs: changed-files-check
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=10240"
|
||||
steps:
|
||||
@@ -229,12 +229,12 @@ jobs:
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Build frontend
|
||||
run: npx nx build twenty-front
|
||||
- name: Upload frontend build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-build
|
||||
path: packages/twenty-front/build
|
||||
retention-days: 1
|
||||
# - name: Upload frontend build artifact
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: frontend-build
|
||||
# path: packages/twenty-front/build
|
||||
# retention-days: 1
|
||||
e2e-test:
|
||||
runs-on: depot-ubuntu-24.04
|
||||
needs: [changed-files-check-e2e, front-build]
|
||||
@@ -296,15 +296,18 @@ jobs:
|
||||
cp packages/twenty-front/.env.example packages/twenty-front/.env
|
||||
npx nx reset:env:e2e-testing-server twenty-server
|
||||
|
||||
- name: Download frontend build artifact
|
||||
if: needs.front-build.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-build
|
||||
path: packages/twenty-front/build
|
||||
# - name: Download frontend build artifact
|
||||
# if: needs.front-build.result == 'success'
|
||||
# uses: actions/download-artifact@v4
|
||||
# with:
|
||||
# name: frontend-build
|
||||
# path: packages/twenty-front/build
|
||||
|
||||
- name: Build frontend (if not available from front-build)
|
||||
if: needs.front-build.result == 'skipped'
|
||||
# - name: Build frontend (if not available from front-build)
|
||||
# if: needs.front-build.result == 'skipped'
|
||||
# run: NODE_ENV=production NODE_OPTIONS="--max-old-space-size=10240" npx nx build twenty-front
|
||||
|
||||
- name: Build frontend
|
||||
run: NODE_ENV=production NODE_OPTIONS="--max-old-space-size=10240" npx nx build twenty-front
|
||||
|
||||
- name: Build server
|
||||
@@ -336,12 +339,12 @@ jobs:
|
||||
- name: Run Playwright tests
|
||||
run: npx nx test twenty-e2e-testing
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: packages/twenty-e2e-testing/run_results/
|
||||
retention-days: 30
|
||||
# - uses: actions/upload-artifact@v4
|
||||
# if: always()
|
||||
# with:
|
||||
# name: playwright-report
|
||||
# path: packages/twenty-e2e-testing/run_results/
|
||||
# retention-days: 30
|
||||
|
||||
ci-front-status-check:
|
||||
if: always() && !cancelled()
|
||||
@@ -352,7 +355,7 @@ jobs:
|
||||
changed-files-check,
|
||||
front-task,
|
||||
front-build,
|
||||
merge-reports-and-check-coverage,
|
||||
# merge-reports-and-check-coverage,
|
||||
front-sb-test,
|
||||
front-sb-build,
|
||||
]
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
tasks: ${{ matrix.task }}
|
||||
sdk-e2e-test:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
needs: [changed-files-check, sdk-test]
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
services:
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
needs: changed-files-check
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
services:
|
||||
postgres:
|
||||
image: twentycrm/twenty-postgres-spilo
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
key: ${{ steps.restore-server-setup-cache.outputs.cache-primary-key }}
|
||||
server-test:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
needs: server-setup
|
||||
steps:
|
||||
- name: Fetch custom Github Actions and base branch history
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
|
||||
server-integration-test:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: depot-ubuntu-24.04-8
|
||||
needs: server-setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
name: CI Zapier
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
changed-files-check:
|
||||
uses: ./.github/workflows/changed-files.yaml
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-zapier/**
|
||||
packages/twenty-server/**
|
||||
!packages/twenty-zapier/package.json
|
||||
!packages/twenty-zapier/CHANGELOG.md
|
||||
zapier-test:
|
||||
needs: changed-files-check
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
timeout-minutes: 30
|
||||
runs-on: depot-ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
task: [lint, typecheck, test, validate]
|
||||
steps:
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
- name: Fetch custom Github Actions and base branch history
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Build
|
||||
run: npx nx build twenty-zapier
|
||||
- name: Run ${{ matrix.task }} task
|
||||
uses: ./.github/actions/nx-affected
|
||||
with:
|
||||
tag: scope:zapier
|
||||
tasks: ${{ matrix.task }}
|
||||
ci-zapier-status-check:
|
||||
if: always() && !cancelled()
|
||||
timeout-minutes: 5
|
||||
runs-on: depot-ubuntu-24.04
|
||||
needs: [changed-files-check, zapier-test]
|
||||
steps:
|
||||
- name: Fail job if any needs failed
|
||||
if: contains(needs.*.result, 'failure')
|
||||
run: exit 1
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
preview-environment:
|
||||
timeout-minutes: 310
|
||||
runs-on: depot-ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -90,7 +90,7 @@ npx nx run twenty-front:graphql:generate --configuration=metadata
|
||||
## Architecture Overview
|
||||
|
||||
### Tech Stack
|
||||
- **Frontend**: React 18, TypeScript, Recoil (state management), Emotion (styling), Vite
|
||||
- **Frontend**: React 18, TypeScript, Jotai (state management), Emotion (styling), Vite
|
||||
- **Backend**: NestJS, TypeORM, PostgreSQL, Redis, GraphQL (with GraphQL Yoga)
|
||||
- **Monorepo**: Nx workspace managed with Yarn 4
|
||||
|
||||
@@ -138,7 +138,7 @@ packages/
|
||||
- Multi-line comments use multiple `//` lines, not `/** */`
|
||||
|
||||
### State Management
|
||||
- **Recoil** for global state: atoms for primitive state, selectors for derived state, atom families for dynamic collections
|
||||
- **Jotai** for global state: atoms for primitive state, selectors for derived state, atom families for dynamic collections
|
||||
- Component-specific state with React hooks (`useState`, `useReducer` for complex logic)
|
||||
- GraphQL cache managed by Apollo Client
|
||||
- Use functional state updates: `setState(prev => prev + 1)`
|
||||
|
||||
@@ -109,7 +109,7 @@ Below are a few features we have implemented to date:
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
- [Nx](https://nx.dev/)
|
||||
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
|
||||
- [React](https://reactjs.org/), with [Recoil](https://recoiljs.org/), [Emotion](https://emotion.sh/) and [Lingui](https://lingui.dev/)
|
||||
- [React](https://reactjs.org/), with [Jotai](https://jotai.org/), [Emotion](https://emotion.sh/) and [Lingui](https://lingui.dev/)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -83,6 +83,10 @@ export default [
|
||||
sourceTag: 'scope:frontend',
|
||||
onlyDependOnLibsWithTags: ['scope:shared', 'scope:frontend'],
|
||||
},
|
||||
{
|
||||
sourceTag: 'scope:zapier',
|
||||
onlyDependOnLibsWithTags: ['scope:shared', 'scope:zapier'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"ci": true,
|
||||
"maxWorkers": 3
|
||||
"maxWorkers": 1
|
||||
},
|
||||
"coverage": {
|
||||
"coverageReporters": ["lcov", "text"]
|
||||
|
||||
+8
-11
@@ -48,8 +48,7 @@
|
||||
"react-responsive": "^9.0.2",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"react-tooltip": "^5.13.1",
|
||||
"recoil": "^0.7.7",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"rxjs": "^7.2.0",
|
||||
"semver": "^7.5.4",
|
||||
"slash": "^5.1.0",
|
||||
@@ -84,11 +83,11 @@
|
||||
"@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/addon-docs": "^10.2.13",
|
||||
"@storybook/addon-links": "^10.2.13",
|
||||
"@storybook/addon-vitest": "^10.2.13",
|
||||
"@storybook/icons": "^2.0.1",
|
||||
"@storybook/react-vite": "^10.1.11",
|
||||
"@storybook/react-vite": "^10.2.13",
|
||||
"@storybook/test-runner": "^0.24.2",
|
||||
"@stylistic/eslint-plugin": "^1.5.0",
|
||||
"@swc-node/register": "1.11.1",
|
||||
@@ -160,7 +159,7 @@
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-storybook": "^10.1.11",
|
||||
"eslint-plugin-storybook": "^10.2.13",
|
||||
"eslint-plugin-unicorn": "^56.0.1",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"http-server": "^14.1.1",
|
||||
@@ -176,9 +175,9 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"source-map-support": "^0.5.20",
|
||||
"storybook": "^10.1.11",
|
||||
"storybook": "^10.2.13",
|
||||
"storybook-addon-mock-date": "2.0.0",
|
||||
"storybook-addon-pseudo-states": "^10.1.11",
|
||||
"storybook-addon-pseudo-states": "^10.2.13",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-loader": "^9.2.3",
|
||||
@@ -202,8 +201,6 @@
|
||||
"type-fest": "4.10.1",
|
||||
"typescript": "5.9.2",
|
||||
"graphql-redis-subscriptions/ioredis": "^5.6.0",
|
||||
"prosemirror-view": "1.40.0",
|
||||
"prosemirror-transform": "1.10.4",
|
||||
"@lingui/core": "5.1.2",
|
||||
"@types/qs": "6.9.16"
|
||||
},
|
||||
|
||||
@@ -31,10 +31,6 @@ See Twenty application documentation https://docs.twenty.com/developers/extend/c
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# If you don't use yarn@4
|
||||
corepack enable
|
||||
yarn install
|
||||
|
||||
# Get help and list all available commands
|
||||
yarn twenty help
|
||||
|
||||
@@ -45,7 +41,7 @@ yarn twenty auth:login
|
||||
yarn twenty entity:add
|
||||
|
||||
# Start dev mode: watches, builds, and syncs local changes to your workspace
|
||||
# (also auto-generates a typed API client in node_modules/twenty-sdk/generated)
|
||||
# (also auto-generates typed API clients — CoreApiClient and MetadataApiClient — in node_modules/twenty-sdk/generated)
|
||||
yarn twenty app:dev
|
||||
|
||||
# Watch your application's function logs
|
||||
@@ -54,6 +50,9 @@ yarn twenty function:logs
|
||||
# Execute a function with a JSON payload
|
||||
yarn twenty function:execute -n my-function -p '{"key": "value"}'
|
||||
|
||||
# Execute the pre-install function
|
||||
yarn twenty function:execute --preInstall
|
||||
|
||||
# Execute the post-install function
|
||||
yarn twenty function:execute --postInstall
|
||||
|
||||
@@ -96,6 +95,7 @@ In interactive mode, you can pick from:
|
||||
**Core files (always created):**
|
||||
- `application-config.ts` — Application metadata configuration
|
||||
- `roles/default-role.ts` — Default role for logic functions
|
||||
- `logic-functions/pre-install.ts` — Pre-install logic function (runs before app installation)
|
||||
- `logic-functions/post-install.ts` — Post-install logic function (runs after app installation)
|
||||
- TypeScript configuration, ESLint, package.json, .gitignore
|
||||
- A prewired `twenty` script that delegates to the `twenty` CLI from twenty-sdk
|
||||
@@ -114,7 +114,7 @@ In interactive mode, you can pick from:
|
||||
- Use `yarn twenty auth:login` to authenticate with your Twenty workspace.
|
||||
- Explore the generated project and add your first entity with `yarn twenty entity:add` (logic functions, front components, objects, roles, views, navigation menu items, skills).
|
||||
- Use `yarn twenty app:dev` while you iterate — it watches, builds, and syncs changes to your workspace in real time.
|
||||
- Types are auto‑generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated`.
|
||||
- Two typed API clients are auto‑generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
|
||||
|
||||
|
||||
## Publish your application
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const jestConfig = {
|
||||
displayName: 'twenty-cli',
|
||||
displayName: 'create-twenty-app',
|
||||
preset: '../../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transformIgnorePatterns: ['../../node_modules/'],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-twenty-app",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"description": "Command-line interface to create Twenty application",
|
||||
"main": "dist/cli.cjs",
|
||||
"bin": "dist/cli.cjs",
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
- Documentation: https://docs.twenty.com/developers/extend/capabilities/apps
|
||||
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-sdk/src/cli/__tests__/apps/rich-app
|
||||
|
||||
## UUID requirement
|
||||
- All generated UUIDs must be valid UUID v4.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Creating an object without an index view associated. Unless this is a technical object, user will need to visualize it.
|
||||
|
||||
@@ -233,9 +233,9 @@ export class CreateAppCommand {
|
||||
console.log('');
|
||||
console.log(chalk.blue('Next steps:'));
|
||||
console.log(chalk.gray(` cd ${dirName}`));
|
||||
console.log(chalk.gray(` corepack enable # if you don't use yarn@4`));
|
||||
console.log(chalk.gray(` yarn install # if you don't use yarn@4`));
|
||||
console.log(chalk.gray(' yarn auth:login # Authenticate with Twenty'));
|
||||
console.log(chalk.gray(' yarn app:dev # Start dev mode'));
|
||||
console.log(
|
||||
chalk.gray(' yarn twenty auth:login # Authenticate with Twenty'),
|
||||
);
|
||||
console.log(chalk.gray(' yarn twenty app:dev # Start dev mode'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +375,18 @@ describe('copyBaseApplicationProject', () => {
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Install functions should always exist
|
||||
expect(
|
||||
await fs.pathExists(
|
||||
join(srcPath, 'logic-functions', 'pre-install.ts'),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
await fs.pathExists(
|
||||
join(srcPath, 'logic-functions', 'post-install.ts'),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -398,6 +410,18 @@ describe('copyBaseApplicationProject', () => {
|
||||
await fs.pathExists(join(srcPath, 'roles', DEFAULT_ROLE_FILE_NAME)),
|
||||
).toBe(true);
|
||||
|
||||
// Install functions should always exist (not gated by exampleOptions)
|
||||
expect(
|
||||
await fs.pathExists(
|
||||
join(srcPath, 'logic-functions', 'pre-install.ts'),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
await fs.pathExists(
|
||||
join(srcPath, 'logic-functions', 'post-install.ts'),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Example files should not exist
|
||||
expect(
|
||||
await fs.pathExists(join(srcPath, 'objects', 'example-object.ts')),
|
||||
@@ -676,4 +700,124 @@ describe('copyBaseApplicationProject', () => {
|
||||
expect(content).toContain('position: 0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('pre-install logic function', () => {
|
||||
it('should create pre-install.ts with definePreInstallLogicFunction and typed payload', async () => {
|
||||
await copyBaseApplicationProject({
|
||||
appName: 'my-test-app',
|
||||
appDisplayName: 'My Test App',
|
||||
appDescription: 'A test application',
|
||||
appDirectory: testAppDirectory,
|
||||
exampleOptions: NO_EXAMPLES,
|
||||
});
|
||||
|
||||
const preInstallPath = join(
|
||||
testAppDirectory,
|
||||
'src',
|
||||
'logic-functions',
|
||||
'pre-install.ts',
|
||||
);
|
||||
|
||||
expect(await fs.pathExists(preInstallPath)).toBe(true);
|
||||
|
||||
const content = await fs.readFile(preInstallPath, 'utf8');
|
||||
|
||||
expect(content).toContain(
|
||||
"import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk'",
|
||||
);
|
||||
expect(content).toContain(
|
||||
'export default definePreInstallLogicFunction({',
|
||||
);
|
||||
expect(content).toContain("name: 'pre-install'");
|
||||
expect(content).toContain('timeoutSeconds: 300');
|
||||
expect(content).toContain(
|
||||
'const handler = async (payload: InstallLogicFunctionPayload): Promise<void>',
|
||||
);
|
||||
expect(content).toContain('payload.previousVersion');
|
||||
|
||||
// Verify it has a universalIdentifier (UUID format)
|
||||
expect(content).toMatch(
|
||||
/universalIdentifier: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should always create pre-install.ts regardless of example options', async () => {
|
||||
await copyBaseApplicationProject({
|
||||
appName: 'my-test-app',
|
||||
appDisplayName: 'My Test App',
|
||||
appDescription: 'A test application',
|
||||
appDirectory: testAppDirectory,
|
||||
exampleOptions: NO_EXAMPLES,
|
||||
});
|
||||
|
||||
const preInstallPath = join(
|
||||
testAppDirectory,
|
||||
'src',
|
||||
'logic-functions',
|
||||
'pre-install.ts',
|
||||
);
|
||||
|
||||
expect(await fs.pathExists(preInstallPath)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('post-install logic function', () => {
|
||||
it('should create post-install.ts with definePostInstallLogicFunction and typed payload', async () => {
|
||||
await copyBaseApplicationProject({
|
||||
appName: 'my-test-app',
|
||||
appDisplayName: 'My Test App',
|
||||
appDescription: 'A test application',
|
||||
appDirectory: testAppDirectory,
|
||||
exampleOptions: NO_EXAMPLES,
|
||||
});
|
||||
|
||||
const postInstallPath = join(
|
||||
testAppDirectory,
|
||||
'src',
|
||||
'logic-functions',
|
||||
'post-install.ts',
|
||||
);
|
||||
|
||||
expect(await fs.pathExists(postInstallPath)).toBe(true);
|
||||
|
||||
const content = await fs.readFile(postInstallPath, 'utf8');
|
||||
|
||||
expect(content).toContain(
|
||||
"import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk'",
|
||||
);
|
||||
expect(content).toContain(
|
||||
'export default definePostInstallLogicFunction({',
|
||||
);
|
||||
expect(content).toContain("name: 'post-install'");
|
||||
expect(content).toContain('timeoutSeconds: 300');
|
||||
expect(content).toContain(
|
||||
'const handler = async (payload: InstallLogicFunctionPayload): Promise<void>',
|
||||
);
|
||||
expect(content).toContain('payload.previousVersion');
|
||||
|
||||
// Verify it has a universalIdentifier (UUID format)
|
||||
expect(content).toMatch(
|
||||
/universalIdentifier: '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should always create post-install.ts regardless of example options', async () => {
|
||||
await copyBaseApplicationProject({
|
||||
appName: 'my-test-app',
|
||||
appDisplayName: 'My Test App',
|
||||
appDescription: 'A test application',
|
||||
appDirectory: testAppDirectory,
|
||||
exampleOptions: NO_EXAMPLES,
|
||||
});
|
||||
|
||||
const postInstallPath = join(
|
||||
testAppDirectory,
|
||||
'src',
|
||||
'logic-functions',
|
||||
'post-install.ts',
|
||||
);
|
||||
|
||||
expect(await fs.pathExists(postInstallPath)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,6 +97,12 @@ export const copyBaseApplicationProject = async ({
|
||||
});
|
||||
}
|
||||
|
||||
await createDefaultPreInstallFunction({
|
||||
appDirectory: sourceFolderPath,
|
||||
fileFolder: 'logic-functions',
|
||||
fileName: 'pre-install.ts',
|
||||
});
|
||||
|
||||
await createDefaultPostInstallFunction({
|
||||
appDirectory: sourceFolderPath,
|
||||
fileFolder: 'logic-functions',
|
||||
@@ -268,6 +274,36 @@ export default defineLogicFunction({
|
||||
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
|
||||
};
|
||||
|
||||
const createDefaultPreInstallFunction = async ({
|
||||
appDirectory,
|
||||
fileFolder,
|
||||
fileName,
|
||||
}: {
|
||||
appDirectory: string;
|
||||
fileFolder?: string;
|
||||
fileName: string;
|
||||
}) => {
|
||||
const universalIdentifier = v4();
|
||||
|
||||
const content = `import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: '${universalIdentifier}',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
handler,
|
||||
});
|
||||
`;
|
||||
|
||||
await fs.ensureDir(join(appDirectory, fileFolder ?? ''));
|
||||
await fs.writeFile(join(appDirectory, fileFolder ?? '', fileName), content);
|
||||
};
|
||||
|
||||
const createDefaultPostInstallFunction = async ({
|
||||
appDirectory,
|
||||
fileFolder,
|
||||
@@ -279,16 +315,14 @@ const createDefaultPostInstallFunction = async ({
|
||||
}) => {
|
||||
const universalIdentifier = v4();
|
||||
|
||||
const content = `import { defineLogicFunction } from 'twenty-sdk';
|
||||
const content = `import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
export const POST_INSTALL_UNIVERSAL_IDENTIFIER = '${universalIdentifier}';
|
||||
|
||||
const handler = async (): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!');
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: '${universalIdentifier}',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
@@ -477,14 +511,12 @@ const createApplicationConfig = async ({
|
||||
}) => {
|
||||
const content = `import { defineApplication } from 'twenty-sdk';
|
||||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
|
||||
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: '${v4()}',
|
||||
displayName: '${displayName}',
|
||||
description: '${description ?? ''}',
|
||||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
`;
|
||||
|
||||
|
||||
@@ -6,7 +6,13 @@ const execPromise = promisify(exec);
|
||||
|
||||
export const install = async (root: string) => {
|
||||
try {
|
||||
await execPromise('yarn', { cwd: root });
|
||||
await execPromise('corepack enable', { cwd: root });
|
||||
} catch (error: any) {
|
||||
console.warn(chalk.yellow('corepack enabled failed:'), error.stderr);
|
||||
}
|
||||
|
||||
try {
|
||||
await execPromise('yarn install', { cwd: root });
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('yarn install failed:'), error.stdout);
|
||||
}
|
||||
|
||||
+19
-18
@@ -7,9 +7,9 @@ This document outlines the best practices you should follow when working on the
|
||||
|
||||
## State management
|
||||
|
||||
React and Recoil handle state management in the codebase.
|
||||
React and Jotai handle state management in the codebase.
|
||||
|
||||
### Use `useRecoilState` to store state
|
||||
### Use Jotai atoms to store state
|
||||
|
||||
It's good practice to create as many atoms as you need to store your state.
|
||||
|
||||
@@ -20,13 +20,16 @@ It's better to use extra atoms than trying to be too concise with props drilling
|
||||
</Warning>
|
||||
|
||||
```tsx
|
||||
export const myAtomState = atom({
|
||||
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',
|
||||
default: 'default value',
|
||||
defaultValue: 'default value',
|
||||
});
|
||||
|
||||
export const MyComponent = () => {
|
||||
const [myAtom, setMyAtom] = useRecoilState(myAtomState);
|
||||
const [myAtom, setMyAtom] = useAtomState(myAtomState);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -43,7 +46,7 @@ export const MyComponent = () => {
|
||||
|
||||
Avoid using `useRef` to store state.
|
||||
|
||||
If you want to store state, you should use `useState` or `useRecoilState`.
|
||||
If you want to store state, you should use `useState` or Jotai atoms with `useAtomState`.
|
||||
|
||||
See [how to manage re-renders](#managing-re-renders) if you feel like you need `useRef` to prevent some re-renders from happening.
|
||||
|
||||
@@ -83,8 +86,8 @@ You can apply the same for data fetching logic, with Apollo hooks.
|
||||
// ❌ Bad, will cause re-renders even if data is not changing,
|
||||
// because useEffect needs to be re-evaluated
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
const [someDependency] = useAtomState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
@@ -96,9 +99,7 @@ export const PageComponent = () => {
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
<PageComponent />
|
||||
);
|
||||
```
|
||||
|
||||
@@ -106,14 +107,14 @@ export const App = () => (
|
||||
// ✅ Good, will not cause re-renders if data is not changing,
|
||||
// because useEffect is re-evaluated in another sibling component
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const PageData = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
const [someDependency] = useAtomState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
@@ -125,16 +126,16 @@ export const PageData = () => {
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<>
|
||||
<PageData />
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### Use recoil family states and recoil family selectors
|
||||
### Use atom family states and selectors
|
||||
|
||||
Recoil family states and selectors are a great way to avoid re-renders.
|
||||
Atom family states and selectors are a great way to avoid re-renders.
|
||||
|
||||
They are useful when you need to store a list of items.
|
||||
|
||||
|
||||
+2
-2
@@ -83,9 +83,9 @@ See [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) for more de
|
||||
|
||||
### States
|
||||
|
||||
Contains the state management logic. [RecoilJS](https://recoiljs.org) handles this.
|
||||
Contains the state management logic. [Jotai](https://jotai.org) handles this.
|
||||
|
||||
- Selectors: See [RecoilJS Selectors](https://recoiljs.org/docs/basic-tutorial/selectors) for more details.
|
||||
- Selectors: Derived atoms (using `createAtomSelector`) compute values from other atoms and are automatically memoized.
|
||||
|
||||
React's built-in state management still handles state within a component.
|
||||
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@ The project has a clean and simple stack, with minimal boilerplate code.
|
||||
- [React](https://react.dev/)
|
||||
- [Apollo](https://www.apollographql.com/docs/)
|
||||
- [GraphQL Codegen](https://the-guild.dev/graphql/codegen)
|
||||
- [Recoil](https://recoiljs.org/docs/introduction/core-concepts)
|
||||
- [Jotai](https://jotai.org/)
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
|
||||
**Testing**
|
||||
@@ -76,7 +76,7 @@ To avoid unnecessary [re-renders](/developers/contribute/capabilities/frontend-d
|
||||
|
||||
### State Management
|
||||
|
||||
[Recoil](https://recoiljs.org/docs/introduction/core-concepts) handles state management.
|
||||
[Jotai](https://jotai.org/) handles state management.
|
||||
|
||||
See [best practices](/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) for more information on state management.
|
||||
|
||||
|
||||
+3
-3
@@ -159,7 +159,7 @@ export enum PageHotkeyScope {
|
||||
}
|
||||
```
|
||||
|
||||
Internally, the currently selected scope is stored in a Recoil state that is shared across the application :
|
||||
Internally, the currently selected scope is stored in a Jotai atom that is shared across the application :
|
||||
|
||||
```tsx
|
||||
export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
@@ -168,10 +168,10 @@ export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
});
|
||||
```
|
||||
|
||||
But this Recoil state should never be handled manually ! We'll see how to use it in the next section.
|
||||
But this atom should never be handled manually ! We'll see how to use it in the next section.
|
||||
|
||||
## How is it working internally?
|
||||
|
||||
We made a thin wrapper on top of [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) that makes it more performant and avoids unnecessary re-renders.
|
||||
|
||||
We also create a Recoil state to handle the hotkey scope state and make it available everywhere in the application.
|
||||
We also create a Jotai atom to handle the hotkey scope state and make it available everywhere in the application.
|
||||
|
||||
@@ -31,13 +31,6 @@ Create a new app using the official scaffolder, then authenticate and start deve
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# If you don't use yarn@4
|
||||
corepack enable
|
||||
yarn install
|
||||
|
||||
# Authenticate using your API key (you'll be prompted)
|
||||
yarn twenty auth:login
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn twenty app:dev
|
||||
```
|
||||
@@ -67,6 +60,9 @@ yarn twenty function:logs
|
||||
# Execute a function by name
|
||||
yarn twenty function:execute -n my-function -p '{"name": "test"}'
|
||||
|
||||
# Execute the pre-install function
|
||||
yarn twenty function:execute --preInstall
|
||||
|
||||
# Execute the post-install function
|
||||
yarn twenty function:execute --postInstall
|
||||
|
||||
@@ -86,7 +82,7 @@ When you run `npx create-twenty-app@latest my-twenty-app`, the scaffolder:
|
||||
- Copies a minimal base application into `my-twenty-app/`
|
||||
- Adds a local `twenty-sdk` dependency and Yarn 4 configuration
|
||||
- Creates config files and scripts wired to the `twenty` CLI
|
||||
- Generates core files (application config, default function role, post-install function) plus example files based on the scaffolding mode
|
||||
- Generates core files (application config, default function role, pre-install and post-install functions) plus example files based on the scaffolding mode
|
||||
|
||||
A freshly scaffolded app with the default `--exhaustive` mode looks like this:
|
||||
|
||||
@@ -113,6 +109,7 @@ my-twenty-app/
|
||||
│ └── example-field.ts # Example standalone field definition
|
||||
├── logic-functions/
|
||||
│ ├── hello-world.ts # Example logic function
|
||||
│ ├── pre-install.ts # Pre-install logic function
|
||||
│ └── post-install.ts # Post-install logic function
|
||||
├── front-components/
|
||||
│ └── hello-world.tsx # Example front component
|
||||
@@ -124,7 +121,7 @@ my-twenty-app/
|
||||
└── example-skill.ts # Example AI agent skill definition
|
||||
```
|
||||
|
||||
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, and `logic-functions/post-install.ts`). With `--interactive`, you choose which example files to include.
|
||||
With `--minimal`, only the core files are created (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts`, and `logic-functions/post-install.ts`). With `--interactive`, you choose which example files to include.
|
||||
|
||||
At a high level:
|
||||
|
||||
@@ -145,6 +142,8 @@ The SDK detects entities by parsing your TypeScript files for **`export default
|
||||
|-----------------|-------------|
|
||||
| `defineObject()` | Custom object definitions |
|
||||
| `defineLogicFunction()` | Logic function definitions |
|
||||
| `definePreInstallLogicFunction()` | Pre-install logic function (runs before installation) |
|
||||
| `definePostInstallLogicFunction()` | Post-install logic function (runs after installation) |
|
||||
| `defineFrontComponent()` | Front component definitions |
|
||||
| `defineRole()` | Role definitions |
|
||||
| `defineField()` | Field extensions for existing objects |
|
||||
@@ -170,7 +169,7 @@ export default defineObject({
|
||||
|
||||
Later commands will add more files and folders:
|
||||
|
||||
- `yarn twenty app:dev` will auto-generate a typed API client in `node_modules/twenty-sdk/generated` (typed Twenty client + workspace types).
|
||||
- `yarn twenty app:dev` will auto-generate two typed API clients in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
|
||||
- `yarn twenty entity:add` will add entity definition files under `src/` for your custom objects, functions, front components, roles, skills, and more.
|
||||
|
||||
## Authentication
|
||||
@@ -219,6 +218,8 @@ The SDK provides helper functions for defining your app entities. As described i
|
||||
| `defineApplication()` | Configure application metadata (required, one per app) |
|
||||
| `defineObject()` | Define custom objects with fields |
|
||||
| `defineLogicFunction()` | Define logic functions with handlers |
|
||||
| `definePreInstallLogicFunction()` | Define a pre-install logic function (one per app) |
|
||||
| `definePostInstallLogicFunction()` | Define a post-install logic function (one per app) |
|
||||
| `defineFrontComponent()` | Define front components for custom UI |
|
||||
| `defineRole()` | Configure role permissions and object access |
|
||||
| `defineField()` | Extend existing objects with additional fields |
|
||||
@@ -325,6 +326,7 @@ Every app has a single `application-config.ts` file that describes:
|
||||
- **Who the app is**: identifiers, display name, and description.
|
||||
- **How its functions run**: which role they use for permissions.
|
||||
- **(Optional) variables**: key–value pairs exposed to your functions as environment variables.
|
||||
- **(Optional) pre-install function**: a logic function that runs before the app is installed.
|
||||
- **(Optional) post-install function**: a logic function that runs after the app is installed.
|
||||
|
||||
Use `defineApplication()` to define your application configuration:
|
||||
@@ -333,7 +335,6 @@ Use `defineApplication()` to define your application configuration:
|
||||
// src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
|
||||
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
||||
@@ -349,7 +350,6 @@ export default defineApplication({
|
||||
},
|
||||
},
|
||||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -357,7 +357,7 @@ Notes:
|
||||
- `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
|
||||
- `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
- `defaultRoleUniversalIdentifier` must match the role file (see below).
|
||||
- `postInstallLogicFunctionUniversalIdentifier` (optional) points to a logic function that runs automatically after the app is installed. See [Post-install functions](#post-install-functions).
|
||||
- Pre-install and post-install functions are automatically detected during the manifest build. See [Pre-install functions](#pre-install-functions) and [Post-install functions](#post-install-functions).
|
||||
|
||||
#### Roles and permissions
|
||||
|
||||
@@ -430,10 +430,10 @@ Each function file uses `defineLogicFunction()` to export a configuration with a
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/generated';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient();
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -490,6 +490,43 @@ 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.
|
||||
|
||||
### Pre-install functions
|
||||
|
||||
A pre-install function is a logic function that runs automatically before your app is installed on a workspace. This is useful for validation tasks, prerequisite checks, or preparing workspace state before the main installation proceeds.
|
||||
|
||||
When you scaffold a new app with `create-twenty-app`, a pre-install function is generated for you at `src/logic-functions/pre-install.ts`:
|
||||
|
||||
```typescript
|
||||
// src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: '<generated-uuid>',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the pre-install function at any time using the CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty function:execute --preInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
- Pre-install functions use `definePreInstallLogicFunction()` — a specialized variant that omits trigger settings (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`).
|
||||
- The handler receives an `InstallLogicFunctionPayload` with `{ previousVersion: string }` — the version of the app that was previously installed (or an empty string for fresh installs).
|
||||
- Only one pre-install function is allowed per application. The manifest build will error if more than one is detected.
|
||||
- The function's `universalIdentifier` is automatically set as `preInstallLogicFunctionUniversalIdentifier` on the application manifest during the build — you do not need to reference it in `defineApplication()`.
|
||||
- The default timeout is set to 300 seconds (5 minutes) to allow for longer preparation tasks.
|
||||
- Pre-install functions do not need triggers — they are invoked by the platform before installation or manually via `function:execute --preInstall`.
|
||||
|
||||
### Post-install functions
|
||||
|
||||
A post-install function is a logic function that runs automatically after your app is installed on a workspace. This is useful for one-time setup tasks such as seeding default data, creating initial records, or configuring workspace settings.
|
||||
@@ -498,16 +535,14 @@ When you scaffold a new app with `create-twenty-app`, a post-install function is
|
||||
|
||||
```typescript
|
||||
// src/logic-functions/post-install.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
export const POST_INSTALL_UNIVERSAL_IDENTIFIER = '<generated-uuid>';
|
||||
|
||||
const handler = async (): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!');
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: '<generated-uuid>',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
@@ -515,17 +550,6 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
The function is wired into your app by referencing its universal identifier in `application-config.ts`:
|
||||
|
||||
```typescript
|
||||
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';
|
||||
|
||||
export default defineApplication({
|
||||
// ...
|
||||
postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually execute the post-install function at any time using the CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
@@ -533,8 +557,10 @@ yarn twenty function:execute --postInstall
|
||||
```
|
||||
|
||||
Key points:
|
||||
- Post-install functions are standard logic functions — they use `defineLogicFunction()` like any other function.
|
||||
- The `postInstallLogicFunctionUniversalIdentifier` field in `defineApplication()` is optional. If omitted, no function runs after installation.
|
||||
- Post-install functions use `definePostInstallLogicFunction()` — a specialized variant that omits trigger settings (`cronTriggerSettings`, `databaseEventTriggerSettings`, `httpRouteTriggerSettings`, `isTool`).
|
||||
- The handler receives an `InstallLogicFunctionPayload` with `{ previousVersion: string }` — the version of the app that was previously installed (or an empty string for fresh installs).
|
||||
- Only one post-install function is allowed per application. The manifest build will error if more than one is detected.
|
||||
- The function's `universalIdentifier` is automatically set as `postInstallLogicFunctionUniversalIdentifier` on the application manifest during the build — you do not need to reference it in `defineApplication()`.
|
||||
- The default timeout is set to 300 seconds (5 minutes) to allow for longer setup tasks like data seeding.
|
||||
- Post-install functions do not need triggers — they are invoked by the platform during installation or manually via `function:execute --postInstall`.
|
||||
|
||||
@@ -642,10 +668,10 @@ To mark a logic function as a tool, set `isTool: true` and provide a `toolInputS
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/generated';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -703,7 +729,7 @@ Key points:
|
||||
Front components let you build custom React components that render within Twenty's UI. Use `defineFrontComponent()` to define components with built-in validation:
|
||||
|
||||
```typescript
|
||||
// src/my-widget.front-component.tsx
|
||||
// src/front-components/my-widget.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
const MyWidget = () => {
|
||||
@@ -725,14 +751,13 @@ export default defineFrontComponent({
|
||||
|
||||
Key points:
|
||||
- Front components are React components that render in isolated contexts within Twenty.
|
||||
- Use the `*.front-component.tsx` file suffix for automatic detection.
|
||||
- The `component` field references your React component.
|
||||
- Components are built and synced automatically during `yarn twenty app:dev`.
|
||||
|
||||
You can create new front components in two ways:
|
||||
|
||||
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new front component.
|
||||
- **Manual**: Create a new `*.front-component.tsx` file and use `defineFrontComponent()`.
|
||||
- **Manual**: Create a new `.tsx` file and use `defineFrontComponent()`, following the same pattern.
|
||||
|
||||
### Skills
|
||||
|
||||
@@ -768,18 +793,24 @@ You can create new skills in two ways:
|
||||
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
|
||||
- **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
|
||||
|
||||
### Generated typed client
|
||||
### Generated typed clients
|
||||
|
||||
The typed client is auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema. Use it in your functions:
|
||||
Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema:
|
||||
|
||||
- **`CoreApiClient`** — queries the `/graphql` endpoint for workspace data
|
||||
- **`MetadataApiClient`** — queries the `/metadata` endpoint for workspace configuration and file uploads
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
|
||||
const metadataClient = new MetadataApiClient();
|
||||
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
|
||||
```
|
||||
|
||||
The client is re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change.
|
||||
Both clients are re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change.
|
||||
|
||||
#### Runtime credentials in logic functions
|
||||
|
||||
@@ -795,17 +826,17 @@ Notes:
|
||||
|
||||
#### Uploading files
|
||||
|
||||
The generated `Twenty` client includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
|
||||
The generated `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/generated';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const metadataClient = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
const uploadedFile = await client.uploadFile(
|
||||
const uploadedFile = await metadataClient.uploadFile(
|
||||
fileBuffer, // file contents as a Buffer
|
||||
'invoice.pdf', // filename
|
||||
'application/pdf', // MIME type (defaults to 'application/octet-stream')
|
||||
@@ -835,7 +866,7 @@ uploadFile(
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | The `universalIdentifier` of the file-type field on your object |
|
||||
|
||||
Key points:
|
||||
- The method sends the file to the **metadata endpoint** (not the main GraphQL endpoint), where the upload mutation is resolved.
|
||||
- The `uploadFile` method is available on `MetadataApiClient` because the upload mutation is resolved by the `/metadata` endpoint.
|
||||
- It uses the field's `universalIdentifier` (not its workspace-specific ID), so your upload code works across any workspace where your app is installed — consistent with how apps reference fields everywhere else.
|
||||
- The returned `url` is a signed URL you can use to access the uploaded file.
|
||||
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ title: كائنات مخصصة
|
||||
## مخطط على مستوى عالي
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="مخطط على مستوى عالي" />
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="مخطط على مستوى عالي" />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
@@ -27,7 +27,7 @@ title: كائنات مخصصة
|
||||
لإضافة كائن مخصص، سيقوم عضو مساحة العمل بالاستعلام عن واجهة برمجة التطبيقات /metadata. يقوم هذا بتحديث البيانات الوصفية وفقًا لذلك ويحسب مخطط GraphQL استنادًا إلى البيانات الوصفية، ويخزنها في ذاكرة GQL للاستخدام لاحقًا.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/server/add-custom-objects.jpeg" alt="استعلام واجهة برمجة التطبيقات /metadata لإضافة الكائنات المخصصة" />
|
||||
<img src="/images/docs/server/add-custom-objects.jpeg" alt="استعلام واجهة برمجة التطبيقات /metadata لإضافة الكائنات المخصصة" />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
@@ -35,5 +35,5 @@ title: كائنات مخصصة
|
||||
لجلب البيانات، تتضمن العملية إجراء استعلامات من خلال نقطة النهاية /graphql وتمريرها من خلال محلل الاستعلام.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="استعلام نقطة النهاية /graphql لجلب البيانات" />
|
||||
<img src="/images/docs/server/custom-object-schema.png" alt="استعلام نقطة النهاية /graphql لجلب البيانات" />
|
||||
</div>
|
||||
|
||||
+4
-2
@@ -60,9 +60,11 @@ npx nx run twenty-server:command workspace:sync-metadata -f
|
||||
```
|
||||
|
||||
<Warning>
|
||||
سيؤدي هذا إلى إسقاط قاعدة البيانات وإعادة تشغيل الهجرات والبذور.
|
||||
|
||||
تأكد من عمل نسخة احتياطية لأي بيانات تريد الاحتفاظ بها قبل تشغيل هذا الأمر.
|
||||
سيؤدي هذا إلى إسقاط قاعدة البيانات وإعادة تشغيل الهجرات والبذور.
|
||||
|
||||
تأكد من عمل نسخة احتياطية لأي بيانات تريد الاحتفاظ بها قبل تشغيل هذا الأمر.
|
||||
|
||||
</Warning>
|
||||
|
||||
## "التقنية المستخدمة"
|
||||
|
||||
+3
-1
@@ -43,7 +43,9 @@ cp .env.example .env
|
||||
## التطوير
|
||||
|
||||
<Warning>
|
||||
تأكد من تشغيل `yarn build` قبل أي أمر `zapier`.
|
||||
|
||||
تأكد من تشغيل `yarn build` قبل أي أمر `zapier`.
|
||||
|
||||
</Warning>
|
||||
|
||||
### تجربة
|
||||
|
||||
+23
-20
@@ -6,24 +6,29 @@ title: أفضل الممارسات
|
||||
|
||||
## إدارة الحالة
|
||||
|
||||
تقوم React و Recoil بإدارة الحالة في قاعدة الشيفرة.
|
||||
تقوم React و Jotai بإدارة الحالة في قاعدة الشيفرة.
|
||||
|
||||
### استخدم `useRecoilState` لتخزين الحالة
|
||||
### استخدم ذرات Jotai لتخزين الحالة
|
||||
|
||||
من الجيد إنشاء أكبر عدد ممكن من الذرات لتخزين الحالة الخاصة بك.
|
||||
|
||||
<Warning>
|
||||
من الأفضل استخدام ذرات إضافية بدلاً من محاولة أن تكون مقتضبًا باستخدام تمرير الخصائص.
|
||||
|
||||
من الأفضل استخدام ذرات إضافية بدلاً من محاولة أن تكون مقتضبًا باستخدام تمرير الخصائص.
|
||||
|
||||
</Warning>
|
||||
|
||||
```tsx
|
||||
export const myAtomState = atom({
|
||||
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',
|
||||
default: 'default value',
|
||||
defaultValue: 'default value',
|
||||
});
|
||||
|
||||
export const MyComponent = () => {
|
||||
const [myAtom, setMyAtom] = useRecoilState(myAtomState);
|
||||
const [myAtom, setMyAtom] = useAtomState(myAtomState);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -40,7 +45,7 @@ export const MyComponent = () => {
|
||||
|
||||
تجنب استخدام `useRef` لتخزين الحالة.
|
||||
|
||||
If you want to store state, you should use `useState` or `useRecoilState`.
|
||||
إذا كنت ترغب في تخزين الحالة، يجب أن تستخدم `useState` أو ذرات Jotai مع `useAtomState`.
|
||||
|
||||
انظر [كيفية إدارة إعادة العرض](#managing-re-renders) إذا شعرت أنك بحاجة إلى `useRef` لمنع بعض إعادة العرض من الحدوث.
|
||||
|
||||
@@ -77,11 +82,11 @@ If you feel like you need to add a `useEffect` in your root component, you shoul
|
||||
يمكنك تطبيق نفس الشيء على منطق جلب البيانات، مع الخُطافات Apollo.
|
||||
|
||||
```tsx
|
||||
// ❌ سيّئ، سيتسبب في إعادة التصيير حتى إذا لم تتغير البيانات،
|
||||
// ❌ سيئ، سيتسبب في إعادة التصيير حتى إذا لم تتغير البيانات،
|
||||
// لأن useEffect يحتاج إلى إعادة التقييم
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
const [someDependency] = useAtomState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
@@ -93,9 +98,7 @@ export const PageComponent = () => {
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
<PageComponent />
|
||||
);
|
||||
```
|
||||
|
||||
@@ -103,14 +106,14 @@ export const App = () => (
|
||||
// ✅ جيّد، لن يتسبب في إعادة التصيير إذا لم تتغير البيانات،
|
||||
// لأن useEffect يُعاد تقييمه في مكوّن شقيق آخر
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const PageData = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
const [data, setData] = useAtomState(dataState);
|
||||
const [someDependency] = useAtomState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
@@ -122,16 +125,16 @@ export const PageData = () => {
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<>
|
||||
<PageData />
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
### استخدم حالات عائلة Recoil ومحددات عائلة Recoil
|
||||
### استخدم حالات عائلة الذرات والمحددات
|
||||
|
||||
حالات عائلة Recoil والمحددات تعتبر طريقة رائعة لتجنب إعادة العرض.
|
||||
تُعد حالات عائلة الذرات والمحددات طريقة رائعة لتجنّب عمليات إعادة التصيير.
|
||||
|
||||
إنها مفيدة عندما تحتاج إلى تخزين قائمة من العناصر.
|
||||
|
||||
|
||||
+2
-2
@@ -82,9 +82,9 @@ module1
|
||||
|
||||
### الحالات
|
||||
|
||||
تشمل منطق إدارة الحالة. [RecoilJS](https://recoiljs.org) يتولّى ذلك.
|
||||
تشمل منطق إدارة الحالة. [Jotai](https://jotai.org) يتولّى ذلك.
|
||||
|
||||
* المحددات: انظر [RecoilJS Selectors](https://recoiljs.org/docs/basic-tutorial/selectors) لمزيد من التفاصيل.
|
||||
* المحددات: الذرات المشتقة (باستخدام `createAtomSelector`) تحسب قيماً من ذرات أخرى وتخزن نتائجها في الذاكرة مؤقتاً تلقائياً.
|
||||
|
||||
لا تزال إدارة الحالة المدمجة في React تتولّى الحالة داخل المكوّن.
|
||||
|
||||
|
||||
+2
-2
@@ -49,7 +49,7 @@ title: أوامر الواجهة الأمامية
|
||||
* "[React](https://react.dev/)"
|
||||
* "[Apollo](https://www.apollographql.com/docs/)"
|
||||
* "[GraphQL Codegen](https://the-guild.dev/graphql/codegen)"
|
||||
* "[Recoil](https://recoiljs.org/docs/introduction/core-concepts)"
|
||||
* [Jotai](https://jotai.org/)
|
||||
* "[TypeScript](https://www.typescriptlang.org/)"
|
||||
|
||||
**الاختبار**
|
||||
@@ -73,7 +73,7 @@ To avoid unnecessary [re-renders](/l/ar/developers/contribute/capabilities/front
|
||||
|
||||
### "إدارة الحالة"
|
||||
|
||||
"[Recoil](https://recoiljs.org/docs/introduction/core-concepts) يتعامل مع إدارة الحالة."
|
||||
[Jotai](https://jotai.org/) يتعامل مع إدارة الحالة.
|
||||
|
||||
"راجع [أفضل الممارسات](/l/ar/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) لمزيد من المعلومات حول إدارة الحالة."
|
||||
|
||||
|
||||
+3
-3
@@ -160,7 +160,7 @@ export enum PageHotkeyScope {
|
||||
}
|
||||
```
|
||||
|
||||
داخليًا، يتم تخزين النطاق المحدد حاليًا في حالة Recoil مشتركة عبر التطبيق:
|
||||
داخليًا، يتم تخزين النطاق المحدد حاليًا في ذرة Jotai مشتركة عبر التطبيق:
|
||||
|
||||
```tsx
|
||||
export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
@@ -169,10 +169,10 @@ export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
});
|
||||
```
|
||||
|
||||
لكن لا يجب التعامل مع هذه الحالة Recoil يدويًا! سنرى كيف يمكن استخدامها في القسم التالي.
|
||||
لكن لا يجب التعامل مع هذه الذرة يدويًا! سنرى كيف يمكن استخدامها في القسم التالي.
|
||||
|
||||
## كيف يعمل داخليًا؟
|
||||
|
||||
قمنا بإنشاء غلاف رقيق فوق [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) والذي يجعله أكثر كفاءة ويتجنب عمليات إعادة التقديم غير الضرورية.
|
||||
|
||||
ونقوم أيضًا بإنشاء حالة Recoil للتعامل مع حالة نطاق المفتاح وجعلها متاحة في جميع أنحاء التطبيق.
|
||||
ونقوم أيضًا بإنشاء ذرة Jotai للتعامل مع حالة نطاق مفاتيح الاختصار وجعلها متاحة في جميع أنحاء التطبيق.
|
||||
|
||||
+3
-1
@@ -13,7 +13,9 @@ info: تعرّف على كيفية التعاون باستخدام Figma الخ
|
||||
تتوفر المميزات الرئيسية فقط للمستخدمين الذين قاموا بتسجيل الدخول، مثل وضع المطور والقدرة على اختيار إطار مخصص.
|
||||
|
||||
<Warning>
|
||||
لن تتمكن من التعاون بفعالية بدون حساب.
|
||||
|
||||
لن تتمكن من التعاون بفعالية بدون حساب.
|
||||
|
||||
</Warning>
|
||||
|
||||
## هيكل فيجما
|
||||
|
||||
@@ -6,66 +6,66 @@ description: الدليل للمساهمين (أو المطورين الفضول
|
||||
## المتطلبات الأساسية
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Linux و MacOS">
|
||||
قبل أن تتمكن من تثبيت واستخدام Twenty، تأكد من تثبيت الأمور التالية على جهاز الكمبيوتر الخاص بك:
|
||||
<Tab title="Linux و MacOS">
|
||||
|
||||
* [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)
|
||||
قبل أن تتمكن من تثبيت واستخدام 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>
|
||||
<Warning>
|
||||
لن يعمل `npm` ، يجب عليك استخدام `yarn` بدلًا من ذلك. يأتي Yarn الآن مع Node.js، لذا لست بحاجة إلى تثبيته بشكل منفصل.
|
||||
عليك فقط تشغيل `corepack enable` لتفعيل Yarn إذا لم تقم بذلك بعد.
|
||||
</Warning>
|
||||
|
||||
<Tab title="ويندوز (WSL)">
|
||||
1. ثبّت WSL
|
||||
افتح PowerShell كمسؤول ثم نفّذ:
|
||||
</Tab>
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
<Tab title="ويندوز (WSL)">
|
||||
|
||||
يجب أن ترى الآن مطالبة لإعادة تشغيل جهاز الكمبيوتر الخاص بك. إذا لم يكن كذلك، فأعد تشغيله يدويًا.
|
||||
1. ثبّت WSL
|
||||
افتح PowerShell كمسؤول ثم نفّذ:
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
يجب أن ترى الآن مطالبة لإعادة تشغيل جهاز الكمبيوتر الخاص بك. إذا لم يكن كذلك، فأعد تشغيله يدويًا.
|
||||
|
||||
عند إعادة التشغيل، ستُفتح نافذة PowerShell وسيتم تثبيت Ubuntu. قد يستغرق هذا وقتًا طويلاً.
|
||||
سترى مطالبة لإنشاء اسم المستخدم وكلمة المرور لتثبيت Ubuntu الخاص بك.
|
||||
عند إعادة التشغيل، ستُفتح نافذة PowerShell وسيتم تثبيت Ubuntu. قد يستغرق هذا وقتًا طويلاً.
|
||||
سترى مطالبة لإنشاء اسم المستخدم وكلمة المرور لتثبيت Ubuntu الخاص بك.
|
||||
|
||||
2. تثبيت وإعداد git
|
||||
2. تثبيت وإعداد git
|
||||
|
||||
```bash
|
||||
sudo apt-get install git
|
||||
```bash
|
||||
sudo apt-get install git
|
||||
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.name "Your Name"
|
||||
|
||||
git config --global user.email "youremail@domain.com"
|
||||
```
|
||||
git config --global user.email "youremail@domain.com"
|
||||
```
|
||||
|
||||
3. تثبيت nvm و node.js و yarn
|
||||
3. تثبيت nvm و node.js و yarn
|
||||
|
||||
<Warning>
|
||||
استخدم `nvm` لتثبيت نسخة `node` الصحيحة. الملف `.nvmrc` يضمن استخدام جميع المشاركين لنفس النسخة.
|
||||
</Warning>
|
||||
<Warning>
|
||||
استخدم `nvm` لتثبيت نسخة `node` الصحيحة. الملف `.nvmrc` يضمن استخدام جميع المشاركين لنفس النسخة.
|
||||
</Warning>
|
||||
|
||||
```bash
|
||||
sudo apt-get install curl
|
||||
```bash
|
||||
sudo apt-get install curl
|
||||
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
|
||||
```
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
|
||||
```
|
||||
أغلق وأعد فتح برنامجك الطرفي لاستخدام nvm. ثم قم بتشغيل الأوامر التالية.
|
||||
|
||||
أغلق وأعد فتح برنامجك الطرفي لاستخدام nvm. ثم قم بتشغيل الأوامر التالية.
|
||||
```bash
|
||||
|
||||
```bash
|
||||
nvm install # يثبت إصدار node الموصى به
|
||||
|
||||
nvm install # يثبت إصدار node الموصى به
|
||||
nvm use # استخدم إصدار node الموصى به
|
||||
|
||||
nvm use # استخدم إصدار node الموصى به
|
||||
corepack enable
|
||||
```
|
||||
|
||||
corepack enable
|
||||
```
|
||||
</Tab>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
@@ -75,19 +75,19 @@ description: الدليل للمساهمين (أو المطورين الفضول
|
||||
في الطرفية الخاصة بك، قم بتشغيل الأمر التالي.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="SSH (موصى به)">
|
||||
إذا لم تكن قد أعددت مفاتيح SSH بالفعل، يمكنك معرفة كيفية القيام بذلك [هنا](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh).
|
||||
<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 git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
```bash
|
||||
git clone https://github.com/twentyhq/twenty.git
|
||||
```
|
||||
|
||||
<Tab title="HTTPS">
|
||||
```bash
|
||||
git clone https://github.com/twentyhq/twenty.git
|
||||
```
|
||||
</Tab>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 2: انتقل إلى جذر المشروع
|
||||
@@ -104,20 +104,16 @@ cd twenty
|
||||
<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`:
|
||||
|
||||
@@ -129,7 +125,6 @@ cd twenty
|
||||
```
|
||||
|
||||
يمكنك التحقق مما إذا كان خادم PostgreSQL يعمل بتنفيذ:
|
||||
|
||||
```bash
|
||||
brew services list
|
||||
```
|
||||
@@ -138,7 +133,6 @@ cd twenty
|
||||
عبر Homebrew على MacOS. بدلاً من ذلك، فإنه ينشئ دور PostgreSQL يطابق
|
||||
اسم المستخدم الخاص بك في MacOS (مثل "john").
|
||||
للتحقق وإنشاء المستخدم `postgres` إذا لزم الأمر، اتبع هذه الخطوات:
|
||||
|
||||
```bash
|
||||
# قم بالاتصال بPostgreSQL
|
||||
psql postgres
|
||||
@@ -147,59 +141,48 @@ cd twenty
|
||||
```
|
||||
|
||||
بمجرد أن تكون عند مطالبة psql (postgres=#)، قم بتشغيل:
|
||||
|
||||
```bash
|
||||
# قائمة الأدوار الموجودة في PostgreSQL
|
||||
\du
|
||||
```
|
||||
|
||||
```bash
|
||||
# قائمة الأدوار الموجودة في PostgreSQL
|
||||
\du
|
||||
```
|
||||
سترى مخرجات مشابهة ل:
|
||||
|
||||
```bash
|
||||
اسم الأدوار | الخصائص | عضو في
|
||||
-----------+-------------+-----------
|
||||
john | مشرف نظام | {}
|
||||
```
|
||||
```bash
|
||||
اسم الأدوار | الخصائص | عضو في
|
||||
-----------+-------------+-----------
|
||||
john | مشرف نظام | {}
|
||||
```
|
||||
|
||||
إذا لم ترَ دور `postgres` مدرجًا، انتقل إلى الخطوة التالية.
|
||||
قم بإنشاء دور `postgres` يدويًا:
|
||||
|
||||
```bash
|
||||
CREATE ROLE postgres WITH SUPERUSER LOGIN;
|
||||
```
|
||||
|
||||
```bash
|
||||
CREATE ROLE postgres WITH SUPERUSER LOGIN;
|
||||
```
|
||||
يقوم هذا بإنشاء دور مشرف نظام باسم `postgres` مع إمكانية تسجيل الدخول.
|
||||
|
||||
```bash
|
||||
اسم الدور | الخصائص | عضو في
|
||||
-----------+-------------+-----------
|
||||
postgres | مشرف نظام | {}
|
||||
john | مشرف نظام | {}
|
||||
```
|
||||
```bash
|
||||
اسم الدور | الخصائص | عضو في
|
||||
-----------+-------------+-----------
|
||||
postgres | مشرف نظام | {}
|
||||
john | مشرف نظام | {}
|
||||
```
|
||||
|
||||
**الخيار 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
|
||||
```
|
||||
@@ -218,35 +201,28 @@ cd twenty
|
||||
استخدم الرابط التالي لتثبيت 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 الخاص بك:
|
||||
`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
|
||||
```
|
||||
@@ -267,7 +243,7 @@ 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) للحصول على التفاصيل.
|
||||
**وضع تعدد مساحات العمل:** بشكل افتراضي، يعمل Twenty في وضع مساحة عمل واحدة حيث يمكن إنشاء مساحة عمل واحدة فقط. لتمكين دعم تعدد مساحات العمل (مفيد لاختبار الميزات المعتمدة على النطاقات الفرعية)، عيّن `IS_MULTIWORKSPACE_ENABLED=true` في ملف الخادم `.env`. راجع [وضع تعدد مساحات العمل](/l/ar/developers/self-host/capabilities/setup#multi-workspace-mode) للحصول على التفاصيل.
|
||||
</Info>
|
||||
|
||||
## الخطوة 6: تثبيت التبعيات
|
||||
@@ -287,15 +263,12 @@ yarn
|
||||
اعتمادًا على توزيعة Linux الخاصة بك، قد يتم بدء خادم Redis تلقائيًا.
|
||||
إذا لم يكن كذلك، تحقق من [دليل تثبيت Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) لتوزيعتك.
|
||||
</Tab>
|
||||
|
||||
<Tab title="نظام Mac OS">
|
||||
من المفترض أن يكون Redis قد تم تشغيله بالفعل. إذا لم يكن كذلك، قم بتشغيل:
|
||||
|
||||
من المفترض أن يكون Redis قد تم تشغيله بالفعل. إذا لم يكن كذلك، قم بتشغيل:
|
||||
```bash
|
||||
brew services start redis
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="ويندوز (WSL)">
|
||||
اعتمادًا على توزيعة Linux الخاصة بك، قد يتم بدء خادم Redis تلقائيًا.
|
||||
إذا لم يكن كذلك، تحقق من [دليل تثبيت ريديس](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) لتوزيعتك.
|
||||
|
||||
@@ -25,7 +25,6 @@ Twenty مفتوح المصدر ويرحب بمساهمات المجتمع. سو
|
||||
<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">
|
||||
ساهم في واجهة المستخدم
|
||||
</Card>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
* **وثائق مخصصة**: يتم إنشاؤها خصيصًا لنموذج بيانات مساحة عملك
|
||||
|
||||
<Note>
|
||||
وثائق واجهة برمجة التطبيقات المخصصة لك متاحة ضمن **الإعدادات → API & Webhooks** بعد إنشاء مفتاح API. نظرًا لأن Twenty تُنشئ واجهات برمجة تطبيقات تتطابق مع نموذج البيانات المخصص لديك، فإن الوثائق فريدة لمساحة عملك.
|
||||
وثائق واجهة برمجة التطبيقات المخصصة لك متاحة ضمن **الإعدادات → API & Webhooks** بعد إنشاء مفتاح API. نظرًا لأن Twenty تُنشئ واجهات برمجة تطبيقات تتطابق مع نموذج البيانات المخصص لديك، فإن الوثائق فريدة لمساحة عملك.
|
||||
</Note>
|
||||
|
||||
## نوعا واجهات برمجة التطبيقات
|
||||
@@ -81,7 +81,7 @@ Authorization: Bearer YOUR_API_KEY
|
||||
<VimeoEmbed videoId="928786722" title="إنشاء مفتاح API" />
|
||||
|
||||
<Warning>
|
||||
يمنح مفتاح API الخاص بك الوصول إلى بيانات حساسة. لا تشاركه مع خدمات غير موثوقة. إذا تم اختراقه، عطّلْه فوراً وأنشئ مفتاحاً جديداً.
|
||||
يمنح مفتاح API الخاص بك الوصول إلى بيانات حساسة. لا تشاركه مع خدمات غير موثوقة. إذا تم اختراقه، عطّلْه فوراً وأنشئ مفتاحاً جديداً.
|
||||
</Warning>
|
||||
|
||||
### تعيين دور لمفتاح API
|
||||
@@ -143,5 +143,5 @@ Authorization: Bearer YOUR_API_KEY
|
||||
| **حجم الدفعة** | 60 سجل لكل استدعاء |
|
||||
|
||||
<Tip>
|
||||
استخدم عمليات الدفعات لزيادة الإنتاجية — عالج ما يصل إلى 60 سجلًا في استدعاء API واحد بدلاً من إجراء طلبات فردية.
|
||||
استخدم عمليات الدفعات لزيادة الإنتاجية — عالج ما يصل إلى 60 سجلًا في استدعاء API واحد بدلاً من إجراء طلبات فردية.
|
||||
</Tip>
|
||||
|
||||
@@ -4,7 +4,7 @@ description: أنشئ وأدِر تخصيصات Twenty على هيئة كود.
|
||||
---
|
||||
|
||||
<Warning>
|
||||
التطبيقات حاليًا في مرحلة الاختبار الألفا. الميزة تعمل لكنها لا تزال قيد التطور.
|
||||
التطبيقات حاليًا في مرحلة الاختبار الألفا. الميزة تعمل لكنها لا تزال قيد التطور.
|
||||
</Warning>
|
||||
|
||||
## ما هي التطبيقات؟
|
||||
@@ -32,13 +32,6 @@ description: أنشئ وأدِر تخصيصات Twenty على هيئة كود.
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# إذا كنت لا تستخدم yarn@4
|
||||
corepack enable
|
||||
yarn install
|
||||
|
||||
# قم بالمصادقة باستخدام مفتاح واجهة برمجة التطبيقات الخاص بك (سيُطلب منك ذلك)
|
||||
yarn twenty auth:login
|
||||
|
||||
# ابدأ وضع التطوير: يُزامن التغييرات المحلية تلقائيًا مع مساحة العمل الخاصة بك
|
||||
yarn twenty app:dev
|
||||
```
|
||||
@@ -59,22 +52,25 @@ npx create-twenty-app@latest my-app --interactive
|
||||
من هنا يمكنك:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new entity to your application (guided)
|
||||
# أضف كيانًا جديدًا إلى تطبيقك (موجّه)
|
||||
yarn twenty entity:add
|
||||
|
||||
# Watch your application's function logs
|
||||
# راقب سجلات وظائف تطبيقك
|
||||
yarn twenty function:logs
|
||||
|
||||
# Execute a function by name
|
||||
# نفّذ وظيفة بالاسم
|
||||
yarn twenty function:execute -n my-function -p '{"name": "test"}'
|
||||
|
||||
# Execute the post-install function
|
||||
# نفّذ دالة ما قبل التثبيت
|
||||
yarn twenty function:execute --preInstall
|
||||
|
||||
# نفّذ دالة ما بعد التثبيت
|
||||
yarn twenty function:execute --postInstall
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
# أزل تثبيت التطبيق من مساحة العمل الحالية
|
||||
yarn twenty app:uninstall
|
||||
|
||||
# Display commands' help
|
||||
# اعرض مساعدة الأوامر
|
||||
yarn twenty help
|
||||
```
|
||||
|
||||
@@ -87,7 +83,7 @@ yarn twenty help
|
||||
* ينسخ تطبيقًا أساسيًا مصغّرًا إلى `my-twenty-app/`
|
||||
* يضيف اعتمادًا محليًا `twenty-sdk` وتهيئة Yarn 4
|
||||
* ينشئ ملفات ضبط ونصوصًا مرتبطة بـ `twenty` CLI
|
||||
* يُنشئ الملفات الأساسية (تهيئة التطبيق، دور الدالة الافتراضي، دالة ما بعد التثبيت) بالإضافة إلى ملفات الأمثلة بحسب وضع الإنشاء
|
||||
* يُنشئ الملفات الأساسية (تهيئة التطبيق، دور الدالة الافتراضي، دالتا ما قبل التثبيت وما بعد التثبيت) بالإضافة إلى ملفات أمثلة استنادًا إلى وضع الإنشاء.
|
||||
|
||||
يبدو التطبيق المُنشأ حديثًا باستخدام الوضع الافتراضي `--exhaustive` كما يلي:
|
||||
|
||||
@@ -114,6 +110,7 @@ my-twenty-app/
|
||||
│ └── example-field.ts # تعريف حقل مستقل — مثال
|
||||
├── logic-functions/
|
||||
│ ├── hello-world.ts # دالة منطقية — مثال
|
||||
│ ├── pre-install.ts # دالة منطقية لما قبل التثبيت
|
||||
│ └── post-install.ts # دالة منطقية لما بعد التثبيت
|
||||
├── front-components/
|
||||
│ └── hello-world.tsx # مكوّن واجهة أمامية — مثال
|
||||
@@ -125,7 +122,7 @@ my-twenty-app/
|
||||
└── example-skill.ts # تعريف مهارة لوكيل الذكاء الاصطناعي — مثال
|
||||
```
|
||||
|
||||
مع `--minimal`، سيتم إنشاء الملفات الأساسية فقط (`application-config.ts` و`roles/default-role.ts` و`logic-functions/post-install.ts`). مع `--interactive`، تختار ملفات الأمثلة التي تريد تضمينها.
|
||||
مع `--minimal`، سيتم إنشاء الملفات الأساسية فقط (`application-config.ts`، `roles/default-role.ts`، `logic-functions/pre-install.ts`، و`logic-functions/post-install.ts`). مع `--interactive`، تختار ملفات الأمثلة التي تريد تضمينها.
|
||||
|
||||
بشكل عام:
|
||||
|
||||
@@ -142,19 +139,21 @@ my-twenty-app/
|
||||
|
||||
يكتشف SDK الكيانات عبر تحليل ملفات TypeScript الخاصة بك بحثًا عن استدعاءات **`export default define<Entity>({...})`**. يحتوي كل نوع كيان على دالة مساعدة مقابلة يتم تصديرها من `twenty-sdk`:
|
||||
|
||||
| دالة مساعدة | نوع الكيان |
|
||||
| ---------------------------- | ------------------------------------ |
|
||||
| `defineObject()` | تعريفات كائنات مخصصة |
|
||||
| `defineLogicFunction()` | تعريفات الوظائف المنطقية |
|
||||
| `defineFrontComponent()` | Front component definitions |
|
||||
| `defineRole()` | تعريفات الأدوار |
|
||||
| `defineField()` | امتدادات الحقول للكائنات الموجودة |
|
||||
| `defineView()` | تعريفات العروض المحفوظة |
|
||||
| `defineNavigationMenuItem()` | تعريفات عناصر قائمة التنقل |
|
||||
| `defineSkill()` | تعريفات مهارات وكيل الذكاء الاصطناعي |
|
||||
| دالة مساعدة | نوع الكيان |
|
||||
| ---------------------------------- | ---------------------------------------------- |
|
||||
| `defineObject()` | تعريفات كائنات مخصصة |
|
||||
| `defineLogicFunction()` | تعريفات الوظائف المنطقية |
|
||||
| `definePreInstallLogicFunction()` | دالة منطقية لما قبل التثبيت (تعمل قبل التثبيت) |
|
||||
| `definePostInstallLogicFunction()` | دالة منطقية لما بعد التثبيت (تعمل بعد التثبيت) |
|
||||
| `defineFrontComponent()` | Front component definitions |
|
||||
| `defineRole()` | تعريفات الأدوار |
|
||||
| `defineField()` | امتدادات الحقول للكائنات الموجودة |
|
||||
| `defineView()` | تعريفات العروض المحفوظة |
|
||||
| `defineNavigationMenuItem()` | تعريفات عناصر قائمة التنقل |
|
||||
| `defineSkill()` | تعريفات مهارات وكيل الذكاء الاصطناعي |
|
||||
|
||||
<Note>
|
||||
**تسمية الملفات مرنة.** يعتمد اكتشاف الكيانات على بنية الشجرة المجردة (AST) — إذ يقوم SDK بفحص ملفات المصدر لديك بحثًا عن النمط `export default define<Entity>({...})`. يمكنك تنظيم ملفاتك ومجلداتك كيفما تشاء. التجميع حسب نوع الكيان (مثلًا، `logic-functions/` و`roles/`) هو مجرد عرف لتنظيم الشيفرة، وليس مطلبًا إلزاميًا.
|
||||
**تسمية الملفات مرنة.** يعتمد اكتشاف الكيانات على بنية الشجرة المجردة (AST) — إذ يقوم SDK بفحص ملفات المصدر لديك بحثًا عن النمط `export default define<Entity>({...})`. يمكنك تنظيم ملفاتك ومجلداتك كيفما تشاء. التجميع حسب نوع الكيان (مثلًا، `logic-functions/` و`roles/`) هو مجرد عرف لتنظيم الشيفرة، وليس مطلبًا إلزاميًا.
|
||||
</Note>
|
||||
|
||||
مثال على كيان تم اكتشافه:
|
||||
@@ -172,7 +171,7 @@ export default defineObject({
|
||||
|
||||
ستضيف الأوامر اللاحقة مزيدًا من الملفات والمجلدات:
|
||||
|
||||
* `yarn twenty app:dev` سيولّد تلقائيًا عميل API مضبوط الأنواع في `node_modules/twenty-sdk/generated` (عميل Twenty مضبوط الأنواع + أنواع مساحة العمل).
|
||||
* سيقوم `yarn twenty app:dev` بتوليد عميلين API مضبوطي الأنواع تلقائيًا في `node_modules/twenty-sdk/generated`: `CoreApiClient` (لبيانات مساحة العمل عبر `/graphql`) و`MetadataApiClient` (لتكوين مساحة العمل وتحميل الملفات عبر `/metadata`).
|
||||
* `yarn twenty entity:add` سيضيف ملفات تعريف الكيانات ضمن `src/` لكائناتك المخصّصة، والوظائف، ومكوّنات الواجهة الأمامية، والأدوار، والمهارات، وغير ذلك.
|
||||
|
||||
## المصادقة
|
||||
@@ -216,17 +215,19 @@ yarn twenty auth:status
|
||||
|
||||
يوفّر SDK دوالًا مساعدة لتعريف كيانات تطبيقك. كما هو موضح في [اكتشاف الكيانات](#entity-detection)، يجب استخدام `export default define<Entity>({...})` كي يتم اكتشاف كياناتك:
|
||||
|
||||
| دالة | الغرض |
|
||||
| ---------------------------- | ---------------------------------------------------- |
|
||||
| `defineApplication()` | تهيئة بيانات التعريف للتطبيق (مطلوب، واحد لكل تطبيق) |
|
||||
| `defineObject()` | تعريف كائنات مخصصة مع حقول |
|
||||
| `defineLogicFunction()` | تعريف وظائف منطقية مع معالجات |
|
||||
| `defineFrontComponent()` | عرِّف مكوّنات أمامية لواجهة مستخدم مخصّصة |
|
||||
| `defineRole()` | تهيئة صلاحيات الدور والوصول إلى الكائنات |
|
||||
| `defineField()` | وسّع الكائنات الموجودة بحقول إضافية |
|
||||
| `defineView()` | تعريف العروض المحفوظة للكائنات |
|
||||
| `defineNavigationMenuItem()` | تعريف روابط التنقل في الشريط الجانبي |
|
||||
| `defineSkill()` | عرّف مهارات وكيل الذكاء الاصطناعي |
|
||||
| دالة | الغرض |
|
||||
| ---------------------------------- | ---------------------------------------------------- |
|
||||
| `defineApplication()` | تهيئة بيانات التعريف للتطبيق (مطلوب، واحد لكل تطبيق) |
|
||||
| `defineObject()` | تعريف كائنات مخصصة مع حقول |
|
||||
| `defineLogicFunction()` | تعريف وظائف منطقية مع معالجات |
|
||||
| `definePreInstallLogicFunction()` | تعريف دالة منطقية لما قبل التثبيت (واحدة لكل تطبيق) |
|
||||
| `definePostInstallLogicFunction()` | تعريف دالة منطقية لما بعد التثبيت (واحدة لكل تطبيق) |
|
||||
| `defineFrontComponent()` | عرِّف مكوّنات أمامية لواجهة مستخدم مخصّصة |
|
||||
| `defineRole()` | تهيئة صلاحيات الدور والوصول إلى الكائنات |
|
||||
| `defineField()` | وسّع الكائنات الموجودة بحقول إضافية |
|
||||
| `defineView()` | تعريف العروض المحفوظة للكائنات |
|
||||
| `defineNavigationMenuItem()` | تعريف روابط التنقل في الشريط الجانبي |
|
||||
| `defineSkill()` | عرّف مهارات وكيل الذكاء الاصطناعي |
|
||||
|
||||
تتحقق هذه الدوال من تكوينك وقت البناء وتوفّر إكمالًا تلقائيًا في بيئة التطوير وأمان الأنواع.
|
||||
|
||||
@@ -312,11 +313,11 @@ export default defineObject({
|
||||
* يمكنك إنشاء كائنات جديدة باستخدام `yarn twenty entity:add`، والذي يرشدك خلال التسمية والحقول والعلاقات.
|
||||
|
||||
<Note>
|
||||
**يتم إنشاء الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، يضيف Twenty تلقائيًا حقولًا قياسية
|
||||
مثل `id` و`name` و`createdAt` و`updatedAt` و`createdBy` و`updatedBy` و`deletedAt`.
|
||||
لا تحتاج إلى تعريف هذه في مصفوفة `fields` — أضف فقط حقولك المخصصة.
|
||||
يمكنك تجاوز الحقول الافتراضية من خلال تعريف حقل بالاسم نفسه في مصفوفة `fields` الخاصة بك،
|
||||
لكن هذا غير مستحسن.
|
||||
**يتم إنشاء الحقول الأساسية تلقائيًا.** عند تعريف كائن مخصص، يضيف Twenty تلقائيًا حقولًا قياسية
|
||||
مثل `id` و`name` و`createdAt` و`updatedAt` و`createdBy` و`updatedBy` و`deletedAt`.
|
||||
لا تحتاج إلى تعريف هذه في مصفوفة `fields` — أضف فقط حقولك المخصصة.
|
||||
يمكنك تجاوز الحقول الافتراضية من خلال تعريف حقل بالاسم نفسه في مصفوفة `fields` الخاصة بك،
|
||||
لكن هذا غير مستحسن.
|
||||
</Note>
|
||||
|
||||
### تكوين التطبيق (application-config.ts)
|
||||
@@ -326,6 +327,7 @@ export default defineObject({
|
||||
* **هوية التطبيق**: المعرفات، اسم العرض، والوصف.
|
||||
* **كيفية تشغيل وظائفه**: الدور الذي تستخدمه للأذونات.
|
||||
* **متغيرات (اختياري)**: أزواج مفتاح-قيمة تُعرض لوظائفك كمتغيرات بيئة.
|
||||
* **(اختياري) دالة ما قبل التثبيت**: دالة منطقية تعمل قبل تثبيت التطبيق.
|
||||
* **(Optional) post-install function**: a logic function that runs after the app is installed.
|
||||
|
||||
Use `defineApplication()` to define your application configuration:
|
||||
@@ -334,23 +336,21 @@ Use `defineApplication()` to define your application configuration:
|
||||
// src/application-config.ts
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
|
||||
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
||||
displayName: 'My Twenty App',
|
||||
description: 'My first Twenty app',
|
||||
displayName: 'تطبيق Twenty الخاص بي',
|
||||
description: 'أول تطبيق لي لـ Twenty',
|
||||
icon: 'IconWorld',
|
||||
applicationVariables: {
|
||||
DEFAULT_RECIPIENT_NAME: {
|
||||
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
|
||||
description: 'Default recipient name for postcards',
|
||||
description: 'الاسم الافتراضي للمستلم للبطاقات البريدية',
|
||||
value: 'Jane Doe',
|
||||
isSecret: false,
|
||||
},
|
||||
},
|
||||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -359,7 +359,7 @@ export default defineApplication({
|
||||
* حقول `universalIdentifier` هي معرّفات حتمية تخصك؛ أنشئها مرة واحدة واحتفظ بها ثابتة عبر عمليات المزامنة.
|
||||
* `applicationVariables` تصبح متغيرات بيئة لوظائفك (على سبيل المثال، `DEFAULT_RECIPIENT_NAME` متاح كـ `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `defaultRoleUniversalIdentifier` يجب أن يطابق ملف الدور (انظر أدناه).
|
||||
* `postInstallLogicFunctionUniversalIdentifier` (optional) points to a logic function that runs automatically after the app is installed. See [Post-install functions](#post-install-functions).
|
||||
* يتم اكتشاف دوال ما قبل التثبيت وما بعد التثبيت تلقائيًا أثناء إنشاء ملف البيان. راجع [دوال ما قبل التثبيت](#pre-install-functions) و[دوال ما بعد التثبيت](#post-install-functions).
|
||||
|
||||
#### الأدوار والصلاحيات
|
||||
|
||||
@@ -433,10 +433,10 @@ export default defineRole({
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/generated';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient();
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -498,6 +498,44 @@ export default defineLogicFunction({
|
||||
* المصفوفة `triggers` اختيارية. يمكن استخدام الوظائف بدون مشغلات كوظائف مساعدة تُستدعى بواسطة وظائف أخرى.
|
||||
* يمكنك مزج أنواع متعددة من المشغلات في وظيفة واحدة.
|
||||
|
||||
### دوال ما قبل التثبيت
|
||||
|
||||
دالة ما قبل التثبيت هي دالة منطقية تعمل تلقائيًا قبل تثبيت تطبيقك على مساحة عمل. يفيد ذلك في مهام التحقق، وفحص المتطلبات المسبقة، أو تجهيز حالة مساحة العمل قبل متابعة التثبيت الرئيسي.
|
||||
|
||||
عند إنشاء هيكل تطبيق جديد باستخدام `create-twenty-app`، يتم إنشاء دالة ما قبل التثبيت لك في `src/logic-functions/pre-install.ts`:
|
||||
|
||||
```typescript
|
||||
// src/logic-functions/pre-install.ts
|
||||
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Pre install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default definePreInstallLogicFunction({
|
||||
universalIdentifier: '<generated-uuid>',
|
||||
name: 'pre-install',
|
||||
description: 'Runs before installation to prepare the application.',
|
||||
timeoutSeconds: 300,
|
||||
handler,
|
||||
});
|
||||
```
|
||||
|
||||
يمكنك أيضًا تنفيذ دالة ما قبل التثبيت يدويًا في أي وقت باستخدام CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty function:execute --preInstall
|
||||
```
|
||||
|
||||
النقاط الرئيسية:
|
||||
|
||||
* تستخدم دوال ما قبل التثبيت `definePreInstallLogicFunction()` — وهو إصدار متخصص يستبعد إعدادات المُشغِّل (`cronTriggerSettings` و`databaseEventTriggerSettings` و`httpRouteTriggerSettings` و`isTool`).
|
||||
* يتلقى المُعالج `InstallLogicFunctionPayload` يحوي `{ previousVersion: string }` — إصدار التطبيق الذي كان مُثبّتًا سابقًا (أو سلسلة فارغة للتثبيتات الجديدة).
|
||||
* يُسمح بدالة ما قبل التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة.
|
||||
* يتم تعيين `universalIdentifier` للدالة تلقائيًا كـ `preInstallLogicFunctionUniversalIdentifier` في بيان التطبيق أثناء الإنشاء — لست بحاجة إلى الإشارة إليه في `defineApplication()`.
|
||||
* تم ضبط المهلة الافتراضية على 300 ثانية (5 دقائق) للسماح بمهام التحضير الأطول.
|
||||
* لا تحتاج دوال ما قبل التثبيت إلى مُشغِّلات — إذ يستدعيها النظام الأساسي قبل التثبيت أو يدويًا عبر `function:execute --preInstall`.
|
||||
|
||||
### Post-install functions
|
||||
|
||||
A post-install function is a logic function that runs automatically after your app is installed on a workspace. This is useful for one-time setup tasks such as seeding default data, creating initial records, or configuring workspace settings.
|
||||
@@ -506,16 +544,14 @@ A post-install function is a logic function that runs automatically after your a
|
||||
|
||||
```typescript
|
||||
// src/logic-functions/post-install.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';
|
||||
|
||||
export const POST_INSTALL_UNIVERSAL_IDENTIFIER = '<generated-uuid>';
|
||||
|
||||
const handler = async (): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!');
|
||||
const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
|
||||
console.log('Post install logic function executed successfully!', payload.previousVersion);
|
||||
};
|
||||
|
||||
export default defineLogicFunction({
|
||||
universalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
export default definePostInstallLogicFunction({
|
||||
universalIdentifier: '<generated-uuid>',
|
||||
name: 'post-install',
|
||||
description: 'Runs after installation to set up the application.',
|
||||
timeoutSeconds: 300,
|
||||
@@ -523,17 +559,6 @@ export default defineLogicFunction({
|
||||
});
|
||||
```
|
||||
|
||||
يتم ربط الدالة بتطبيقك من خلال الإشارة إلى المعرِّف العالمي الخاص بها في `application-config.ts`:
|
||||
|
||||
```typescript
|
||||
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';
|
||||
|
||||
export default defineApplication({
|
||||
// ...
|
||||
postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
```
|
||||
|
||||
يمكنك أيضًا تنفيذ دالة ما بعد التثبيت يدويًا في أي وقت باستخدام CLI:
|
||||
|
||||
```bash filename="Terminal"
|
||||
@@ -542,35 +567,35 @@ yarn twenty function:execute --postInstall
|
||||
|
||||
النقاط الرئيسية:
|
||||
|
||||
* دوال ما بعد التثبيت هي دوال منطقية قياسية — فهي تستخدم `defineLogicFunction()` مثل أي دالة أخرى.
|
||||
* حقل `postInstallLogicFunctionUniversalIdentifier` في `defineApplication()` اختياري. إذا تم تجاهله، لن يتم تشغيل أي دالة بعد التثبيت.
|
||||
* تستخدم دوال ما بعد التثبيت `definePostInstallLogicFunction()` — وهو إصدار متخصص يستبعد إعدادات المُشغِّل (`cronTriggerSettings` و`databaseEventTriggerSettings` و`httpRouteTriggerSettings` و`isTool`).
|
||||
* يتلقى المُعالج `InstallLogicFunctionPayload` يحوي `{ previousVersion: string }` — إصدار التطبيق الذي كان مُثبّتًا سابقًا (أو سلسلة فارغة للتثبيتات الجديدة).
|
||||
* يُسمح بدالة ما بعد التثبيت واحدة فقط لكل تطبيق. سيُنتج إنشاء ملف البيان خطأً إذا تم اكتشاف أكثر من واحدة.
|
||||
* يتم تعيين `universalIdentifier` للدالة تلقائيًا كـ `postInstallLogicFunctionUniversalIdentifier` في بيان التطبيق أثناء الإنشاء — لست بحاجة إلى الإشارة إليه في `defineApplication()`.
|
||||
* تم تعيين مهلة افتراضية إلى 300 ثانية (5 دقائق) للسماح بمهام الإعداد الأطول مثل تهيئة البيانات.
|
||||
* لا تحتاج دوال ما بعد التثبيت إلى مُشغِّلات — حيث يستدعيها النظام الأساسي أثناء التثبيت أو يدويًا عبر `function:execute --postInstall`.
|
||||
|
||||
### حمولة مشغل المسار
|
||||
|
||||
<Warning>
|
||||
**تغيير غير متوافق (v1.16، يناير 2026):** لقد تغير تنسيق حمولة مشغل المسار. قبل v1.16، كانت معلمات الاستعلام، ومعلمات المسار، وجسم الطلب تُرسل مباشرةً كحمولة. بدءًا من v1.16، أصبحت متداخلة داخل كائن منظَّم `RoutePayload`.
|
||||
**تغيير غير متوافق (v1.16، يناير 2026):** لقد تغير تنسيق حمولة مشغل المسار. قبل v1.16، كانت معلمات الاستعلام، ومعلمات المسار، وجسم الطلب تُرسل مباشرةً كحمولة. بدءًا من v1.16، أصبحت متداخلة داخل كائن منظَّم `RoutePayload`.
|
||||
|
||||
**قبل v1.16:**
|
||||
**قبل v1.16:**
|
||||
```typescript
|
||||
const handler = async (params) => {
|
||||
const { param1, param2 } = params; // Direct access
|
||||
};
|
||||
```
|
||||
|
||||
```typescript
|
||||
const handler = async (params) => {
|
||||
const { param1, param2 } = params; // Direct access
|
||||
};
|
||||
```
|
||||
**بعد v1.16:**
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const { param1, param2 } = event.body; // Access via .body
|
||||
const { queryParam } = event.queryStringParameters;
|
||||
const { id } = event.pathParameters;
|
||||
};
|
||||
```
|
||||
|
||||
**بعد v1.16:**
|
||||
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const { param1, param2 } = event.body; // Access via .body
|
||||
const { queryParam } = event.queryStringParameters;
|
||||
const { id } = event.pathParameters;
|
||||
};
|
||||
```
|
||||
|
||||
**لترحيل الدوال الحالية:** حدّث المعالج لديك لفكّ البنية من `event.body` أو `event.queryStringParameters` أو `event.pathParameters` بدلاً من القراءة مباشرةً من كائن params.
|
||||
**لترحيل الدوال الحالية:** حدّث المعالج لديك لفكّ البنية من `event.body` أو `event.queryStringParameters` أو `event.pathParameters` بدلاً من القراءة مباشرةً من كائن params.
|
||||
</Warning>
|
||||
|
||||
عندما يستدعي مشغّل المسار وظيفتك المنطقية، يتلقى كائنًا من النوع `RoutePayload` يتبع تنسيق AWS HTTP API v2. استورد النوع من `twenty-sdk`:
|
||||
@@ -653,10 +678,10 @@ const handler = async (event: RoutePayload) => {
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/generated';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -706,7 +731,7 @@ export default defineLogicFunction({
|
||||
* يمكنك دمج `isTool` مع المشغِّلات — إذ يمكن للدالة أن تكون أداة (قابلة للاستدعاء من قِبل وكلاء الذكاء الاصطناعي) وأن تُشغَّل بواسطة أحداث (cron، وأحداث قاعدة البيانات، والمسارات) في الوقت نفسه.
|
||||
|
||||
<Note>
|
||||
**اكتب `description` جيدًا.** يعتمد وكلاء الذكاء الاصطناعي على حقل `description` الخاص بالدالة لتحديد وقت استخدام الأداة. كن محددًا بشأن ما تفعله الأداة ومتى ينبغي استدعاؤها.
|
||||
**اكتب `description` جيدًا.** يعتمد وكلاء الذكاء الاصطناعي على حقل `description` الخاص بالدالة لتحديد وقت استخدام الأداة. كن محددًا بشأن ما تفعله الأداة ومتى ينبغي استدعاؤها.
|
||||
</Note>
|
||||
|
||||
### المكوّنات الأمامية
|
||||
@@ -714,7 +739,7 @@ export default defineLogicFunction({
|
||||
تتيح لك المكوّنات الأمامية إنشاء مكوّنات React مخصّصة تُعرَض داخل واجهة مستخدم Twenty. استخدم `defineFrontComponent()` لتعريف مكوّنات مع تحقّق مدمج:
|
||||
|
||||
```typescript
|
||||
// src/my-widget.front-component.tsx
|
||||
// src/front-components/my-widget.tsx
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
const MyWidget = () => {
|
||||
@@ -737,14 +762,13 @@ export default defineFrontComponent({
|
||||
النقاط الرئيسية:
|
||||
|
||||
* المكوّنات الأمامية هي مكوّنات React تُعرَض ضمن سياقات معزولة داخل Twenty.
|
||||
* استخدم لاحقة الملف `*.front-component.tsx` للاكتشاف التلقائي.
|
||||
* يشير الحقل `component` إلى مكوّن React الخاص بك.
|
||||
* يتم بناء المكوّنات ومزامنتها تلقائيًا أثناء `yarn twenty app:dev`.
|
||||
|
||||
يمكنك إنشاء مكوّنات أمامية جديدة بطريقتين:
|
||||
|
||||
* **مُنشأ بالقالب**: شغّل `yarn twenty entity:add` واختر خيار إضافة مكوّن أمامي جديد.
|
||||
* **يدوي**: أنشئ ملفًا جديدًا `*.front-component.tsx` واستخدم `defineFrontComponent()`.
|
||||
* **يدوي**: أنشئ ملفًا جديدًا `.tsx` واستخدم `defineFrontComponent()` مع اتباع النمط نفسه.
|
||||
|
||||
### المهارات
|
||||
|
||||
@@ -781,18 +805,24 @@ export default defineSkill({
|
||||
* **مُنشأ بالقالب**: شغِّل `yarn twenty entity:add` واختر خيار إضافة مهارة جديدة.
|
||||
* **يدوي**: أنشئ ملفًا جديدًا واستخدم `defineSkill()` مع اتباع النمط نفسه.
|
||||
|
||||
### عميل مُولَّد مضبوط الأنواع
|
||||
### عملاء مُولَّدون مضبوطو الأنواع
|
||||
|
||||
يُولَّد العميل مضبوط الأنواع تلقائيًا بواسطة `yarn twenty app:dev` ويُخزَّن في `node_modules/twenty-sdk/generated` استنادًا إلى مخطط مساحة العمل لديك. استخدمه في وظائفك:
|
||||
يتم توليد عميلين مضبوطي الأنواع تلقائيًا بواسطة `yarn twenty app:dev` وتخزينهما في `node_modules/twenty-sdk/generated` استنادًا إلى مخطط مساحة العمل لديك:
|
||||
|
||||
* **`CoreApiClient`** — يُجري استعلامات إلى نقطة النهاية `/graphql` للحصول على بيانات مساحة العمل
|
||||
* **`MetadataApiClient`** — يستعلم عن نقطة النهاية `/metadata` لتكوين مساحة العمل وتحميل الملفات.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
|
||||
const metadataClient = new MetadataApiClient();
|
||||
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
|
||||
```
|
||||
|
||||
يُعاد توليد العميل تلقائيًا بواسطة `yarn twenty app:dev` كلما تغيّرت كائناتك أو حقولك.
|
||||
يُعاد توليد كلا العميلين تلقائيًا بواسطة `yarn twenty app:dev` كلما تغيّرت كائناتك أو حقولك.
|
||||
|
||||
#### بيانات الاعتماد وقت التشغيل في الوظائف المنطقية
|
||||
|
||||
@@ -809,17 +839,17 @@ const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
|
||||
#### رفع الملفات
|
||||
|
||||
يتضمن العميل `Twenty` المُولَّد طريقة `uploadFile` لإرفاق الملفات بالحقول من نوع ملف ضمن كائنات مساحة العمل الخاصة بك. نظرًا لأن عملاء GraphQL القياسيون لا يدعمون تحميل الملفات متعددة الأجزاء افتراضيًا، يوفر العميل هذه الطريقة المخصصة التي تطبق [مواصفة طلب GraphQL متعدد الأجزاء](https://github.com/jaydenseric/graphql-multipart-request-spec) في الخلفية.
|
||||
يتضمن `MetadataApiClient` المُولَّد طريقة `uploadFile` لإرفاق الملفات بالحقول من نوع ملف ضمن كائنات مساحة العمل الخاصة بك. نظرًا لأن عملاء GraphQL القياسيون لا يدعمون تحميل الملفات متعددة الأجزاء افتراضيًا، يوفر العميل هذه الطريقة المخصصة التي تطبق [مواصفة طلب GraphQL متعدد الأجزاء](https://github.com/jaydenseric/graphql-multipart-request-spec) في الخلفية.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/generated';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const metadataClient = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
const uploadedFile = await client.uploadFile(
|
||||
const uploadedFile = await metadataClient.uploadFile(
|
||||
fileBuffer, // file contents as a Buffer
|
||||
'invoice.pdf', // filename
|
||||
'application/pdf', // MIME type (defaults to 'application/octet-stream')
|
||||
@@ -850,7 +880,7 @@ uploadFile(
|
||||
|
||||
النقاط الرئيسية:
|
||||
|
||||
* ترسل هذه الطريقة الملف إلى **نقطة نهاية البيانات الوصفية** (وليست نقطة النهاية الرئيسية لـ GraphQL)، حيث تُنفَّذ عملية الرفع.
|
||||
* تتوفر طريقة `uploadFile` على `MetadataApiClient` لأن عملية الـ mutation الخاصة بالرفع تُعالَج عبر نقطة النهاية `/metadata`.
|
||||
* تستخدم `universalIdentifier` الخاص بالحقل (وليس المعرّف الخاص بمساحة العمل)، ليعمل كود الرفع لديك عبر أي مساحة عمل مُثبَّت فيها تطبيقك — بما يتماشى مع كيفية إشارة التطبيقات إلى الحقول في كل مكان آخر.
|
||||
* العنوان `url` المُعاد هو عنوان URL موقّع يمكنك استخدامه للوصول إلى الملف المرفوع.
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
| `الطابع الزمني` | وقت حدوث الحدث (UTC) |
|
||||
|
||||
<Note>
|
||||
استجب بحالة **HTTP 2xx** (200-299) لتأكيد الاستلام. تُسجَّل الاستجابات غير 2xx كإخفاقات في التسليم.
|
||||
استجب بحالة **HTTP 2xx** (200-299) لتأكيد الاستلام. تُسجَّل الاستجابات غير 2xx كإخفاقات في التسليم.
|
||||
</Note>
|
||||
|
||||
## التحقق من صحة خطاف الويب
|
||||
|
||||
@@ -23,11 +23,9 @@ description: وسّع وظائف Twenty باستخدام واجهات برمجة
|
||||
<Card title="واجهات برمجة التطبيقات" icon="كود" href="/l/ar/developers/extend/capabilities/apis">
|
||||
اتصل بـ Twenty برمجياً
|
||||
</Card>
|
||||
|
||||
<Card title="الويب هوكس" icon="bell" href="/l/ar/developers/extend/capabilities/webhooks">
|
||||
احصل على إشعارات بالأحداث في الوقت الفعلي
|
||||
</Card>
|
||||
|
||||
<Card title="التطبيقات" icon="puzzle-piece" href="/l/ar/developers/extend/capabilities/apps">
|
||||
أنشئ تخصيصات كرمز برمجي (ألفا)
|
||||
</Card>
|
||||
|
||||
@@ -3,7 +3,7 @@ title: بنقرة واحدة مع Docker Compose
|
||||
---
|
||||
|
||||
<Warning>
|
||||
الحاويات الخاصة بدوكر مخصصة للاستضافة الإنتاجية أو الاستضافة الذاتية، للتحقيق يرجى التحقق من [الإعداد المحلي](/l/ar/developers/contribute/capabilities/local-setup).
|
||||
الحاويات الخاصة بدوكر مخصصة للاستضافة الإنتاجية أو الاستضافة الذاتية، للتحقيق يرجى التحقق من [الإعداد المحلي](/l/ar/developers/contribute/capabilities/local-setup).
|
||||
</Warning>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
@@ -5,7 +5,7 @@ title: إعداد
|
||||
# إدارة الإعدادات
|
||||
|
||||
<Warning>
|
||||
**هل هي المرة الأولى التي تقوم فيها بالتثبيت؟** اتبع [دليل تثبيت Docker Compose](/l/ar/developers/self-host/capabilities/docker-compose) لتشغيل Twenty، ثم عد هنا للإعداد.
|
||||
**هل هي المرة الأولى التي تقوم فيها بالتثبيت؟** اتبع [دليل تثبيت Docker Compose](/l/ar/developers/self-host/capabilities/docker-compose) لتشغيل Twenty، ثم عد هنا للإعداد.
|
||||
</Warning>
|
||||
|
||||
يوفر Twenty **وضعين للإعداد** ليلائم احتياجات النشر المختلفة:
|
||||
@@ -26,7 +26,7 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # افتراضي
|
||||
4. تسري التغييرات على الفور (خلال 15 ثانية لعمليات النشر متعددة الحاويات)
|
||||
|
||||
<Warning>
|
||||
**نشرات متعددة الحاويات:** عند استخدام إعدادات قاعدة البيانات (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`)، يقوم كل من حاويات الخادم والعامل بالقراءة من نفس قاعدة البيانات. التغييرات في لوحة الإدارة تؤثر عليهما تلقائيًا، مما يلغي الحاجة إلى تكرار متغيرات البيئة بين الحاويات (باستثناء متغيرات البنية التحتية).
|
||||
**نشرات متعددة الحاويات:** عند استخدام إعدادات قاعدة البيانات (`IS_CONFIG_VARIABLES_IN_DB_ENABLED=true`)، يقوم كل من حاويات الخادم والعامل بالقراءة من نفس قاعدة البيانات. التغييرات في لوحة الإدارة تؤثر عليهما تلقائيًا، مما يلغي الحاجة إلى تكرار متغيرات البيئة بين الحاويات (باستثناء متغيرات البنية التحتية).
|
||||
</Warning>
|
||||
|
||||
**ما يمكنك تكوينه عبر لوحة الإدارة:**
|
||||
@@ -41,10 +41,10 @@ IS_CONFIG_VARIABLES_IN_DB_ENABLED=true # افتراضي
|
||||

|
||||
|
||||
<Warning>
|
||||
كل متغير موثق بوصف في لوحة الإدارة الخاصة بك في **الإعدادات → لوحة الإدارة → متغيرات التكوين**.
|
||||
بعض إعدادات البنية التحتية مثل اتصالات قاعدة البيانات (`PG_DATABASE_URL`)، عناوين الخوادم (`SERVER_URL`)، وأسرار التطبيقات (`APP_SECRET`) يمكن ضبطها فقط عبر ملف `.env`.
|
||||
كل متغير موثق بوصف في لوحة الإدارة الخاصة بك في **الإعدادات → لوحة الإدارة → متغيرات التكوين**.
|
||||
بعض إعدادات البنية التحتية مثل اتصالات قاعدة البيانات (`PG_DATABASE_URL`)، عناوين الخوادم (`SERVER_URL`)، وأسرار التطبيقات (`APP_SECRET`) يمكن ضبطها فقط عبر ملف `.env`.
|
||||
|
||||
[مرجع تقني كامل →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts)
|
||||
[مرجع تقني كامل →](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts)
|
||||
</Warning>
|
||||
|
||||
## 2. إعداد بيئي فقط
|
||||
@@ -93,7 +93,7 @@ DEFAULT_SUBDOMAIN=app # default value
|
||||
* إعدادات خاصة بمساحة العمل مثل النطاق الفرعي والنطاق المخصص تصبح متاحة ضمن إعدادات مساحة العمل
|
||||
|
||||
<Warning>
|
||||
**إعداد خاص بالبيئة فقط:** لا يمكن تكوين `IS_MULTIWORKSPACE_ENABLED` إلا عبر ملف `.env` ويتطلب إعادة تشغيل. لا يمكن تغييره عبر لوحة الإدارة.
|
||||
**إعداد خاص بالبيئة فقط:** لا يمكن تكوين `IS_MULTIWORKSPACE_ENABLED` إلا عبر ملف `.env` ويتطلب إعادة تشغيل. لا يمكن تغييره عبر لوحة الإدارة.
|
||||
</Warning>
|
||||
|
||||
### تكوين DNS لوضع تعدد مساحات العمل
|
||||
@@ -149,7 +149,7 @@ IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
|
||||
* `AUTH_GOOGLE_APIS_CALLBACK_URL=https://{your-domain}/auth/google-apis/get-access-token`
|
||||
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
**النطاقات المطلوبة** (يتم تكوينها تلقائيًا):
|
||||
@@ -168,7 +168,7 @@ IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
|
||||
## تكامل Microsoft 365
|
||||
|
||||
<Warning>
|
||||
يجب على المستخدمين الحصول على [ترخيص Microsoft 365](https://admin.microsoft.com/Adminportal/Home) ليتمكنوا من استخدام تقويم API ورسائل. لن يتمكنوا من مزامنة حسابهم في Twenty دون واحد منها.
|
||||
يجب على المستخدمين الحصول على [ترخيص Microsoft 365](https://admin.microsoft.com/Adminportal/Home) ليتمكنوا من استخدام تقويم API ورسائل. لن يتمكنوا من مزامنة حسابهم في Twenty دون واحد منها.
|
||||
</Warning>
|
||||
|
||||
### إنشاء مشروع في Microsoft Azure
|
||||
@@ -211,7 +211,7 @@ IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=true
|
||||
* `AUTH_MICROSOFT_APIS_CALLBACK_URL=https://{your-domain}/auth/microsoft-apis/get-access-token`
|
||||
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
### تكوين النطاقات
|
||||
@@ -256,40 +256,45 @@ yarn command:prod cron:workflow:automated-cron-trigger
|
||||
3. قم بضبط إعدادات SMTP الخاصة بك:
|
||||
|
||||
<ArticleTabs label1="جيميل" label2="أوفيس 365" label3="Smtp4dev">
|
||||
<ArticleTab>
|
||||
ستحتاج إلى توفير [كلمة مرور التطبيق](https://support.google.com/accounts/answer/185833).
|
||||
|
||||
<ArticleTab>
|
||||
|
||||
ستحتاج إلى توفير [كلمة مرور التطبيق](https://support.google.com/accounts/answer/185833).
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.gmail.com
|
||||
* EMAIL_SMTP_PORT=465
|
||||
* EMAIL_SMTP_USER=gmail_email_address
|
||||
* EMAIL_SMTP_PASSWORD='gmail_app_password'
|
||||
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
|
||||
تذكر أنه إذا كنت تشغل التحقق بعاملين، ستحتاج إلى توفير [كلمة مرور التطبيق](https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9).
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.office365.com
|
||||
* EMAIL_SMTP_PORT=587
|
||||
* EMAIL_SMTP_USER=office365_email_address
|
||||
* EMAIL_SMTP_PASSWORD='office365_password'
|
||||
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
|
||||
**smtp4dev** هو خادم بريد إلكتروني مزيف للتطوير والاختبار.
|
||||
* قم بتشغيل صورة smtp4dev: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev`
|
||||
* الوصول إلى واجهة المستخدم smtp4dev هنا: [http://localhost:8090](http://localhost:8090)
|
||||
* حدد المتغيرات التالية:
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.gmail.com
|
||||
* EMAIL_SMTP_PORT=465
|
||||
* EMAIL_SMTP_USER=gmail_email_address
|
||||
* EMAIL_SMTP_PASSWORD='gmail_app_password'
|
||||
* EMAIL_SMTP_HOST=localhost
|
||||
* EMAIL_SMTP_PORT=2525
|
||||
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
تذكر أنه إذا كنت تشغل التحقق بعاملين، ستحتاج إلى توفير [كلمة مرور التطبيق](https://support.microsoft.com/en-us/account-billing/manage-app-passwords-for-two-step-verification-d6dc8c6d-4bf7-4851-ad95-6d07799387e9).
|
||||
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=smtp.office365.com
|
||||
* EMAIL_SMTP_PORT=587
|
||||
* EMAIL_SMTP_USER=office365_email_address
|
||||
* EMAIL_SMTP_PASSWORD='office365_password'
|
||||
</ArticleTab>
|
||||
|
||||
<ArticleTab>
|
||||
**smtp4dev** هو خادم بريد إلكتروني مزيف للتطوير والاختبار.
|
||||
|
||||
* قم بتشغيل صورة smtp4dev: `docker run --rm -it -p 8090:80 -p 2525:25 rnwood/smtp4dev`
|
||||
* الوصول إلى واجهة المستخدم smtp4dev هنا: [http://localhost:8090](http://localhost:8090)
|
||||
* حدد المتغيرات التالية:
|
||||
* EMAIL_DRIVER=smtp
|
||||
* EMAIL_SMTP_HOST=localhost
|
||||
* EMAIL_SMTP_PORT=2525
|
||||
</ArticleTab>
|
||||
</ArticleTabs>
|
||||
|
||||
<Warning>
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
**وضع بيئي فقط:** إذا كنت قد ضبطت `IS_CONFIG_VARIABLES_IN_DB_ENABLED=false`، فأضف هذه المتغيرات إلى ملف `.env` الخاص بك بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
## الوظائف المنطقية
|
||||
@@ -297,7 +302,7 @@ yarn command:prod cron:workflow:automated-cron-trigger
|
||||
تدعم Twenty الوظائف المنطقية لعمليات سير العمل والمنطق المخصص. يتم تكوين بيئة التنفيذ عبر متغير البيئة `SERVERLESS_TYPE`.
|
||||
|
||||
<Warning>
|
||||
**ملاحظة أمنية:** يقوم برنامج التشغيل المحلي (`SERVERLESS_TYPE=LOCAL`) بتشغيل الشيفرة مباشرةً على المضيف ضمن عملية Node.js من دون عزل. يجب استخدامه فقط للشيفرة الموثوقة أثناء التطوير. بالنسبة لعمليات النشر الإنتاجية التي تتعامل مع شيفرة غير موثوق بها، نوصي بشدة باستخدام `SERVERLESS_TYPE=LAMBDA` أو `SERVERLESS_TYPE=DISABLED`.
|
||||
**ملاحظة أمنية:** يقوم برنامج التشغيل المحلي (`SERVERLESS_TYPE=LOCAL`) بتشغيل الشيفرة مباشرةً على المضيف ضمن عملية Node.js من دون عزل. يجب استخدامه فقط للشيفرة الموثوقة أثناء التطوير. بالنسبة لعمليات النشر الإنتاجية التي تتعامل مع شيفرة غير موثوق بها، نوصي بشدة باستخدام `SERVERLESS_TYPE=LAMBDA` أو `SERVERLESS_TYPE=DISABLED`.
|
||||
</Warning>
|
||||
|
||||
### برامج التشغيل المتاحة
|
||||
@@ -333,5 +338,5 @@ SERVERLESS_TYPE=DISABLED
|
||||
```
|
||||
|
||||
<Note>
|
||||
عند استخدام `SERVERLESS_TYPE=DISABLED`، ستؤدي أي محاولة لتنفيذ وظيفة منطقية إلى إرجاع خطأ. يكون هذا مفيدًا إذا كنت ترغب في تشغيل Twenty من دون إمكانات الوظائف المنطقية.
|
||||
عند استخدام `SERVERLESS_TYPE=DISABLED`، ستؤدي أي محاولة لتنفيذ وظيفة منطقية إلى إرجاع خطأ. يكون هذا مفيدًا إذا كنت ترغب في تشغيل Twenty من دون إمكانات الوظائف المنطقية.
|
||||
</Note>
|
||||
|
||||
@@ -23,7 +23,6 @@ description: قم بنشر Twenty وإدارته على البنية التحت
|
||||
<Card title="Docker Compose" icon="docker" href="/l/ar/developers/self-host/capabilities/docker-compose">
|
||||
إعداد سريع باستخدام Docker
|
||||
</Card>
|
||||
|
||||
<Card title="مزودو الخدمات السحابية" icon="cloud" href="/l/ar/developers/self-host/capabilities/cloud-providers">
|
||||
انشر على AWS أو GCP أو Azure
|
||||
</Card>
|
||||
|
||||
@@ -10,46 +10,54 @@ image: /images/user-guide/tips/light-bulb.png
|
||||
رسالة مختصرة تعرض معلومات إضافية عند تفاعل المستخدم مع عنصر.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { AppTooltip } from "@/ui/display/tooltip/AppTooltip";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<p id="hoverText" style={{ display: "inline-block" }}>
|
||||
رؤى العملاء
|
||||
</p>
|
||||
<AppTooltip
|
||||
className
|
||||
anchorSelect="#hoverText"
|
||||
content="استكشاف سلوك العملاء وتفضيلاتهم"
|
||||
delayHide={0}
|
||||
offset={6}
|
||||
noArrow={false}
|
||||
isOpen={true}
|
||||
place="bottom"
|
||||
positionStrategy="absolute"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { AppTooltip } from "@/ui/display/tooltip/AppTooltip";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<p id="hoverText" style={{ display: "inline-block" }}>
|
||||
رؤى العملاء
|
||||
</p>
|
||||
<AppTooltip
|
||||
className
|
||||
anchorSelect="#hoverText"
|
||||
content="استكشاف سلوك العملاء وتفضيلاتهم"
|
||||
delayHide={0}
|
||||
offset={6}
|
||||
noArrow={false}
|
||||
isOpen={true}
|
||||
place="bottom"
|
||||
positionStrategy="absolute"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| اسم الفئة | نص | فئة 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>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| اسم الفئة | نص | فئة 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>
|
||||
|
||||
## نص متجاوز مع تلميح
|
||||
@@ -57,22 +65,30 @@ image: /images/user-guide/tips/light-bulb.png
|
||||
يعالج النص الزائد ويعرض مربط تنبيهي عند فيضان النص.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const crmTaskDescription =
|
||||
'المتابعة مع العميل بشأن استفساره الأخير عن المنتج. مناقشة خيارات التسعير، ومعالجة أي مخاوف، وتقديم معلومات إضافية عن المنتج. تسجيل تفاصيل المحادثة في نظام إدارة علاقات العملاء (CRM) للرجوع إليها لاحقاً.';
|
||||
```jsx
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
|
||||
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const crmTaskDescription =
|
||||
'المتابعة مع العميل بشأن استفساره الأخير عن المنتج. مناقشة خيارات التسعير، ومعالجة أي مخاوف، وتقديم معلومات إضافية عن المنتج. تسجيل تفاصيل المحادثة في نظام إدارة علاقات العملاء (CRM) للرجوع إليها لاحقاً.';
|
||||
|
||||
return <OverflowingTextWithTooltip text={crmTaskDescription} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------- | ------ | --------------------------------------------- |
|
||||
| نص | string | المحتوى الذي تريد عرضه في منطقة النص المتجاوز |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------- | ------ | --------------------------------------------- |
|
||||
| نص | string | المحتوى الذي تريد عرضه في منطقة النص المتجاوز |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,19 +10,25 @@ image: /images/user-guide/tasks/tasks_header.png
|
||||
يمثل إجراءً ناجحًا أو مكتملًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Checkmark } from 'twenty-ui/display';
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <Checkmark />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Checkmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <Checkmark />;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
|
||||
يمتد `React.ComponentPropsWithoutRef<'div'>` و يقبل جميع خصائص عنصر `div` العادي.
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
يمتد `React.ComponentPropsWithoutRef<'div'>` و يقبل جميع خصائص عنصر `div` العادي.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## علامة صحيح متحركة
|
||||
@@ -30,29 +36,38 @@ image: /images/user-guide/tasks/tasks_header.png
|
||||
يمثل رمز علامة صحيح مع ميزة الإضافة للحركة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { AnimatedCheckmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AnimatedCheckmark
|
||||
isAnimating={true}
|
||||
color="green"
|
||||
duration={0.5}
|
||||
size={30}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="استخدام">
|
||||
|
||||
```jsx
|
||||
import { AnimatedCheckmark } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AnimatedCheckmark
|
||||
isAnimating={true}
|
||||
color="green"
|
||||
duration={0.5}
|
||||
size={30}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | ------------------------------------- | ----------------- |
|
||||
| isAnimating | قيمة منطقية | يتحكم فيما إذا كانت علامة صحيح متحركة | خاطئ |
|
||||
| اللون | string | لون علامة الاختيار | |
|
||||
| المدة | رقم | مدة الحركة بالثواني | 0.5 ثانية |
|
||||
| الحجم | رقم | حجم علامة الاختيار | 28 بكسل |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | ------------------------------------- | ----------------- |
|
||||
| isAnimating | قيمة منطقية | يتحكم فيما إذا كانت علامة صحيح متحركة | خاطئ |
|
||||
| اللون | string | لون علامة الاختيار | |
|
||||
| المدة | رقم | مدة الحركة بالثواني | 0.5 ثانية |
|
||||
| الحجم | رقم | حجم علامة الاختيار | 28 بكسل |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,40 +10,48 @@ image: /images/user-guide/github/github-header.png
|
||||
عنصر مرئي يمكن استخدامه كحاوية قابلة للنقر أو غير قابلة للنقر، مع علامة وعناصر اختيارية يسار ويمين، وخيارات تصميم متنوعة لعرض العلامات والبطاقات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Clickable Chip"
|
||||
clickable={true}
|
||||
variant="highlighted"
|
||||
accent="text-primary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
<Tab title="استخدام">
|
||||
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
|
||||
| linkToEntity | نص | الرابط إلى الكيان |
|
||||
| معرف الكيان | نص | المعرف الفريد للكيان |
|
||||
| الاسم | نص | اسم الكيان |
|
||||
| رابط الصورة | نص | s picture", |
|
||||
| نوع الصورة الرمزية | نوع الصورة الرمزية | نوع الصورة الرمزية التي تريد عرضها. لديه خياران: `مستدير` و `مربع` |
|
||||
| التنوع | تعداد EntityChipVariant | تنوع الرقاقة الكيانية التي ترغب في عرضها. لديه خياران: `عادي` و `شفاف` |
|
||||
| الأيقونة اليسرى | مكون رمز | مكون React يمثل رمزًا. يظهر على الجانب الأيسر من الرقاقة |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Chip
|
||||
size="large"
|
||||
label="Clickable Chip"
|
||||
clickable={true}
|
||||
variant="highlighted"
|
||||
accent="text-primary"
|
||||
leftComponent
|
||||
rightComponent
|
||||
maxWidth="200px"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
|
||||
| linkToEntity | نص | الرابط إلى الكيان |
|
||||
| معرف الكيان | نص | المعرف الفريد للكيان |
|
||||
| الاسم | نص | اسم الكيان |
|
||||
| رابط الصورة | نص | s picture", |
|
||||
| نوع الصورة الرمزية | نوع الصورة الرمزية | نوع الصورة الرمزية التي تريد عرضها. لديه خياران: `مستدير` و `مربع` |
|
||||
| التنوع | تعداد EntityChipVariant | تنوع الرقاقة الكيانية التي ترغب في عرضها. لديه خياران: `عادي` و `شفاف` |
|
||||
| الأيقونة اليسرى | مكون رمز | مكون React يمثل رمزًا. يظهر على الجانب الأيسر من الرقاقة |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الأمثلة
|
||||
@@ -100,39 +108,47 @@ export const MyComponent = () => {
|
||||
عنصر يشبه الرقاقة لعرض معلومات عن كيان.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Chip
|
||||
linkToEntity="/entity-link"
|
||||
entityId="entityTest"
|
||||
name="Entity name"
|
||||
pictureUrl=""
|
||||
avatarType="rounded"
|
||||
variant="regular"
|
||||
LeftIcon={IconTwentyStar}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
|
||||
| linkToEntity | نص | الرابط إلى الكيان |
|
||||
| معرف الكيان | نص | المعرف الفريد للكيان |
|
||||
| الاسم | نص | اسم الكيان |
|
||||
| رابط الصورة | نص | s picture", |
|
||||
| نوع الصورة الرمزية | نوع الصورة الرمزية | نوع الصورة الرمزية التي تريد عرضها. لديه خياران: `مستدير` و `مربع` |
|
||||
| التنوع | تعداد EntityChipVariant | تنوع الرقاقة الكيانية التي ترغب في عرضها. لديه خياران: `عادي` و `شفاف` |
|
||||
| الأيقونة اليسرى | مكون رمز | مكون React يمثل رمزًا. يظهر على الجانب الأيسر من الرقاقة |
|
||||
</Tab>
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
import { Chip } from 'twenty-ui/components';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Chip
|
||||
linkToEntity="/entity-link"
|
||||
entityId="entityTest"
|
||||
name="Entity name"
|
||||
pictureUrl=""
|
||||
avatarType="rounded"
|
||||
variant="regular"
|
||||
LeftIcon={IconTwentyStar}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------------ | ----------------------- | ---------------------------------------------------------------------- |
|
||||
| linkToEntity | نص | الرابط إلى الكيان |
|
||||
| معرف الكيان | نص | المعرف الفريد للكيان |
|
||||
| الاسم | نص | اسم الكيان |
|
||||
| رابط الصورة | نص | s picture", |
|
||||
| نوع الصورة الرمزية | نوع الصورة الرمزية | نوع الصورة الرمزية التي تريد عرضها. لديه خياران: `مستدير` و `مربع` |
|
||||
| التنوع | تعداد EntityChipVariant | تنوع الرقاقة الكيانية التي ترغب في عرضها. لديه خياران: `عادي` و `شفاف` |
|
||||
| الأيقونة اليسرى | مكون رمز | مكون React يمثل رمزًا. يظهر على الجانب الأيسر من الرقاقة |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -14,35 +14,44 @@ image: /images/user-guide/objects/objects.png
|
||||
نستخدم أيقونات Tabler لـ React في جميع أنحاء التطبيق.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="التثبيت">
|
||||
<br />
|
||||
|
||||
```
|
||||
yarn add @tabler/icons-react
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="التثبيت">
|
||||
<br />
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
يمكنك استيراد كل أيقونة كمكون. إليك مثال:
|
||||
```
|
||||
yarn add @tabler/icons-react
|
||||
```
|
||||
|
||||
<br />
|
||||
</Tab>
|
||||
|
||||
```jsx
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
<Tab title="الإزاحة">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconArrowLeft color="red" size={48} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
يمكنك استيراد كل أيقونة كمكون. إليك مثال:
|
||||
<br />
|
||||
|
||||
```jsx
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconArrowLeft color="red" size={48} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
|
||||
|
||||
| الإزاحة | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ------ | -------------------------------- | ----------------- |
|
||||
| الحجم | رقم | ارتفاع وعرض الأيقونة بالبكسل | 24 |
|
||||
| اللون | string | لون الأيقونات | اللون الحالي |
|
||||
| الخط العريض | رقم | عرض الخط العريض للأيقونة بالبكسل | 2 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
| الإزاحة | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ------ | -------------------------------- | ----------------- |
|
||||
| الحجم | رقم | ارتفاع وعرض الأيقونة بالبكسل | 24 |
|
||||
| اللون | string | لون الأيقونات | اللون الحالي |
|
||||
| الخط العريض | رقم | عرض الخط العريض للأيقونة بالبكسل | 2 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## أيقونات مخصصة
|
||||
@@ -54,20 +63,29 @@ image: /images/user-guide/objects/objects.png
|
||||
يعرض أيقونة دفتر العناوين.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconAddressBook } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconAddressBook size={24} stroke={2} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
```jsx
|
||||
import { IconAddressBook } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <IconAddressBook size={24} stroke={2} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----- | -------------------------------- | ----------------- |
|
||||
| الحجم | رقم | ارتفاع وعرض الأيقونة بالبكسل | 24 |
|
||||
| الخط العريض | رقم | عرض الخط العريض للأيقونة بالبكسل | 2 |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----- | -------------------------------- | ----------------- |
|
||||
| الحجم | رقم | ارتفاع وعرض الأيقونة بالبكسل | 24 |
|
||||
| الخط العريض | رقم | عرض الخط العريض للأيقونة بالبكسل | 2 |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,29 +10,38 @@ image: /images/user-guide/table-views/table.png
|
||||
مكوّن لتصنيف المحتوى أو وسمه بصريًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Tag } from "@/ui/display/tag/components/Tag";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Tag
|
||||
className
|
||||
color="red"
|
||||
text="Urgent"
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="استخدام">
|
||||
|
||||
```jsx
|
||||
import { Tag } from "@/ui/display/tag/components/Tag";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Tag
|
||||
className
|
||||
color="red"
|
||||
text="Urgent"
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ----- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| اللون | نص | لون العلامة. الخيارات تشمل: `أخضر`, `تركواز`, `سماوي`, `أزرق`, `أرجواني`, `وردي`, `أحمر`, `برتقالي`, `أصفر`, `رمادي`. |
|
||||
| نص | نص | محتوى العلامة |
|
||||
| عند_النقر | دالة | دالة اختيارية تُستدعى عند نقر المستخدم على العلامة |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ----- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| اللون | نص | لون العلامة. الخيارات تشمل: `أخضر`, `تركواز`, `سماوي`, `أزرق`, `أرجواني`, `وردي`, `أحمر`, `برتقالي`, `أصفر`, `رمادي`. |
|
||||
| نص | نص | محتوى العلامة |
|
||||
| عند_النقر | دالة | دالة اختيارية تُستدعى عند نقر المستخدم على العلامة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,22 +10,28 @@ image: /images/user-guide/api/api.png
|
||||
يستخدم محرر نصوص غني يعتمد على الكتل من [BlockNote](https://www.blocknotejs.org/) للسماح للمستخدمين بتحرير وعرض كتل المحتوى.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { useBlockNote } from "@blocknote/react";
|
||||
import { BlockEditor } from "@/ui/input/editor/components/BlockEditor";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const BlockNoteEditor = useBlockNote();
|
||||
```jsx
|
||||
import { useBlockNote } from "@blocknote/react";
|
||||
import { BlockEditor } from "@/ui/input/editor/components/BlockEditor";
|
||||
|
||||
return <BlockEditor editor={BlockNoteEditor} />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const BlockNoteEditor = useBlockNote();
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------- | ----------------- | ------------------------ |
|
||||
| محرر | `BlockNoteEditor` | مثيل أو تكوين محرر الكتل |
|
||||
</Tab>
|
||||
return <BlockEditor editor={BlockNoteEditor} />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------- | ----------------- | ------------------------ |
|
||||
| محرر | `BlockNoteEditor` | مثيل أو تكوين محرر الكتل |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -12,428 +12,511 @@ image: /images/user-guide/views/filter.png
|
||||
## زر
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Title"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="default"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="27332A2E2F2745">
|
||||
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Title"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="default"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون رمز اختياري يُعرض داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| عرض كامل | قيمة منطقية | يُحدد إذا كان الزر يجب أن يمتد ليغطي العرض الكامل للحاوية الخاصة به |
|
||||
| التنوع | string | النمط المرئي للزر. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `default`، `blue`، `danger` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون رمز اختياري يُعرض داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| عرض كامل | قيمة منطقية | يُحدد إذا كان الزر يجب أن يمتد ليغطي العرض الكامل للحاوية الخاصة به |
|
||||
| التنوع | string | النمط المرئي للزر. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `default`، `blue`، `danger` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| معطل | قيمة منطقية | يحدد إذا كان الزر معطل أم لا |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة الأزرار
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
import { ButtonGroup } from "@/ui/input/button/components/ButtonGroup";
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Button } from "@/ui/input/button/components/Button";
|
||||
import { ButtonGroup } from "@/ui/input/button/components/ButtonGroup";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ButtonGroup variant="primary" size="large" accent="blue" className>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 1"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 2"
|
||||
fullWidth={false}
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
position="left"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 3"
|
||||
fullWidth={false}
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
position="right"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ButtonGroup variant="primary" size="large" accent="blue" className>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 1"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 2"
|
||||
fullWidth={false}
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
position="left"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
<Button
|
||||
className
|
||||
Icon={null}
|
||||
title="Button 3"
|
||||
fullWidth={false}
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
position="right"
|
||||
accent="blue"
|
||||
soon={false}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | --------- | -------------------------------------------------------------------------------------- |
|
||||
| التنوع | string | النمط المرئي للأزرار داخل المجموعة. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
|
||||
| الحجم | string | حجم الأزرار داخل المجموعة. يوجد خياران: `medium` و`small`. |
|
||||
| accent | نص | لون تمييز الأزرار داخل المجموعة. تشمل الخيارات `default`، `blue` و`danger`. |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة |
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | --------- | -------------------------------------------------------------------------------------- |
|
||||
| التنوع | string | النمط المرئي للأزرار داخل المجموعة. تشمل الخيارات `primary`، `secondary`، و`tertiary`. |
|
||||
| الحجم | string | حجم الأزرار داخل المجموعة. يوجد خياران: `medium` و`small`. |
|
||||
| accent | نص | لون تمييز الأزرار داخل المجموعة. تشمل الخيارات `default`، `blue` و`danger`. |
|
||||
| className | string | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر عائم
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
title="Title"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | --------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `middle`، `right`. |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
title="Title"
|
||||
size="medium"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | --------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | string | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `middle`، `right`. |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة الأزرار العائمة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { FloatingButtonGroup } from "@/ui/input/button/components/FloatingButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButtonGroup size="small">
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconClipboardText}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconCheckbox}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
/>
|
||||
</FloatingButtonGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { FloatingButton } from "@/ui/input/button/components/FloatingButton";
|
||||
import { FloatingButtonGroup } from "@/ui/input/button/components/FloatingButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingButtonGroup size="small">
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconClipboardText}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
/>
|
||||
<FloatingButton
|
||||
className
|
||||
Icon={IconCheckbox}
|
||||
title
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
/>
|
||||
</FloatingButtonGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ------- | --------- | -------------------------------------------------------- | ----------------- |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` | صغير |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة | |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ------- | --------- | -------------------------------------------------------- | ----------------- |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` | صغير |
|
||||
| الأبناء | ReactNode | مجموعة من عناصر React تمثل الأزرار الفردية داخل المجموعة | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر رمز عائم
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingIconButton } from "@/ui/input/button/components/FloatingIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingIconButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
isActive={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { FloatingIconButton } from "@/ui/input/button/components/FloatingIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<FloatingIconButton
|
||||
className
|
||||
Icon={IconSearch}
|
||||
size="small"
|
||||
position="standalone"
|
||||
applyShadow={true}
|
||||
applyBlur={true}
|
||||
disabled={false}
|
||||
focus={false}
|
||||
onClick={() => console.log("click")}
|
||||
isActive={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | نص | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| className | نص | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| الحجم | نص | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| الموقع | نص | موقع الزر بالنسبة لأخوته. تشمل الخيارات: `standalone`، `left`، `right`، و`middle`. |
|
||||
| تطبيق الظل | قيمة منطقية | يحدد إذا ما سيتم تطبيق الظلال على الزر |
|
||||
| تطبيق الضباب | قيمة منطقية | يحدد ما إذا كان ينبغي تطبيق تأثير الضباب على الزر |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
| فعّال | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة أزرار الرموز العائمة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { FloatingIconButtonGroup } from "@/ui/input/button/components/FloatingIconButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const iconButtons = [
|
||||
{
|
||||
Icon: IconClipboardText,
|
||||
onClick: () => console.log("Button 1 clicked"),
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => console.log("Button 2 clicked"),
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
```jsx
|
||||
import { FloatingIconButtonGroup } from "@/ui/input/button/components/FloatingIconButtonGroup";
|
||||
import { IconClipboardText, IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
return (
|
||||
<FloatingIconButtonGroup
|
||||
className
|
||||
size="small"
|
||||
iconButtons={iconButtons} />
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const iconButtons = [
|
||||
{
|
||||
Icon: IconClipboardText,
|
||||
onClick: () => console.log("Button 1 clicked"),
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => console.log("Button 2 clicked"),
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<FloatingIconButtonGroup
|
||||
className
|
||||
size="small"
|
||||
iconButtons={iconButtons} />
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| أزرار الرموز | array | مجموعة من الكائنات، يمثل كل منها زر رمز في المجموعة. يجب أن يشمل كل كائن مكون الرمز الذي تريد عرضه في الزر، الوظيفة التي ترغب في استدعائها عند نقر المستخدم على الزر، وما إذا كان الزر ينبغي أن يكون نشطًا أم لا. |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| أزرار الرموز | array | مجموعة من الكائنات، يمثل كل منها زر رمز في المجموعة. يجب أن يشمل كل كائن مكون الرمز الذي تريد عرضه في الزر، الوظيفة التي ترغب في استدعائها عند نقر المستخدم على الزر، وما إذا كان الزر ينبغي أن يكون نشطًا أم لا. |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر خفيف
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { LightButton } from "@/ui/input/button/components/LightButton";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <LightButton
|
||||
className
|
||||
icon={null}
|
||||
title="Title"
|
||||
accent="secondary"
|
||||
active={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={()=>console.log('click')}
|
||||
/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { LightButton } from "@/ui/input/button/components/LightButton";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <LightButton
|
||||
className
|
||||
icon={null}
|
||||
title="Title"
|
||||
accent="secondary"
|
||||
active={false}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={()=>console.log('click')}
|
||||
/>;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
|
||||
|
||||
| الإزاحة | النوع | الوصف |
|
||||
| --------- | ----------------- | -------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ReactNode` | الرمز الذي تريد عرضه في الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. لون الزر المميز تشمل الخيارات: `ثانوي` و `ثالثي` |
|
||||
| نشط | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الإزاحة">
|
||||
| الإزاحة | النوع | الوصف |
|
||||
| --------- | ----------------- | -------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| أيقونة | `React.ReactNode` | الرمز الذي تريد عرضه في الزر |
|
||||
| العنوان | string | محتوى نص الزر |
|
||||
| accent | string | موقع الزر بالنسبة لأخوته. لون الزر المميز تشمل الخيارات: `ثانوي` و `ثالثي` |
|
||||
| نشط | قيمة منطقية | يحدد إذا كان الزر في وضع فعّال |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطل |
|
||||
| تركيز | قيمة منطقية | يحدد إذا كان الزر في وضع التركيز |
|
||||
| عند النقر | وظيفة | وظيفة رد فعل تنطلق عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر أيقونة خفيف
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { LightIconButton } from "@/ui/input/button/components/LightIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<LightIconButton
|
||||
className
|
||||
testId="test1"
|
||||
Icon={IconSearch}
|
||||
title="Title"
|
||||
size="small"
|
||||
accent="secondary"
|
||||
active={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { LightIconButton } from "@/ui/input/button/components/LightIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<LightIconButton
|
||||
className
|
||||
testId="test1"
|
||||
Icon={IconSearch}
|
||||
title="Title"
|
||||
size="small"
|
||||
accent="secondary"
|
||||
active={true}
|
||||
disabled={false}
|
||||
focus={true}
|
||||
onClick={() => console.log("click")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
|
||||
|
||||
| العناصر | النوع | الوصف |
|
||||
| --------- | --------------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| testId | string | معرف اختبار للزر |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نصي للزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| accent | string | لون الزر المميز تشمل الخيارات: `ثانوي` و `ثالثي` |
|
||||
| نشط | قيمة منطقية | يحدد ما إذا كان الزر في حالة نشطة |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطلاً |
|
||||
| التركيز | قيمة منطقية | يشير إلى ما إذا كان الزر لديه تركيز |
|
||||
| عند النقر | function | وظيفة رد اتصال تتفعّل عند نقر المستخدم على الزر |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| --------- | --------------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري لتنسيقات إضافية |
|
||||
| testId | string | معرف اختبار للزر |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| العنوان | string | محتوى نصي للزر |
|
||||
| الحجم | string | حجم الزر. يوجد خياران: `صغير` و `متوسط` |
|
||||
| accent | string | لون الزر المميز تشمل الخيارات: `ثانوي` و `ثالثي` |
|
||||
| نشط | قيمة منطقية | يحدد ما إذا كان الزر في حالة نشطة |
|
||||
| معطل | قيمة منطقية | يحدد ما إذا كان الزر معطلاً |
|
||||
| التركيز | قيمة منطقية | يشير إلى ما إذا كان الزر لديه تركيز |
|
||||
| عند النقر | function | وظيفة رد اتصال تتفعّل عند نقر المستخدم على الزر |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الزر الرئيسي
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MainButton } from "@/ui/input/button/components/MainButton";
|
||||
import { IconCheckbox } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<MainButton
|
||||
title="Checkbox"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
soon={false}
|
||||
Icon={IconCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { MainButton } from "@/ui/input/button/components/MainButton";
|
||||
import { IconCheckbox } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<MainButton
|
||||
title="Checkbox"
|
||||
fullWidth={false}
|
||||
variant="primary"
|
||||
soon={false}
|
||||
Icon={IconCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
|
||||
|
||||
| العناصر | النوع | الوصف |
|
||||
| -------------- | -------------------------------- | -------------------------------------------------------------- |
|
||||
| العنوان | string | محتوى نصي للزر |
|
||||
| عرض كامل | قيمة منطقية | يحدد ما إذا كان الزر يجب أن يمتد على كامل عرض الحاوية |
|
||||
| التنوع | string | النمط البصري للزر. تشمل الخيارات `أساسي` و `ثانوي` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| خصائص زر React | `React.ComponentProps<'button'>` | كل خصائص زر HTML القياسية مدعومة |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| -------------- | -------------------------------- | -------------------------------------------------------------- |
|
||||
| العنوان | string | محتوى نصي للزر |
|
||||
| عرض كامل | قيمة منطقية | يحدد ما إذا كان الزر يجب أن يمتد على كامل عرض الحاوية |
|
||||
| التنوع | string | النمط البصري للزر. تشمل الخيارات `أساسي` و `ثانوي` |
|
||||
| قريباً | قيمة منطقية | يشير إلى ما إذا كان الزر معلمًا "قريبًا" (مثل الميزات القادمة) |
|
||||
| أيقونة | `React.ComponentType` | مكون أيقونة اختياري يظهر داخل الزر |
|
||||
| خصائص زر React | `React.ComponentProps<'button'>` | كل خصائص زر HTML القياسية مدعومة |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## زر أيقونة دائري
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RoundedIconButton } from "@/ui/input/button/components/RoundedIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RoundedIconButton
|
||||
Icon={IconSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { RoundedIconButton } from "@/ui/input/button/components/RoundedIconButton";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RoundedIconButton
|
||||
Icon={IconSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
|
||||
|
||||
| العناصر | النوع | الوصف |
|
||||
| -------------- | ----------------------------------------------- | ----- |
|
||||
| أيقونة | `React.ComponentType` | |
|
||||
| خصائص زر React | `React.ButtonHTMLAttributes<HTMLButtonElement>` | |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| -------------- | ----------------------------------------------- | ----- |
|
||||
| أيقونة | `React.ComponentType` | |
|
||||
| خصائص زر React | `React.ButtonHTMLAttributes<HTMLButtonElement>` | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,35 +10,41 @@ image: /images/user-guide/tasks/tasks_header.png
|
||||
يُستخدم عندما يحتاج المستخدم إلى اختيار قيم متعددة من بين عدة خيارات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Checkbox } from "twenty-ui/display";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={true}
|
||||
indeterminate={false}
|
||||
onChange={() => console.log("onChange function fired")}
|
||||
onCheckedChange={() => console.log("onCheckedChange function fired")}
|
||||
variant="primary"
|
||||
size="small"
|
||||
shape="squared"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Checkbox } from "twenty-ui/display";
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------- | ----------------------------------------------------------------------------- |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار محددًا |
|
||||
| غير محدد | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار في حالة غير محددة (لا هو محدد ولا غير محدد) |
|
||||
| عند التغيير | دالة | الدالة التي ترغب في تفعيلها عند تغيير حالة مربع الاختيار |
|
||||
| عند تغيير الحالة المحددة | دالة | الدالة التي ترغب في تفعيلها عند تغيّر حالة `checked` |
|
||||
| نموذج | نص | النمط البصري للصندوق. تتضمن الخيارات: 'أساسي'، 'ثانوي'، و 'ثالثي' |
|
||||
| الحجم | نص | حجم مربع الاختيار. له خياران: `small` و `large` |
|
||||
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={true}
|
||||
indeterminate={false}
|
||||
onChange={() => console.log("onChange function fired")}
|
||||
onCheckedChange={() => console.log("onCheckedChange function fired")}
|
||||
variant="primary"
|
||||
size="small"
|
||||
shape="squared"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------- | ----------------------------------------------------------------------------- |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار محددًا |
|
||||
| غير محدد | قيمة منطقية | يشير إلى ما إذا كان مربع الاختيار في حالة غير محددة (لا هو محدد ولا غير محدد) |
|
||||
| عند التغيير | دالة | الدالة التي ترغب في تفعيلها عند تغيير حالة مربع الاختيار |
|
||||
| عند تغيير الحالة المحددة | دالة | الدالة التي ترغب في تفعيلها عند تغيّر حالة `checked` |
|
||||
| نموذج | نص | النمط البصري للصندوق. تتضمن الخيارات: 'أساسي'، 'ثانوي'، و 'ثالثي' |
|
||||
| الحجم | نص | حجم مربع الاختيار. له خياران: `small` و `large` |
|
||||
| الشكل | نص | شكل مربع الاختيار. لديه خياران: 'مربع' و 'مدور' |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -12,28 +12,36 @@ image: /images/user-guide/fields/field.png
|
||||
يمثل مخططات ألوان مختلفة ومخصص بشكل خاص للمواضيع الفاتحة والداكنة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { ColorSchemeCard } from "twenty-ui/display";
|
||||
<Tab title="27332A2E2F2745">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ColorSchemeCard
|
||||
variant="Dark"
|
||||
selected={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ColorSchemeCard } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ColorSchemeCard
|
||||
variant="Dark"
|
||||
selected={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ------------ | --------------------------------------- | ---------------------------------------------------------------------- | ----------------- |
|
||||
| التنوع | string | نوع مخطط الألوان. تشمل الخيارات `داكنة`, `فاتحة`, و `النظام` | فاتح |
|
||||
| المحدد | قيمة منطقية | إذا كان `صحيح`, يتم عرض علامة الاختيار للدلالة على مخطط الألوان المحدد | |
|
||||
| خصائص إضافية | `React.ComponentPropsWithoutRef<'div'>` | خصائص عنصر `div` العادي في HTML | |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ------------ | --------------------------------------- | ---------------------------------------------------------------------- | ----------------- |
|
||||
| التنوع | string | نوع مخطط الألوان. تشمل الخيارات `داكنة`, `فاتحة`, و `النظام` | فاتح |
|
||||
| المحدد | قيمة منطقية | إذا كان `صحيح`, يتم عرض علامة الاختيار للدلالة على مخطط الألوان المحدد | |
|
||||
| خصائص إضافية | `React.ComponentPropsWithoutRef<'div'>` | خصائص عنصر `div` العادي في HTML | |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## منتقي مخطط الألوان
|
||||
@@ -41,23 +49,31 @@ image: /images/user-guide/fields/field.png
|
||||
يتيح للمستخدمين اختيار بين مخططات الألوان المختلفة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { ColorSchemePicker } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ColorSchemePicker
|
||||
value="Dark"
|
||||
onChange
|
||||
/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ColorSchemePicker } from "twenty-ui/display";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ColorSchemePicker
|
||||
value="Dark"
|
||||
onChange
|
||||
/>;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| ----------- | ------------------- | ------------------------------------------------------------- |
|
||||
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
|
||||
| عند التغيير | function | الدالة التي ترغب في تفعيلها عندما يختار المستخدم نظام الألوان |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ----------- | ------------------- | ------------------------------------------------------------- |
|
||||
| القيمة | `طريقة عرض الألوان` | مخطط الألوان المحدد حاليًا |
|
||||
| عند التغيير | function | الدالة التي ترغب في تفعيلها عندما يختار المستخدم نظام الألوان |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,43 +10,49 @@ image: /images/user-guide/github/github-header.png
|
||||
منتقى الأيقونات المعتمد على القائمة المنسدلة الذي يتيح للمستخدمين اختيار أيقونة من قائمة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import React, { useState } from "react";
|
||||
import { IconPicker } from "@/ui/input/components/IconPicker";
|
||||
|
||||
export const MyComponent = () => {
|
||||
<Tab title="استخدام">
|
||||
|
||||
const [selectedIcon, setSelectedIcon] = useState("");
|
||||
const handleIconChange = ({ iconKey, Icon }) => {
|
||||
console.log("Selected Icon:", iconKey);
|
||||
setSelectedIcon(iconKey);
|
||||
};
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { IconPicker } from "@/ui/input/components/IconPicker";
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<IconPicker
|
||||
disabled={false}
|
||||
onChange={handleIconChange}
|
||||
selectedIconKey={selectedIcon}
|
||||
variant="primary"
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ----------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| معطل | قيمة منطقية | يقوم بتعطيل منتقى الأيقونات إذا تم تعيينه إلى `true` |
|
||||
| عند التغيير | دالة | الدالة الارتجاعية التي تُفعل عندما يختار المستخدم أيقونة. يستقبل كائنًا يحتوي على الخصائص `iconKey` و `Icon` |
|
||||
| مفتاح الأيقونة المختارة | نص | مفتاح الأيقونة المختارة في البداية |
|
||||
| النقر بالخارج | دالة | الدالة الارتجاعية التي تُفعل عندما ينقر المستخدم خارج القائمة المنسدلة |
|
||||
| عند الإغلاق | دالة | الدالة الارتجاعية التي تُفعل عند إغلاق القائمة المنسدلة |
|
||||
| عند الفتح | دالة | الدالة الارتجاعية التي تُفعل عند فتح القائمة المنسدلة |
|
||||
| التنوع | نص | متغير النمط البصري للأيقونة القابلة للنقر. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
|
||||
</Tab>
|
||||
const [selectedIcon, setSelectedIcon] = useState("");
|
||||
const handleIconChange = ({ iconKey, Icon }) => {
|
||||
console.log("Selected Icon:", iconKey);
|
||||
setSelectedIcon(iconKey);
|
||||
};
|
||||
|
||||
return (
|
||||
<IconPicker
|
||||
disabled={false}
|
||||
onChange={handleIconChange}
|
||||
selectedIconKey={selectedIcon}
|
||||
variant="primary"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ----------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| معطل | قيمة منطقية | يقوم بتعطيل منتقى الأيقونات إذا تم تعيينه إلى `true` |
|
||||
| عند التغيير | دالة | الدالة الارتجاعية التي تُفعل عندما يختار المستخدم أيقونة. يستقبل كائنًا يحتوي على الخصائص `iconKey` و `Icon` |
|
||||
| مفتاح الأيقونة المختارة | نص | مفتاح الأيقونة المختارة في البداية |
|
||||
| النقر بالخارج | دالة | الدالة الارتجاعية التي تُفعل عندما ينقر المستخدم خارج القائمة المنسدلة |
|
||||
| عند الإغلاق | دالة | الدالة الارتجاعية التي تُفعل عند إغلاق القائمة المنسدلة |
|
||||
| عند الفتح | دالة | الدالة الارتجاعية التي تُفعل عند فتح القائمة المنسدلة |
|
||||
| التنوع | نص | متغير النمط البصري للأيقونة القابلة للنقر. تشمل الخيارات: `رئيسي`, `ثانوي`, و `ثالثي` |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,25 +10,32 @@ image: /images/user-guide/objects/objects.png
|
||||
4A4F33452D 44445245332A2E2F454A46 28452F 482532274429 35483129.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { ImageInput } from "@/ui/input/components/ImageInput";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <ImageInput/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ImageInput } from "@/ui/input/components/ImageInput";
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------ | ----------- | --------------------------------------------------------------------------------- |
|
||||
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
|
||||
| onUpload | دالة | الدالة التي تُستدعى عند قيام المستخدم بتحميل صورة جديدة. تستقبل كائن `File` كوسيط |
|
||||
| onRemove | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإزالة |
|
||||
| onAbort | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإلغاء أثناء تحميل الصورة |
|
||||
| isUploading | قيمة منطقية | يشير إلى ما إذا كان يتم تحميل صورة حاليًا |
|
||||
| errorMessage | نص | رسالة خطأ اختيارية لعرضها أسفل حقل إدخال الصورة |
|
||||
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون حقل الإدخال بأكمله معطلاً، ولن تكون الأزرار قابلة للنقر |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return <ImageInput/>;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------ | ----------- | --------------------------------------------------------------------------------- |
|
||||
| صورة | نص | 3946482746 45352F31 274435483129 27442544432A3148464A |
|
||||
| onUpload | دالة | الدالة التي تُستدعى عند قيام المستخدم بتحميل صورة جديدة. تستقبل كائن `File` كوسيط |
|
||||
| onRemove | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإزالة |
|
||||
| onAbort | دالة | الدالة التي تُستدعى عند نقر المستخدم على زر الإلغاء أثناء تحميل الصورة |
|
||||
| isUploading | قيمة منطقية | يشير إلى ما إذا كان يتم تحميل صورة حاليًا |
|
||||
| errorMessage | نص | رسالة خطأ اختيارية لعرضها أسفل حقل إدخال الصورة |
|
||||
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون حقل الإدخال بأكمله معطلاً، ولن تكون الأزرار قابلة للنقر |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,50 +10,58 @@ image: /images/user-guide/create-workspace/workspace-cover.png
|
||||
تستخدم عندما يمكن للمستخدمين اختيار خيار واحد فقط من سلسلة من الخيارات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { Radio } from "twenty-ui/display";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import { Radio } from "twenty-ui/display";
|
||||
|
||||
const handleRadioChange = (event) => {
|
||||
console.log("Radio button changed:", event.target.checked);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
|
||||
const handleCheckedChange = (checked) => {
|
||||
console.log("Checked state changed:", checked);
|
||||
};
|
||||
const handleRadioChange = (event) => {
|
||||
console.log("Radio button changed:", event.target.checked);
|
||||
};
|
||||
|
||||
const handleCheckedChange = (checked) => {
|
||||
console.log("Checked state changed:", checked);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Radio
|
||||
checked={true}
|
||||
value="Option 1"
|
||||
onChange={handleRadioChange}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
size="large"
|
||||
disabled={false}
|
||||
labelPosition="right"
|
||||
/>
|
||||
);
|
||||
};
|
||||
},{
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<Radio
|
||||
checked={true}
|
||||
value="Option 1"
|
||||
onChange={handleRadioChange}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
size="large"
|
||||
disabled={false}
|
||||
labelPosition="right"
|
||||
/>
|
||||
);
|
||||
};
|
||||
},{
|
||||
```
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------------- | ------------------------------------------------------------------ |
|
||||
| النمط | خصائص `React.CSS` | أنماط إضافية مضمنة للمكون |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية لتصميم إضافي |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان زر الراديو محددًا |
|
||||
| القيمة | نص | التسمية أو النص المرتبط بزر الراديو |
|
||||
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير زر الاختيار المحدد. |
|
||||
| عند تغيير الحالة المحددة | دالة | الدالة التي تُستدعى عند تغيير حالة `checked` لزر الاختيار. |
|
||||
| الحجم | نص | حجم زر الراديو. تشمل الخيارات: `large` و `small` |
|
||||
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون زر الاختيار معطلاً وغير قابل للنقر |
|
||||
| موضع التسمية | نص | موضع نص التسمية بالنسبة لزر الراديو. يوجد خياران: `left` و `right` |
|
||||
</Tab>
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------------------ | ----------------- | ------------------------------------------------------------------ |
|
||||
| النمط | خصائص `React.CSS` | أنماط إضافية مضمنة للمكون |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية لتصميم إضافي |
|
||||
| مختار | قيمة منطقية | يشير إلى ما إذا كان زر الراديو محددًا |
|
||||
| القيمة | نص | التسمية أو النص المرتبط بزر الراديو |
|
||||
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير زر الاختيار المحدد. |
|
||||
| عند تغيير الحالة المحددة | دالة | الدالة التي تُستدعى عند تغيير حالة `checked` لزر الاختيار. |
|
||||
| الحجم | نص | حجم زر الراديو. تشمل الخيارات: `large` و `small` |
|
||||
| معطل | قيمة منطقية | إذا كانت `true`، فسيكون زر الاختيار معطلاً وغير قابل للنقر |
|
||||
| موضع التسمية | نص | موضع نص التسمية بالنسبة لزر الراديو. يوجد خياران: `left` و `right` |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مجموعة الراديو
|
||||
@@ -61,37 +69,45 @@ image: /images/user-guide/create-workspace/workspace-cover.png
|
||||
يجمع أزرار الراديو ذات الصلة معًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { Radio, RadioGroup } from "twenty-ui/display";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import React, { useState } from "react";
|
||||
import { Radio, RadioGroup } from "twenty-ui/display";
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState("Option 1");
|
||||
export const MyComponent = () => {
|
||||
|
||||
const handleChange = (event) => {
|
||||
setSelectedValue(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<RadioGroup value={selectedValue} onChange={handleChange}>
|
||||
<Radio value="Option 1" />
|
||||
<Radio value="Option 2" />
|
||||
<Radio value="Option 3" />
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
const [selectedValue, setSelectedValue] = useState("Option 1");
|
||||
|
||||
```
|
||||
</Tab>
|
||||
const handleChange = (event) => {
|
||||
setSelectedValue(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<RadioGroup value={selectedValue} onChange={handleChange}>
|
||||
<Radio value="Option 1" />
|
||||
<Radio value="Option 2" />
|
||||
<Radio value="Option 3" />
|
||||
</RadioGroup>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------- | ----------------- | --------------------------------------------------------------------- |
|
||||
| القيمة | string | قيمة زر الراديو المحدد حاليًا |
|
||||
| عند التغيير | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير زر الاختيار. |
|
||||
| onValueChange | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير القيمة المحددة في المجموعة. |
|
||||
| الأبناء | `React.ReactNode` | يتيح لك تمرير مكوّنات React (مثل Radio) كعناصر فرعية إلى Radio Group |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| ------------- | ----------------- | --------------------------------------------------------------------- |
|
||||
| القيمة | string | قيمة زر الراديو المحدد حاليًا |
|
||||
| عند التغيير | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير زر الاختيار. |
|
||||
| onValueChange | دالة | دالة الاستدعاء التي يتم تشغيلها عند تغيير القيمة المحددة في المجموعة. |
|
||||
| الأبناء | `React.ReactNode` | يتيح لك تمرير مكوّنات React (مثل Radio) كعناصر فرعية إلى Radio Group |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,42 +10,46 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
يتيح للمستخدمين اختيار قيمة من قائمة من الخيارات المحددة مسبقًا.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
<Tab title="استخدام">
|
||||
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
```jsx
|
||||
import { IconTwentyStar } from 'twenty-ui/display';
|
||||
|
||||
export const MyComponent = () => {
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<Select
|
||||
className
|
||||
disabled={false}
|
||||
label="Select an option"
|
||||
options={[
|
||||
{ value: 'option1', label: 'Option A', Icon: IconTwentyStar },
|
||||
{ value: 'option2', label: 'Option B', Icon: IconTwentyStar },
|
||||
]}
|
||||
value="option1"
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<Select
|
||||
className
|
||||
disabled={false}
|
||||
label="Select an option"
|
||||
options={[
|
||||
{ value: 'option1', label: 'Option A', Icon: IconTwentyStar },
|
||||
{ value: 'option2', label: 'Option B', Icon: IconTwentyStar },
|
||||
]}
|
||||
value="option1"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
|
||||
| معطل | قيمة منطقية | عند ضبطها على `true`، يتم تعطيل تفاعل المستخدم مع المكون |
|
||||
| التسمية | نص | التسمية التي تصف غرض مكوّن `Select` |
|
||||
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير القيم المحددة |
|
||||
| خيارات | مصفوفة | تمثل الخيارات المتاحة في مكون `الاختيار`. إنها مصفوفة من الكائنات حيث يحتوي كل كائن على `قيمة` (معرف فريد)، `تسمية` (معرف فريد)، و`أيقونة` اختيارية |
|
||||
| القيمة | نص | تمثل القيمة المحددة حاليًا. يجب أن تطابق إحدى خصائص `القيمة` في مصفوفة `الخيارات` |
|
||||
</Tab>
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | فئة CSS اختيارية للتنسيق الإضافي |
|
||||
| معطل | قيمة منطقية | عند ضبطها على `true`، يتم تعطيل تفاعل المستخدم مع المكون |
|
||||
| التسمية | نص | التسمية التي تصف غرض مكوّن `Select` |
|
||||
| عند التغيير | دالة | الدالة التي تُستدعى عند تغيير القيم المحددة |
|
||||
| خيارات | مصفوفة | تمثل الخيارات المتاحة في مكون `الاختيار`. إنها مصفوفة من الكائنات حيث يحتوي كل كائن على `قيمة` (معرف فريد)، `تسمية` (معرف فريد)، و`أيقونة` اختيارية |
|
||||
| القيمة | نص | تمثل القيمة المحددة حاليًا. يجب أن تطابق إحدى خصائص `القيمة` في مصفوفة `الخيارات` |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -12,53 +12,61 @@ image: /images/user-guide/notes/notes_header.png
|
||||
يسمح للمستخدمين بإدخال وتحرير النص.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { TextInput } from "@/ui/input/components/TextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("تم تغيير الإدخال:", text);
|
||||
};
|
||||
<Tab title="27332A2E2F2745">
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("تم ضغط المفتاح:", event.key);
|
||||
};
|
||||
```jsx
|
||||
import { TextInput } from "@/ui/input/components/TextInput";
|
||||
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<TextInput
|
||||
className
|
||||
label="اسم المستخدم"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="اسم مستخدم غير صالح"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
},{
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleChange = (text) => {
|
||||
console.log("Input changed:", text);
|
||||
};
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري للتنسيق الإضافي. |
|
||||
| التسمية | نص | يمثل التسمية للإدخال. |
|
||||
| onChange | وظيفة | الدالة التي تُستدعى عند تغيير قيمة الإدخال. |
|
||||
| عرض كامل | قيمة منطقية | يشير إلى ما إذا كان الإدخال يجب أن يشغل 100% من العرض. |
|
||||
| تعطيل الإختصارات | قيمة منطقية | يشير إلى ما إذا كانت الاختصارات ممكنة للإدخال. |
|
||||
| خطأ | string | يمثل رسالة الخطأ التي سيتم عرضها. عند توفرها، تضيف رمز خطأ على الجانب الأيمن من الإدخال. |
|
||||
| onKeyDown | دالة | يتم الاستدعاء عندما يتم الضغط على مفتاح عند التركيز على حقل الإدخال. يتلقى `React.KeyboardEvent` كمعلمة |
|
||||
| أيقونة يمين | مكون الأيقونة | مكون أيقونة اختياري معروض على الجانب الأيمن من الإدخال. |
|
||||
const handleKeyDown = (event) => {
|
||||
console.log("Key pressed:", event.key);
|
||||
};
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
className
|
||||
label="Username"
|
||||
onChange={handleChange}
|
||||
fullWidth={false}
|
||||
disableHotkeys={false}
|
||||
error="Invalid username"
|
||||
onKeyDown={handleKeyDown}
|
||||
RightIcon={null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| className | string | اسم اختياري للتنسيق الإضافي. |
|
||||
| التسمية | نص | يمثل التسمية للإدخال. |
|
||||
| onChange | وظيفة | الدالة التي تُستدعى عند تغيير قيمة الإدخال. |
|
||||
| عرض كامل | قيمة منطقية | يشير إلى ما إذا كان الإدخال يجب أن يشغل 100% من العرض. |
|
||||
| تعطيل الإختصارات | قيمة منطقية | يشير إلى ما إذا كانت الاختصارات ممكنة للإدخال. |
|
||||
| خطأ | string | يمثل رسالة الخطأ التي سيتم عرضها. عند توفرها، تضيف رمز خطأ على الجانب الأيمن من الإدخال. |
|
||||
| onKeyDown | دالة | يتم الاستدعاء عندما يتم الضغط على مفتاح عند التركيز على حقل الإدخال. يتلقى `React.KeyboardEvent` كمعلمة |
|
||||
| أيقونة يمين | مكون الأيقونة | مكون أيقونة اختياري معروض على الجانب الأيمن من الإدخال. |
|
||||
|
||||
|
||||
|
||||
|
||||
يقبل المكون أيضًا دعم خصائص HTML أخرى لعناصر الإدخال.
|
||||
|
||||
</Tab>
|
||||
|
||||
يقبل المكون أيضًا دعم خصائص HTML أخرى لعناصر الإدخال.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## إدخال نص بالحجم التلقائي
|
||||
@@ -66,40 +74,48 @@ image: /images/user-guide/notes/notes_header.png
|
||||
مكون إدخال نصي يعدل ارتفاعه تلقائيًا بناءً على المحتوى.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { AutosizeTextInput } from "@/ui/input/components/AutosizeTextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("تم تشغيل الدالة onValidate")}
|
||||
minRows={1}
|
||||
placeholder="اكتب تعليقًا"
|
||||
onFocus={() => console.log("تم تشغيل الدالة onFocus")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="المهمة: "
|
||||
/>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};},{
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
```jsx
|
||||
import { AutosizeTextInput } from "@/ui/input/components/AutosizeTextInput";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<AutosizeTextInput
|
||||
onValidate={() => console.log("onValidate function fired")}
|
||||
minRows={1}
|
||||
placeholder="Write a comment"
|
||||
onFocus={() => console.log("onFocus function fired")}
|
||||
variant="icon"
|
||||
buttonTitle
|
||||
value="Task: "
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| ------------------ | ----- | ------------------------------------------------------------- |
|
||||
| onValidate | دالة | الدالة التي ترغب في تفعيلها عند تصديق المستخدم الإدخال. |
|
||||
| الحد الأدنى للأسطر | رقم | عدد الأسطر الأدنى للمساحة النصية. |
|
||||
| النص التوضيحي | نص | النص التوضيحي الذي ترغب في عرضه عند كون المساحة النصية فارغة. |
|
||||
| onFocus | دالة | الدالة التي ترغب في تفعيلها عند تركيز المساحة النصية. |
|
||||
| البديل | نص | البديل للإدخال. تشمل الخيارات: `افتراضي`، `أيقونة`، و`زر`. |
|
||||
| عنوان الزر | نص | العنوان للزر (فقط للبديل الزر). |
|
||||
| القيمة | نص | القيمة الأولية للمساحة النصية. |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ------------------ | ----- | ------------------------------------------------------------- |
|
||||
| onValidate | دالة | الدالة التي ترغب في تفعيلها عند تصديق المستخدم الإدخال. |
|
||||
| الحد الأدنى للأسطر | رقم | عدد الأسطر الأدنى للمساحة النصية. |
|
||||
| النص التوضيحي | نص | النص التوضيحي الذي ترغب في عرضه عند كون المساحة النصية فارغة. |
|
||||
| onFocus | دالة | الدالة التي ترغب في تفعيلها عند تركيز المساحة النصية. |
|
||||
| البديل | نص | البديل للإدخال. تشمل الخيارات: `افتراضي`، `أيقونة`، و`زر`. |
|
||||
| عنوان الزر | نص | العنوان للزر (فقط للبديل الزر). |
|
||||
| القيمة | نص | القيمة الأولية للمساحة النصية. |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## مساحة نصية
|
||||
@@ -107,31 +123,40 @@ image: /images/user-guide/notes/notes_header.png
|
||||
تتيح لك إنشاء إدخالات نصية متعددة الأسطر.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { TextArea } from "@/ui/input/components/TextArea";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('تم تشغيل الدالة onChange')}
|
||||
placeholder="أدخل النص هنا"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { TextArea } from "@/ui/input/components/TextArea";
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| ------------------ | ----------- | ------------------------------------------------ |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كانت المساحة النصية معطلة. |
|
||||
| الحد الأدنى للأسطر | رقم | العدد الأدنى للأسطر الظاهرة للمساحة النصية. |
|
||||
| onChange | وظيفة | دالة الاستدعاء تُشغّل عند تغيّر محتوى منطقة النص |
|
||||
| نص توضيحي | نص | النص المُوضّح عندما تكون منطقة النص فارغة |
|
||||
| القيمة | نص | القيمة الحالية لمنطقة النص |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<TextArea
|
||||
disabled={false}
|
||||
minRows={4}
|
||||
onChange={()=>console.log('تم تشغيل الدالة onChange')}
|
||||
placeholder="أدخل النص هنا"
|
||||
value=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| ------------------ | ----------- | ------------------------------------------------ |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كانت المساحة النصية معطلة. |
|
||||
| الحد الأدنى للأسطر | رقم | العدد الأدنى للأسطر الظاهرة للمساحة النصية. |
|
||||
| onChange | وظيفة | دالة الاستدعاء تُشغّل عند تغيّر محتوى منطقة النص |
|
||||
| نص توضيحي | نص | النص المُوضّح عندما تكون منطقة النص فارغة |
|
||||
| القيمة | نص | القيمة الحالية لمنطقة النص |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -8,29 +8,35 @@ image: /images/user-guide/table-views/table.png
|
||||
</Frame>
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { Toggle } from "twenty-ui/input";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Toggle
|
||||
value = {true}
|
||||
onChange = {()=>console.log('تم تشغيل حدث onChange')}
|
||||
color="green"
|
||||
toggleSize = "medium"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { Toggle } from "twenty-ui/input";
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| القيمة | قيمة منطقية | الحالة الحالية لمفتاح التبديل | `خاطئ` |
|
||||
| عند التغيير | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل | |
|
||||
| اللون | string | لون التبديل عند كونه | لون أزرق |
|
||||
| حجم التبديل | نص | حجم التبديل الذي يؤثر على كل من الطول والوزن. لديها خياران: `صغير` و`متوسط` | متوسط |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Toggle
|
||||
value = {true}
|
||||
onChange = {()=>console.log('تم تشغيل حدث onChange')}
|
||||
color="green"
|
||||
toggleSize = "medium"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ----------- | ----------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| القيمة | قيمة منطقية | الحالة الحالية لمفتاح التبديل | `خاطئ` |
|
||||
| عند التغيير | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل | |
|
||||
| اللون | string | لون التبديل عند كونه | لون أزرق |
|
||||
| حجم التبديل | نص | حجم التبديل الذي يؤثر على كل من الطول والوزن. لديها خياران: `صغير` و`متوسط` | متوسط |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,32 +10,38 @@ image: /images/user-guide/fields/field.png
|
||||
يعرض شريط مسار التنقّل.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Breadcrumb } from "@/ui/navigation/bread-crumb/components/Breadcrumb";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const breadcrumbLinks = [
|
||||
{ children: "Home", href: "/" },
|
||||
{ children: "Category", href: "/category" },
|
||||
{ children: "Subcategory", href: "/category/subcategory" },
|
||||
{ children: "Current Page" },
|
||||
];
|
||||
```jsx
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { Breadcrumb } from "@/ui/navigation/bread-crumb/components/Breadcrumb";
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Breadcrumb className links={breadcrumbLinks} />
|
||||
</BrowserRouter>
|
||||
)
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const breadcrumbLinks = [
|
||||
{ children: "Home", href: "/" },
|
||||
{ children: "Category", href: "/category" },
|
||||
{ children: "Subcategory", href: "/category/subcategory" },
|
||||
{ children: "Current Page" },
|
||||
];
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| روابط | مصفوفة | مصفوفة من الكائنات، يمثّل كلٌّ منها رابطًا في مسار التنقّل. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
|
||||
</Tab>
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Breadcrumb className links={breadcrumbLinks} />
|
||||
</BrowserRouter>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم الفئة | نص | اسم فئة اختياري لتنسيقات إضافية |
|
||||
| روابط | مصفوفة | مصفوفة من الكائنات، يمثّل كلٌّ منها رابطًا في مسار التنقّل. كل كائن يحتوي على خاصية `children` (محتوى النص للرابط) وخاصية `href` اختيارية (رابط URL للتنقل إليه عند النقر على الرابط) |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -12,40 +12,49 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
مكون رابط منمق لعرض معلومات الاتصال.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="27332A2E2F2745">
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
<Tab title="27332A2E2F2745">
|
||||
|
||||
import { ContactLink } from 'twenty-ui/navigation';
|
||||
```jsx
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('تم النقر على رابط الاتصال!', event);
|
||||
};
|
||||
import { ContactLink } from 'twenty-ui/navigation';
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ContactLink
|
||||
className
|
||||
href="mailto:example@example.com"
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
example@example.com
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};},{
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log('تم النقر على رابط الاتصال!', event);
|
||||
};
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ContactLink
|
||||
className
|
||||
href="mailto:example@example.com"
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
example@example.com
|
||||
</ContactLink>
|
||||
</Router>
|
||||
);
|
||||
};},{
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري للتنسيق الإضافي. |
|
||||
| رابط | نص | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| className | string | اسم اختياري للتنسيق الإضافي. |
|
||||
| رابط | نص | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## رابط خام
|
||||
@@ -53,36 +62,44 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
مكون رابط منمق لعرض الروابط.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RawLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { RawLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RawLink className href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RawLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<Router>
|
||||
<RawLink className href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RawLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| اسم الصنف | string | اسم اختياري لتنسيقات إضافية |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
</Tab>
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| اسم الصنف | string | اسم اختياري لتنسيقات إضافية |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## رابط مستدير
|
||||
@@ -90,34 +107,41 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
رابط مستدير مثبت مع مكون Chip للروابط.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { RoundedLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { RoundedLink } from "/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RoundedLink href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RoundedLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleLinkClick = (event) => {
|
||||
console.log("Contact link clicked!", event);
|
||||
};
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
</Tab>
|
||||
return (
|
||||
<Router>
|
||||
<RoundedLink href="/contact" onClick={handleLinkClick}>
|
||||
Contact Us
|
||||
</RoundedLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | ------------------------------------------------ |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## رابط التواصل الاجتماعي
|
||||
@@ -125,30 +149,37 @@ image: /images/user-guide/what-is-twenty/20.png
|
||||
روابط اجتماعية منمقة، مع دعم لأنواع متعددة من الروابط الاجتماعية، مثل العناوين الإلكترونية، LinkedIn، وX (أو Twitter).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { SocialLink } from "twenty-ui/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<SocialLink
|
||||
type="twitter"
|
||||
href="https://twitter.com/twentycrm"
|
||||
></SocialLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { SocialLink } from "twenty-ui/navigation";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
<Tab title="خصائص">
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | -------------------------------------------------------------------- |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| النوع | string | نوع الروابط الاجتماعية. تشمل الخيارات: `url`, `LinkedIn`, و`Twitter` |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<Router>
|
||||
<SocialLink
|
||||
type="twitter"
|
||||
href="https://twitter.com/twentycrm"
|
||||
></SocialLink>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="خصائص">
|
||||
|
||||
|
||||
| خصائص | النوع | الوصف |
|
||||
| --------- | ----------------- | -------------------------------------------------------------------- |
|
||||
| رابط | string | عنوان URL المستهدف أو المسار للرابط |
|
||||
| الأبناء | `React.ReactNode` | المحتوى ليتم عرضه داخل الرابط |
|
||||
| النوع | string | نوع الروابط الاجتماعية. تشمل الخيارات: `url`, `LinkedIn`, و`Twitter` |
|
||||
| عند النقر | وظيفة | دالة رد النداء ليتم تفعيلها عند النقر على الرابط |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,49 +10,57 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة متعدد الاستخدامات مصمم للاستخدام في قائمة أو قائمة تنقل.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItem } from "twenty-ui/display";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItem } from "twenty-ui/display";
|
||||
|
||||
const handleButtonClick = (event) => {
|
||||
console.log("Icon button clicked!", event);
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
text="Menu item text"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={true}
|
||||
testId="menu-item-1"
|
||||
onClick={handleMenuItemClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
const handleButtonClick = (event) => {
|
||||
console.log("Icon button clicked!", event);
|
||||
};
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------------- | ------------- | ---------------------------------------------------------------------------- |
|
||||
| أيقونة اليسار | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | يحدد لون الإبراز لعنصر القائمة. تشمل الخيارات: `افتراضي`, `خطر`, `موضع مؤقت` |
|
||||
| نص | نص | المحتوى النصي لعنصر القائمة |
|
||||
| أزرار الأيقونة | array | مجموعة من الكائنات التي تمثل أيقونات إضافية مرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
|
||||
| معرف الفحص | نص | السمة data-testid لأغراض الاختبار |
|
||||
| عند النقر | function | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItem
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
text="Menu item text"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={true}
|
||||
testId="menu-item-1"
|
||||
onClick={handleMenuItemClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| -------------- | ------------- | ---------------------------------------------------------------------------- |
|
||||
| أيقونة اليسار | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | يحدد لون الإبراز لعنصر القائمة. تشمل الخيارات: `افتراضي`, `خطر`, `موضع مؤقت` |
|
||||
| نص | نص | المحتوى النصي لعنصر القائمة |
|
||||
| أزرار الأيقونة | array | مجموعة من الكائنات التي تمثل أيقونات إضافية مرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
|
||||
| معرف الفحص | نص | السمة data-testid لأغراض الاختبار |
|
||||
| عند النقر | function | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتصميم إضافي |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الأشكال
|
||||
@@ -64,42 +72,49 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة على نمط الأوامر داخل القائمة للإشارة إلى اختصارات لوحة المفاتيح.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemCommand } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleCommandClick = () => {
|
||||
console.log("Command clicked!");
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemCommand } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
firstHotKey="⌘"
|
||||
secondHotKey="1"
|
||||
isSelected={true}
|
||||
onClick={handleCommandClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleCommandClick = () => {
|
||||
console.log("Command clicked!");
|
||||
};
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ----------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
|
||||
| secondHotKey | string | اختصار لوحة المفاتيح الثاني المرتبط بالأمر |
|
||||
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
|
||||
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemCommand
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
firstHotKey="⌘"
|
||||
secondHotKey="1"
|
||||
isSelected={true}
|
||||
onClick={handleCommandClick}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ----------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية إذا ظهرت قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| firstHotKey | string | أول اختصار لوحة مفاتيح مرتبط بالأمر |
|
||||
| secondHotKey | string | اختصار لوحة المفاتيح الثاني المرتبط بالأمر |
|
||||
| isSelected | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددا أو مميزا |
|
||||
| عند النقر | دالة | دالة الاستدعاء يتم تنشيطها عند النقر فوق عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### قابلة للسحب
|
||||
@@ -107,45 +122,52 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
مكون عنصر قائمة قابل للسحب مصمم ليتم استخدامه في قائمة أو قائمة حيث يمكن سحب العناصر، ويتم تنفيذ إجراءات إضافية عبر أزرار الأيقونات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItemDraggable } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { IconAlertCircle } from "@tabler/icons-react";
|
||||
import { MenuItemDraggable } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemDraggable
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={false}
|
||||
onClick={handleMenuItemClick}
|
||||
text="Menu item draggable"
|
||||
isDragDisabled={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleMenuItemClick = (event) => {
|
||||
console.log("Menu item clicked!", event);
|
||||
};
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | لون العنصر لهجة القائمة. يمكن أن يكون إحدى القيم: `default`، `placeholder`، و`danger` |
|
||||
| أزرار الأيقونات | array | مصفوفة الكائنات التي تمثل أزرار الأيقونات الإضافية المرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
|
||||
| عند_النقر | دالة | وظيفة استدعاء ليتم تشغيلها عند النقر فوق الرابط |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| isDragDisabled | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemDraggable
|
||||
LeftIcon={IconBell}
|
||||
accent="default"
|
||||
iconButtons={[{ Icon: IconAlertCircle, onClick: handleButtonClick }]}
|
||||
isTooltipOpen={false}
|
||||
onClick={handleMenuItemClick}
|
||||
text="Menu item draggable"
|
||||
isDragDisabled={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| accent | نص | لون العنصر لهجة القائمة. يمكن أن يكون إحدى القيم: `default`، `placeholder`، و`danger` |
|
||||
| أزرار الأيقونات | array | مصفوفة الكائنات التي تمثل أزرار الأيقونات الإضافية المرتبطة بعنصر القائمة |
|
||||
| isTooltipOpen | قيمة منطقية | يتحكم في ظهور تلميح الأدوات المرتبط بعنصر القائمة |
|
||||
| عند_النقر | دالة | وظيفة استدعاء ليتم تشغيلها عند النقر فوق الرابط |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| isDragDisabled | قيمة منطقية | يشير إلى ما إذا كان تم تعطيل السحب |
|
||||
| اسم الفئة | نص | اسم اختياري لإضافة التنسيق |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### التحديد المتعدد
|
||||
@@ -153,34 +175,41 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
يوفر طريقة لتنفيذ وظيفة التحديد المتعدد مع مربع اختيار مصاحب.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemMultiSelect } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemMultiSelect } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemMultiSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | string | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| onSelectChange | دالة | وظيفة استدعاء يتم تشغيلها عند تغيير حالة مربع الاختيار |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemMultiSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | string | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| onSelectChange | دالة | وظيفة استدعاء يتم تشغيلها عند تغيير حالة مربع الاختيار |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### التحديد المتعدد للصورة الرمزية
|
||||
@@ -188,35 +217,42 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة متعدد الخيارات مع صورة رمزية، ومربع اختيار للتحديد، ومحتوى نصي.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MenuItemMultiSelectAvatar } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
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=";
|
||||
```jsx
|
||||
import { MenuItemMultiSelectAvatar } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
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=";
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ----------- | -------------------------------------------------------------------- |
|
||||
| الصورة الرمزبية | `ReactNode` | الصورة الرمزبية أو الأيقونة لعرضها على الجانب الأيسر من عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| onSelectChange | دالة | وظيفة استدعاء يتم تشغيلها عند تغيير حالة مربع الاختيار |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemMultiSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="Avatar" />}
|
||||
text="First Option"
|
||||
selected={false}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ----------- | -------------------------------------------------------------------- |
|
||||
| الصورة الرمزبية | `ReactNode` | الصورة الرمزبية أو الأيقونة لعرضها على الجانب الأيسر من عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| onSelectChange | دالة | وظيفة استدعاء يتم تشغيلها عند تغيير حالة مربع الاختيار |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### التنقل
|
||||
@@ -224,36 +260,43 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة يتضمن أيقونة يسارية اختيارية، محتوى نصي، وأيقونة سهم إلى اليمين.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemNavigate } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleNavigation = () => {
|
||||
console.log("Navigate to another page");
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemNavigate } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemNavigate
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
onClick={handleNavigation}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleNavigation = () => {
|
||||
console.log("Navigate to another page");
|
||||
};
|
||||
|
||||
<Tab title="العناصر">
|
||||
| العناصر | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| عند_النقر | دالة | وظيفة الاستدعاء يتم تنشيطها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemNavigate
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
onClick={handleNavigation}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="العناصر">
|
||||
|
||||
|
||||
| العناصر | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| عند_النقر | دالة | وظيفة الاستدعاء يتم تنشيطها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### اختيار
|
||||
@@ -261,42 +304,49 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة يمكن تحديده، مع شكل محتوى متاح (أيقونة ونص) ومُؤشر (أيقونة تحقق) لحالة الاختيار.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemSelect } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
```jsx
|
||||
import { IconBell } from "@tabler/icons-react";
|
||||
import { MenuItemSelect } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ---------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلا |
|
||||
| معطل | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ---------------------------------------------------------- |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تظهر قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص لعنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلا |
|
||||
| معطل | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### اختيار الصورة الرمزية
|
||||
@@ -304,47 +354,54 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة قابل للتحديد مع صورة رمزية، يتضمن محتوى يسارياً اختيارياً (صورة رمزية ونصاً) ومؤشراً (أيقونة التحقق) لحالة التحديد.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MenuItemSelectAvatar } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const imageUrl =
|
||||
"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=";
|
||||
```jsx
|
||||
import { MenuItemSelectAvatar } from "twenty-ui/display";
|
||||
|
||||
const handleSelection = () => {
|
||||
console.log("تم تحديد عنصر القائمة");
|
||||
};
|
||||
export const MyComponent = () => {
|
||||
const imageUrl =
|
||||
"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=";
|
||||
|
||||
return (
|
||||
<MenuItemSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="الصورة الرمزية" />}
|
||||
text="الخيار الأول"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
testId="menu-item-test"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
const handleSelection = () => {
|
||||
console.log("تم تحديد عنصر القائمة");
|
||||
};
|
||||
|
||||
```
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemSelectAvatar
|
||||
avatar={<img src={imageUrl} alt="الصورة الرمزية" />}
|
||||
text="الخيار الأول"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={false}
|
||||
testId="menu-item-test"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| -------------- | ------------ | ---------------------------------------------------------------------------- |
|
||||
| الصورة الرمزية | `مكون React` | الصورة الرمزية أو الأيقونة التي سيتم عرضها على الجانب الأيسر من عنصر القائمة |
|
||||
| نص | نص | محتوى النص في عنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| testId | نص | سمة data-testid لأغراض الاختبار |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
</Tab>
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| -------------- | ------------ | ---------------------------------------------------------------------------- |
|
||||
| الصورة الرمزية | `مكون React` | الصورة الرمزية أو الأيقونة التي سيتم عرضها على الجانب الأيسر من عنصر القائمة |
|
||||
| نص | نص | محتوى النص في عنصر القائمة |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| testId | نص | سمة data-testid لأغراض الاختبار |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### اختيار اللون
|
||||
@@ -352,41 +409,48 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة قابل للتحديد يتضمن عينة لون للسيناريوهات التي تريد فيها من المستخدمين اختيار لون من القائمة.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { MenuItemSelectColor } from "twenty-ui/display";
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
```jsx
|
||||
import { MenuItemSelectColor } from "twenty-ui/display";
|
||||
|
||||
return (
|
||||
<MenuItemSelectColor
|
||||
color="green"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={true}
|
||||
variant="default"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
const handleSelection = () => {
|
||||
console.log("Menu item selected");
|
||||
};
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اللون | نص | لون الثيم المعروض كعينة في عنصر القائمة. الخيارات تشمل: `أخضر`, `تركواز`, `سماوي`, `أزرق`, `أرجواني`, `وردي`, `أحمر`, `برتقالي`, `أصفر`, `رمادي`. |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| البديل | نص | المتغير لعينة اللون. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemSelectColor
|
||||
color="green"
|
||||
selected={true}
|
||||
disabled={false}
|
||||
hovered={true}
|
||||
variant="default"
|
||||
onClick={handleSelection}
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اللون | نص | لون الثيم المعروض كعينة في عنصر القائمة. الخيارات تشمل: `أخضر`, `تركواز`, `سماوي`, `أزرق`, `أرجواني`, `وردي`, `أحمر`, `برتقالي`, `أصفر`, `رمادي`. |
|
||||
| المحدد | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة محددًا (مفحوص) |
|
||||
| تعطيل | قيمة منطقية | يشير إلى ما إذا كان عنصر القائمة معطلاً |
|
||||
| معلق عليه | قيمة منطقية | يشير إلى ما إذا كان يتم التحويم حاليًا على عنصر القائمة |
|
||||
| البديل | نص | المتغير لعينة اللون. يمكن أن يكون إما `افتراضي` أو `خط أنابيب` |
|
||||
| عند_النقر | دالة | دالة الاستدعاء التي يتم تحفيزها عند النقر على عنصر القائمة |
|
||||
| اسم الفئة | نص | اسم اختياري للتنسيق الإضافي. |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### تبديل
|
||||
@@ -394,35 +458,42 @@ image: /images/user-guide/kanban-views/kanban.png
|
||||
عنصر قائمة مع مفتاح تبديل مرتبط للسماح للمستخدمين بتمكين أو تعطيل ميزة معينة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="الاستخدام">
|
||||
```jsx
|
||||
import { IconBell } from '@tabler/icons-react';
|
||||
<Tab title="الاستخدام">
|
||||
|
||||
import { MenuItemToggle } from 'twenty-ui/display';
|
||||
```jsx
|
||||
import { IconBell } from '@tabler/icons-react';
|
||||
|
||||
export const MyComponent = () => {
|
||||
import { MenuItemToggle } from 'twenty-ui/display';
|
||||
|
||||
return (
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
toggled={true}
|
||||
toggleSize="small"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تُعرض قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص في عنصر القائمة |
|
||||
| مبدل | قيمة منطقية | يشير إلى ما إذا كان مفتاح التبديل في حالة "تشغيل" أو "إيقاف" |
|
||||
| onToggleChange | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل |
|
||||
| حجم التبديل | نص | حجم مفتاح التبديل. يمكن أن يكون إما \ |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
</Tab>
|
||||
return (
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBell}
|
||||
text="First Option"
|
||||
toggled={true}
|
||||
toggleSize="small"
|
||||
className
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف |
|
||||
| --------------- | ------------- | ------------------------------------------------------------ |
|
||||
| الأيقونة اليسرى | مكون الأيقونة | أيقونة اختيارية تُعرض قبل النص في عنصر القائمة |
|
||||
| نص | نص | محتوى النص في عنصر القائمة |
|
||||
| مبدل | قيمة منطقية | يشير إلى ما إذا كان مفتاح التبديل في حالة "تشغيل" أو "إيقاف" |
|
||||
| onToggleChange | دالة | دالة الاستدعاء التي يتم تحفيزها عند تغيير حالة مفتاح التبديل |
|
||||
| حجم التبديل | نص | حجم مفتاح التبديل. يمكن أن يكون إما \ |
|
||||
| اسم الفئة | نص | اسم اختياري لتنسيقات إضافية |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,40 +10,47 @@ image: /images/user-guide/table-views/table.png
|
||||
يستعرض شريط التنقل الذي يحتوي على عدة مكونات `NavigationBarItem`.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { IconHome, IconUser, IconSettings } from '@tabler/icons-react';
|
||||
import { NavigationBar } from "@/ui/navigation/navigation-bar/components/NavigationBar";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
```jsx
|
||||
import { IconHome, IconUser, IconSettings } from '@tabler/icons-react';
|
||||
import { NavigationBar } from "@/ui/navigation/navigation-bar/components/NavigationBar";
|
||||
|
||||
const navigationItems = [
|
||||
{
|
||||
name: "Home",
|
||||
Icon: IconHome,
|
||||
onClick: () => console.log("Home clicked"),
|
||||
},
|
||||
{
|
||||
name: "Profile",
|
||||
Icon: IconUser,
|
||||
onClick: () => console.log("Profile clicked"),
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
Icon: IconSettings,
|
||||
onClick: () => console.log("Settings clicked"),
|
||||
},
|
||||
];
|
||||
export const MyComponent = () => {
|
||||
|
||||
return <NavigationBar activeItemName="Home" items={navigationItems}/>;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
const navigationItems = [
|
||||
{
|
||||
name: "Home",
|
||||
Icon: IconHome,
|
||||
onClick: () => console.log("Home clicked"),
|
||||
},
|
||||
{
|
||||
name: "Profile",
|
||||
Icon: IconUser,
|
||||
onClick: () => console.log("Profile clicked"),
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
Icon: IconSettings,
|
||||
onClick: () => console.log("Settings clicked"),
|
||||
},
|
||||
];
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ---------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم العنصر النشط | نص | اسم العنصر النشط حاليًا في قائمة التنقل |
|
||||
| العناصر | مصفوفة | مصفوفة من الكائنات التي تمثل كل عنصر من عناصر التنقل. كل كائن يحتوي على `الاسم` للعنصر، ومكون `الأيقونة` للعرض، ودالة `onClick` التي تستدعي عند النقر على العنصر |
|
||||
</Tab>
|
||||
return <NavigationBar activeItemName="Home" items={navigationItems}/>;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ---------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| اسم العنصر النشط | نص | اسم العنصر النشط حاليًا في قائمة التنقل |
|
||||
| العناصر | مصفوفة | مصفوفة من الكائنات التي تمثل كل عنصر من عناصر التنقل. كل كائن يحتوي على `الاسم` للعنصر، ومكون `الأيقونة` للعرض، ودالة `onClick` التي تستدعي عند النقر على العنصر |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,25 +10,33 @@ image: /images/user-guide/api/api.png
|
||||
يعرض التقدم من خلال سلسلة من الخطوات المرقمة عن طريق تمييز الخطوة النشطة. يولد حاوية تحتوي على خطوات، يتم تمثيل كل منها بواسطة مكون 'Step'.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { StepBar } from "@/ui/navigation/step-bar/components/StepBar";
|
||||
<Tab title="استخدام">
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<StepBar activeStep={2}>
|
||||
<StepBar.Step>Step 1</StepBar.Step>
|
||||
<StepBar.Step>Step 2</StepBar.Step>
|
||||
<StepBar.Step>Step 3</StepBar.Step>
|
||||
</StepBar>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
```jsx
|
||||
import { StepBar } from "@/ui/navigation/step-bar/components/StepBar";
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------- | ----- | ----------------------------------------------------------------- |
|
||||
| الخطوة النشطة | رقم | مؤشر للخطوة النشطة حاليًا. هذا يحدد أي خطوة يجب إبرازها بشكل مرئي |
|
||||
</Tab>
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<StepBar activeStep={2}>
|
||||
<StepBar.Step>Step 1</StepBar.Step>
|
||||
<StepBar.Step>Step 2</StepBar.Step>
|
||||
<StepBar.Step>Step 3</StepBar.Step>
|
||||
</StepBar>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف |
|
||||
| ------------- | ----- | ----------------------------------------------------------------- |
|
||||
| الخطوة النشطة | رقم | مؤشر للخطوة النشطة حاليًا. هذا يحدد أي خطوة يجب إبرازها بشكل مرئي |
|
||||
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -10,35 +10,43 @@ image: /images/user-guide/emails/emails_header.png
|
||||
يشير إلى تقدم أو عد تنازلي ويتحرك من اليمين إلى اليسار.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { ProgressBar } from "twenty-ui/feedback";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ProgressBar
|
||||
duration={6000}
|
||||
delay={0}
|
||||
easing="easeInOut"
|
||||
barHeight={10}
|
||||
barColor="#4bb543"
|
||||
autoStart={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="استخدام">
|
||||
|
||||
<Tab title="المحددات">
|
||||
| المحددات | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ---------------- | ----------- | ------------------------------------------------------------------------------- | ----------------- |
|
||||
| المدة | رقم | إجمالي مدة الرسوم المتحركة لشريط التقدم بالميلي ثانية | 3 |
|
||||
| التأخير | رقم | التأخير في بدء الرسوم المتحركة لشريط التقدم بالميلي ثانية | 0 |
|
||||
| التخفيف | نص | وظيفة التخفيف للرسوم المتحركة لشريط التقدم | easeInOut |
|
||||
| ارتفاع الشريط | رقم | ارتفاع الشريط بالبكسل | 24 |
|
||||
| لون الشريط | نص | لون الشريط | gray80 |
|
||||
| التشغيل التلقائي | قيمة منطقية | إذا كان `true`، فإن الرسوم المتحركة لشريط التقدم تبدأ تلقائيًا عند تحميل المكون | `صحيح` |
|
||||
</Tab>
|
||||
```jsx
|
||||
import { ProgressBar } from "twenty-ui/feedback";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
<ProgressBar
|
||||
duration={6000}
|
||||
delay={0}
|
||||
easing="easeInOut"
|
||||
barHeight={10}
|
||||
barColor="#4bb543"
|
||||
autoStart={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="المحددات">
|
||||
|
||||
|
||||
| المحددات | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ---------------- | ----------- | ------------------------------------------------------------------------------- | ----------------- |
|
||||
| المدة | رقم | إجمالي مدة الرسوم المتحركة لشريط التقدم بالميلي ثانية | 3 |
|
||||
| التأخير | رقم | التأخير في بدء الرسوم المتحركة لشريط التقدم بالميلي ثانية | 0 |
|
||||
| التخفيف | نص | وظيفة التخفيف للرسوم المتحركة لشريط التقدم | easeInOut |
|
||||
| ارتفاع الشريط | رقم | ارتفاع الشريط بالبكسل | 24 |
|
||||
| لون الشريط | نص | لون الشريط | gray80 |
|
||||
| التشغيل التلقائي | قيمة منطقية | إذا كان `true`، فإن الرسوم المتحركة لشريط التقدم تبدأ تلقائيًا عند تحميل المكون | `صحيح` |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## شريط التقدم الدائري
|
||||
@@ -46,21 +54,30 @@ image: /images/user-guide/emails/emails_header.png
|
||||
يشير إلى تقدم المهمة، ويستخدم غالباً في شاشات التحميل أو الأماكن التي ترغب فيها في إبلاغ العمليات الجارية إلى المستخدم.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="استخدام">
|
||||
```jsx
|
||||
import { CircularProgressBar } from "@/ui/feedback/progress-bar/components/CircularProgressBar";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <CircularProgressBar size={80} barWidth={6} barColor="green" />;
|
||||
};
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="استخدام">
|
||||
|
||||
```jsx
|
||||
import { CircularProgressBar } from "@/ui/feedback/progress-bar/components/CircularProgressBar";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <CircularProgressBar size={80} barWidth={6} barColor="green" />;
|
||||
};
|
||||
```
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
|
||||
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ---------- | ----- | ----------------------- | ----------------- |
|
||||
| الحجم | رقم | حجم شريط التقدم الدائري | 50 |
|
||||
| عرض الشريط | رقم | عرض خط شريط التقدم | 5 |
|
||||
| لون الشريط | نص | لون شريط التقدم | currentColor |
|
||||
|
||||
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="الخصائص">
|
||||
| الخصائص | النوع | الوصف | الإعداد الافتراضي |
|
||||
| ---------- | ----- | ----------------------- | ----------------- |
|
||||
| الحجم | رقم | حجم شريط التقدم الدائري | 50 |
|
||||
| عرض الشريط | رقم | عرض خط شريط التقدم | 5 |
|
||||
| لون الشريط | نص | لون شريط التقدم | currentColor |
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -4,7 +4,7 @@ description: ادمج قدرات الذكاء الاصطناعي مباشرةً
|
||||
---
|
||||
|
||||
<Note>
|
||||
هذه الميزة قيد التطوير حالياً وستكون متاحة في النسخة التجريبية قريباً.
|
||||
هذه الميزة قيد التطوير حالياً وستكون متاحة في النسخة التجريبية قريباً.
|
||||
</Note>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
@@ -4,7 +4,7 @@ description: مساعد ذكي يساعدك على التفاعل مع بيان
|
||||
---
|
||||
|
||||
<Note>
|
||||
هذه الميزة قيد التطوير حالياً وستكون متاحة في النسخة التجريبية قريباً.
|
||||
هذه الميزة قيد التطوير حالياً وستكون متاحة في النسخة التجريبية قريباً.
|
||||
</Note>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
@@ -26,7 +26,7 @@ description: تحكّم بما يمكن لوكلاء الذكاء الاصطنا
|
||||
| **إمكانية التدقيق** | تتبُّع الإجراءات التي نفّذها كل وكيل |
|
||||
|
||||
<Note>
|
||||
بالنسبة لوكلاء الذكاء الاصطناعي الذين يعملون ضمن سير العمل، يضمن تعيين الدور ألا يتمكّن الوكيل من الوصول إلى البيانات أو تعديلها خارج نطاقه المقصود — حتى إذا كانت لسير العمل أذونات أوسع.
|
||||
بالنسبة لوكلاء الذكاء الاصطناعي الذين يعملون ضمن سير العمل، يضمن تعيين الدور ألا يتمكّن الوكيل من الوصول إلى البيانات أو تعديلها خارج نطاقه المقصود — حتى إذا كانت لسير العمل أذونات أوسع.
|
||||
</Note>
|
||||
|
||||
## ذات صلة
|
||||
|
||||
@@ -4,6 +4,7 @@ description: الأسئلة الشائعة حول ميزات الذكاء الا
|
||||
---
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="متى ستكون ميزات الذكاء الاصطناعي متاحة؟">
|
||||
ميزات الذكاء الاصطناعي قيد التطوير حالياً وسيتم إصدارها كإصدار تجريبي قريباً. ترقبوا التحديثات!
|
||||
</Accordion>
|
||||
@@ -26,4 +27,5 @@ description: الأسئلة الشائعة حول ميزات الذكاء الا
|
||||
<Accordion title="هل يمكنني استخدام نماذج الذكاء الاصطناعي الخاصة بي؟">
|
||||
في البداية، ستستخدم Twenty نماذج ذكاء اصطناعي مدمجة. قد تتم إضافة دعم لنماذج الذكاء الاصطناعي المخصصة أو الخارجية في إصدارات مستقبلية استناداً إلى ملاحظات المستخدمين.
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -19,7 +19,7 @@ description: تعرّف على خطط تسعير Twenty وكيفية التبد
|
||||
* دعم قياسي
|
||||
|
||||
<Note>
|
||||
الميزات المتميزة (SSO وأذونات على مستوى الصف) غير مشمولة في خطة Pro.
|
||||
الميزات المتميزة (SSO وأذونات على مستوى الصف) غير مشمولة في خطة Pro.
|
||||
</Note>
|
||||
|
||||
### المؤسسة (سحابي)
|
||||
|
||||
@@ -17,7 +17,7 @@ description: فهم أرصدة سير العمل، والاستهلاك، وكي
|
||||
| سنوي | 50 مليون/سنة |
|
||||
|
||||
<Note>
|
||||
تم تصميم 5 ملايين رصيد شهريًا لتمكينك من تشغيل الأتمتة دون القلق بشأن التكاليف. بالنسبة إلى معظم سير العمل التي تستخدم الإجراءات القياسية، فهذا أكثر من كافٍ. ستحتاج إلى أرصدة إضافية فقط عند تشغيل عقد برمجية متقدمة أو ميزات مدعومة بالذكاء الاصطناعي.
|
||||
تم تصميم 5 ملايين رصيد شهريًا لتمكينك من تشغيل الأتمتة دون القلق بشأن التكاليف. بالنسبة إلى معظم سير العمل التي تستخدم الإجراءات القياسية، فهذا أكثر من كافٍ. ستحتاج إلى أرصدة إضافية فقط عند تشغيل عقد برمجية متقدمة أو ميزات مدعومة بالذكاء الاصطناعي.
|
||||
</Note>
|
||||
|
||||
## استهلاك الأرصدة
|
||||
|
||||
@@ -6,81 +6,78 @@ description: الأسئلة الشائعة حول تسعير Twenty والفوت
|
||||
## التسعير
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل يمكنني استخدام Twenty مجانًا أثناء الاستضافة الذاتية؟">
|
||||
نعم، يمكنك استخدام Twenty مجانًا أثناء الاستضافة الذاتية. ستحصل على إمكانية الوصول إلى كل ما هو مضمّن في خطة Pro (السحابة)، باستثناء الدعم من فريقنا الأساسي. الدعم متاح عبر مجتمعنا في Discord.
|
||||
<Accordion title="هل يمكنني استخدام Twenty مجانًا أثناء الاستضافة الذاتية؟">
|
||||
نعم، يمكنك استخدام Twenty مجانًا أثناء الاستضافة الذاتية. ستحصل على إمكانية الوصول إلى كل ما هو مضمّن في خطة Pro (السحابة)، باستثناء الدعم من فريقنا الأساسي. الدعم متاح عبر مجتمعنا في Discord.
|
||||
|
||||
إذا كنت تريد الاستضافة الذاتية وتحتاج إلى الميزات المتميزة (SSO وأذونات على مستوى السجل)، فيمكنك اختيار ترخيص Organization (الاستضافة الذاتية) المدفوع. يشمل ذلك أيضًا دعمًا من فريق Twenty ويلغي شرط نشر الشيفرة المخصصة كمصدر مفتوح قبل التوزيع.
|
||||
</Accordion>
|
||||
إذا كنت تريد الاستضافة الذاتية وتحتاج إلى الميزات المتميزة (SSO وأذونات على مستوى السجل)، فيمكنك اختيار ترخيص Organization (الاستضافة الذاتية) المدفوع. يشمل ذلك أيضًا دعمًا من فريق Twenty ويلغي شرط نشر الشيفرة المخصصة كمصدر مفتوح قبل التوزيع.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ما هي الميزات المتميزة؟">
|
||||
لا تتوفر الميزات المتميزة إلا في خطط Organization (السحابة أو الاستضافة الذاتية):
|
||||
<Accordion title="ما هي الميزات المتميزة؟">
|
||||
لا تتوفر الميزات المتميزة إلا في خطط Organization (السحابة أو الاستضافة الذاتية):
|
||||
* **تكامل SSO**: تسجيل الدخول الأحادي مع موفر الهوية لديك
|
||||
* **أذونات على مستوى السجل**: تحكم دقيق في الوصول على مستوى السجل
|
||||
</Accordion>
|
||||
|
||||
* **تكامل SSO**: تسجيل الدخول الأحادي مع موفر الهوية لديك
|
||||
* **أذونات على مستوى السجل**: تحكم دقيق في الوصول على مستوى السجل
|
||||
</Accordion>
|
||||
<Accordion title="هل تقدمون مقاعد مجانية للمستخدمين العارضين فقط؟">
|
||||
نحن لا نقدم مقاعد مجانية. التسعير يكون لكل مستخدم وكل مستخدم يحتاج إلى ترخيص للوصول إلى Twenty.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل تقدمون مقاعد مجانية للمستخدمين العارضين فقط؟">
|
||||
نحن لا نقدم مقاعد مجانية. التسعير يكون لكل مستخدم وكل مستخدم يحتاج إلى ترخيص للوصول إلى Twenty.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى خطة المؤسسة؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `التبديل إلى المؤسسة`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى خطة المؤسسة؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `التبديل إلى المؤسسة`.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى الخطة الاحترافية؟">
|
||||
يرجى التواصل مع فريقنا مباشرة عبر الدعم، لا يوجد حاليًا طريقة سهلة للقيام بذلك عبر واجهة المستخدم.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى الخطة الاحترافية؟">
|
||||
يرجى التواصل مع فريقنا مباشرة عبر الدعم، لا يوجد حاليًا طريقة سهلة للقيام بذلك عبر واجهة المستخدم.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى سنوي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `التبديل إلى السنوي`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى سنوي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `التبديل إلى السنوي`.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى شهري؟">
|
||||
يرجى التواصل مع فريقنا مباشرة عبر الدعم، لا يوجد حاليًا طريقة سهلة للقيام بذلك عبر واجهة المستخدم.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تبديل اشتراكي إلى شهري؟">
|
||||
يرجى التواصل مع فريقنا مباشرة عبر الدعم، لا يوجد حاليًا طريقة سهلة للقيام بذلك عبر واجهة المستخدم.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني رؤية استخدام الرصيد الخاص بي؟">
|
||||
ستجد ذلك ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني رؤية استخدام الرصيد الخاص بي؟">
|
||||
ستجد ذلك ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
<Accordion title="كم عدد أرصدة سير العمل لدينا؟">
|
||||
يعتمد عدد الأرصدة على دورة الفوترة لديك، وليس على خطتك:
|
||||
* **الاشتراكات الشهرية**: 5 ملايين رصيد شهريًا
|
||||
* **الاشتراكات السنوية**: 50 مليون رصيد سنويًا
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="كم عدد أرصدة سير العمل لدينا؟">
|
||||
يعتمد عدد الأرصدة على دورة الفوترة لديك، وليس على خطتك:
|
||||
<Accordion title="كيف يعمل استهلاك رصيد سير العمل؟">
|
||||
يستهلك كل إجراء سير عمل الرصيد بناءً على تعقيده.
|
||||
* **العمليات الداخلية الأساسية** (مثل البحث والتحديث وإنشاء السجلات) تستهلك عدد قليل من الأرصدة
|
||||
* **عمليات أكثر تعقيدًا** مثل عقد الشيفرة والطلبات إلى الخدمات الخارجية تستهلك مزيدًا من الأرصدة
|
||||
* **طلبات الذكاء الاصطناعي** (قريبًا!) ستستهلك أيضًا المزيد من الأرصدة بناءً على الاستخدام
|
||||
|
||||
* **الاشتراكات الشهرية**: 5 ملايين رصيد شهريًا
|
||||
* **الاشتراكات السنوية**: 50 مليون رصيد سنويًا
|
||||
</Accordion>
|
||||
يتم خصم الأرصدة فورًا عند تنفيذ سير العمل. يمكنك متابعة استخدامك في **الإعدادات → الفوترة** لمتابعة الاستهلاك والأرصدة المتبقية.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="كيف يعمل استهلاك رصيد سير العمل؟">
|
||||
يستهلك كل إجراء سير عمل الرصيد بناءً على تعقيده.
|
||||
|
||||
* **العمليات الداخلية الأساسية** (مثل البحث والتحديث وإنشاء السجلات) تستهلك عدد قليل من الأرصدة
|
||||
* **عمليات أكثر تعقيدًا** مثل عقد الشيفرة والطلبات إلى الخدمات الخارجية تستهلك مزيدًا من الأرصدة
|
||||
* **طلبات الذكاء الاصطناعي** (قريبًا!) ستستهلك أيضًا المزيد من الأرصدة بناءً على الاستخدام
|
||||
|
||||
يتم خصم الأرصدة فورًا عند تنفيذ سير العمل. يمكنك متابعة استخدامك في **الإعدادات → الفوترة** لمتابعة الاستهلاك والأرصدة المتبقية.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني شراء المزيد من أرصدة سير العمل؟">
|
||||
تستطيع شراء أرصدة إضافية ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني شراء المزيد من أرصدة سير العمل؟">
|
||||
تستطيع شراء أرصدة إضافية ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## الفوترة
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="أين يمكنني إدارة تفاصيل الفوترة؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني إدارة تفاصيل الفوترة؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تحديث طريقة الدفع الخاصة بي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. ستتمكن من إضافة طريقة دفع جديدة هناك.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني تحديث طريقة الدفع الخاصة بي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. ستتمكن من إضافة طريقة دفع جديدة هناك.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني تحديث معلومات الفوترة الخاصة بي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. ستتمكن من تعديل معلومات الفوترة هناك.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني تحديث معلومات الفوترة الخاصة بي؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. ستتمكن من تعديل معلومات الفوترة هناك.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="أين يمكنني الوصول إلى فواتيري؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. سترى جميع فواتيرك في أسفل الشاشة.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني الوصول إلى فواتيري؟">
|
||||
يمكنك القيام بذلك ضمن `الإعدادات → الفوترة`. ثم انقر على `عرض تفاصيل الفوترة`. سترى جميع فواتيرك في أسفل الشاشة.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -16,7 +16,6 @@ image: /images/user-guide/setup/pricing.png
|
||||
<Card title="خطط التسعير" icon="tags" href="/l/ar/user-guide/billing/capabilities/pricing-plans">
|
||||
تعرّف على خطط تسعير Twenty وما تتضمنه.
|
||||
</Card>
|
||||
|
||||
<Card title="الأسئلة الشائعة" icon="circle-question" href="/l/ar/user-guide/billing/how-tos/billing-faq">
|
||||
أسئلة شائعة حول التسعير والفوترة.
|
||||
</Card>
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ description: معلومات حول إرسال رسائل البريد الإلك
|
||||
بالنسبة لتسلسلات البريد الإلكتروني والنشرات الإخبارية، نوصي باستخدام سير العمل لربط Twenty بأداة مخصّصة للتسويق عبر البريد الإلكتروني.
|
||||
|
||||
<Warning>
|
||||
لا ينبغي إرسال رسائل بريد إلكتروني جماعية مباشرةً من صندوق بريدك لحماية سمعة نطاقك. استخدم أداة مخصّصة للاتصالات الجماعية.
|
||||
لا ينبغي إرسال رسائل بريد إلكتروني جماعية مباشرةً من صندوق بريدك لحماية سمعة نطاقك. استخدم أداة مخصّصة للاتصالات الجماعية.
|
||||
</Warning>
|
||||
|
||||
→ اطّلع على [كيفية إرسال الرسائل عبر سير العمل](/l/ar/user-guide/workflows/capabilities/send-emails-from-workflows) للحصول على إرشادات الإعداد
|
||||
|
||||
@@ -46,7 +46,7 @@ image: /images/user-guide/emails/emails_header.png
|
||||
* **إعدادات المزامنة**: تكوين إعدادات مختلفة لكل صندوق بريد
|
||||
|
||||
<Warning>
|
||||
لا يمكن ربط إلا صناديق البريد الحقيقية (مثل support@domain.com بصندوق الوارد الخاص بها). لا يمكن ربط الأسماء المستعارة للبريد الإلكتروني التي توجه إلى صندوق بريد آخر بـ Twenty.
|
||||
لا يمكن ربط إلا صناديق البريد الحقيقية (مثل support@domain.com بصندوق الوارد الخاص بها). لا يمكن ربط الأسماء المستعارة للبريد الإلكتروني التي توجه إلى صندوق بريد آخر بـ Twenty.
|
||||
</Warning>
|
||||
|
||||
## تكوين البريد الإلكتروني
|
||||
@@ -121,7 +121,7 @@ image: /images/user-guide/emails/emails_header.png
|
||||
**التحديثات كل 5 دقائق**: تتم مزامنة البيانات البريدية والتقويمية تلقائيًا كل 5 دقائق بعد الاستيراد الأولي.
|
||||
|
||||
<Note>
|
||||
**توقيت المزامنة الأولية**: يكتمل تزامن التقويم بسرعة (عادةً خلال دقائق)، بينما تستغرق مزامنة البريد الإلكتروني وقتاً أطول لصناديق البريد الكبيرة. يقوم Twenty باستيراد رسائل البريد الإلكتروني بمعدل يقارب **400 رسالة في الدقيقة** (محدود بتقييد المعدل من Gmail API)، لذا فإن صندوق بريد يحتوي على 10,000 رسالة يستغرق حوالي 25 دقيقة. Don't worry if you see contacts from calendar events appearing before your email contacts; this is normal behavior.
|
||||
**توقيت المزامنة الأولية**: يكتمل تزامن التقويم بسرعة (عادةً خلال دقائق)، بينما تستغرق مزامنة البريد الإلكتروني وقتاً أطول لصناديق البريد الكبيرة. يقوم Twenty باستيراد رسائل البريد الإلكتروني بمعدل يقارب **400 رسالة في الدقيقة** (محدود بتقييد المعدل من Gmail API)، لذا فإن صندوق بريد يحتوي على 10,000 رسالة يستغرق حوالي 25 دقيقة. Don't worry if you see contacts from calendar events appearing before your email contacts; this is normal behavior.
|
||||
</Note>
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
@@ -23,7 +23,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
* **النمط**: الألوان، التسميات، والخيارات المرئية
|
||||
|
||||
<Note>
|
||||
تظهر بعض الإعدادات فقط عند تهيئة إعدادات أخرى. على سبيل المثال، يظهر خيار **دقة التاريخ** فقط عند تحديد حقل تاريخ.
|
||||
تظهر بعض الإعدادات فقط عند تهيئة إعدادات أخرى. على سبيل المثال، يظهر خيار **دقة التاريخ** فقط عند تحديد حقل تاريخ.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -58,11 +58,11 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
<img src="/images/user-guide/dashboard/dashboard-chart-settings-filters.png" alt="تصفية" />
|
||||
|
||||
<Tip>
|
||||
بالنسبة لحقول التاريخ، جرّب **نسبي** لإنشاء عوامل تصفية ديناميكية مثل "آخر 7 أيام" أو "هذا الشهر" التي تتحدّث تلقائيًا.
|
||||
بالنسبة لحقول التاريخ، جرّب **نسبي** لإنشاء عوامل تصفية ديناميكية مثل "آخر 7 أيام" أو "هذا الشهر" التي تتحدّث تلقائيًا.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
تُدعم عوامل التصفية المتقدمة—أضِف قواعد تصفية متعددة وادمجها بمنطق **و** أو **أو**.
|
||||
تُدعم عوامل التصفية المتقدمة—أضِف قواعد تصفية متعددة وادمجها بمنطق **و** أو **أو**.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
@@ -110,11 +110,11 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
**نصيحة**: لمراحل خط الأنابيب، استخدم **الموضع** لعرض المراحل بترتيب عملية المبيعات لديك (Lead → Qualified → Proposal → Won).
|
||||
|
||||
<Note>
|
||||
تعتمد الخيارات المعروضة على نوع الحقل لديك. حقول الاختيار تتضمن جميع الخيارات؛ حقول التاريخ والنص تعرض خيارات أقل.
|
||||
تعتمد الخيارات المعروضة على نوع الحقل لديك. حقول الاختيار تتضمن جميع الخيارات؛ حقول التاريخ والنص تعرض خيارات أقل.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
مع الفرز **اليدوي**، اسحب الفئات لإنشاء ترتيب مخصص—مناسب تمامًا لإبراز شرائح محددة أو سرد قصة ببياناتك.
|
||||
مع الفرز **اليدوي**، اسحب الفئات لإنشاء ترتيب مخصص—مناسب تمامًا لإبراز شرائح محددة أو سرد قصة ببياناتك.
|
||||
</Tip>
|
||||
|
||||
### <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}> <ChartIcon icon="eye-off" />تجاهل القيم الصفرية </span>
|
||||
@@ -147,7 +147,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
<img src="/images/user-guide/dashboard/dashboard-chart-settings-y-data-on-display.png" alt="البيانات المعروضة" />
|
||||
|
||||
<Note>
|
||||
تعتمد الخيارات المعروضة على نوع الحقل ونوع المخطط. إذا لم تر العملية المتوقعة، فتحقّق من نوع الحقل.
|
||||
تعتمد الخيارات المعروضة على نوع الحقل ونوع المخطط. إذا لم تر العملية المتوقعة، فتحقّق من نوع الحقل.
|
||||
</Note>
|
||||
|
||||
### النسبة
|
||||
@@ -222,7 +222,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
عيّن نظام الألوان لمخططك. اختر من مجموعة متنوعة من لوحات الألوان.
|
||||
|
||||
<Note>
|
||||
عند التجميع حسب حقل اختيار، يستخدم المخطط تلقائيًا الألوان المحددة لكل خيار في إعدادات الحقل لديك.
|
||||
عند التجميع حسب حقل اختيار، يستخدم المخطط تلقائيًا الألوان المحددة لكل خيار في إعدادات الحقل لديك.
|
||||
</Note>
|
||||
|
||||
### <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}> <ChartIcon icon="gizmo" />اسم المحور </span>
|
||||
@@ -256,7 +256,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
* **إيقاف**: لا تُعرض وسيلة إيضاح
|
||||
|
||||
<Tip>
|
||||
**مرّر المؤشر** فوق عنصر في وسيلة الإيضاح لإبراز تلك المجموعة. **انقر** لإخفائه مؤقتًا من المخطط.
|
||||
**مرّر المؤشر** فوق عنصر في وسيلة الإيضاح لإبراز تلك المجموعة. **انقر** لإخفائه مؤقتًا من المخطط.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
@@ -84,9 +84,9 @@ description: أنشئ ونظّم لوحات المعلومات باستخدام
|
||||
تعرض لوحات المعلومات حاليًا البيانات استنادًا إلى المنطقة الزمنية للمستخدم الذي يشاهدها. يعني هذا أن لوحة المعلومات نفسها قد تعرض مقاييس مختلفة لأعضاء الفريق في مناطق مختلفة (مثل: APAC مقابل US).
|
||||
|
||||
<Note>
|
||||
**قريبًا**: سنضيف إمكانية تعيين منطقة زمنية محددة للوحة المعلومات، بحيث يرى جميع المستخدمين بيانات متسقة بغض النظر عن مواقعهم.
|
||||
**قريبًا**: سنضيف إمكانية تعيين منطقة زمنية محددة للوحة المعلومات، بحيث يرى جميع المستخدمين بيانات متسقة بغض النظر عن مواقعهم.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**قريبًا**: ستتيح عوامل التصفية على مستوى لوحة المعلومات تطبيق عوامل التصفية عبر جميع عناصر واجهة المستخدم دفعة واحدة، مما يجعل استكشاف بياناتك أسرع.
|
||||
**قريبًا**: ستتيح عوامل التصفية على مستوى لوحة المعلومات تطبيق عوامل التصفية عبر جميع عناصر واجهة المستخدم دفعة واحدة، مما يجعل استكشاف بياناتك أسرع.
|
||||
</Note>
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
اعرض البيانات كأعمدة أفقية أو عمودية.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-bar-chart.png" alt="مخطط أعمدة" />
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-bar-chart.png" alt="مخطط أعمدة" />
|
||||
</Frame>
|
||||
|
||||
**الأفضل لـ:**
|
||||
@@ -30,7 +30,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
* جهات الاتصال المضافة شهريًا
|
||||
|
||||
<Note>
|
||||
**حدود العرض**: يمكن لمخططات الأعمدة عرض حد أقصى يبلغ 100 عمود، و50 مجموعة لكل عمود عند استخدام "Group By". إذا ظهرت لك رسالة التحذير "بيانات غير معروضة: الحد الأقصى X عمودًا لكل مخطط"، فأضِف عوامل تصفية لتضييق نطاق بياناتك أو غيّر أسلوب التجميع (على سبيل المثال، اجعل التجميع حسب الأسبوع بدلًا من الأيام).
|
||||
**حدود العرض**: يمكن لمخططات الأعمدة عرض حد أقصى يبلغ 100 عمود، و50 مجموعة لكل عمود عند استخدام "Group By". إذا ظهرت لك رسالة التحذير "بيانات غير معروضة: الحد الأقصى X عمودًا لكل مخطط"، فأضِف عوامل تصفية لتضييق نطاق بياناتك أو غيّر أسلوب التجميع (على سبيل المثال، اجعل التجميع حسب الأسبوع بدلًا من الأيام).
|
||||
</Note>
|
||||
|
||||
### <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><ChartIcon icon="chart-pie" />مخططات دائرية</span>
|
||||
@@ -38,7 +38,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
اعرض نسب الأجزاء من الكل.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-pie-chart.png" alt="مخطط دائري" />
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-pie-chart.png" alt="مخطط دائري" />
|
||||
</Frame>
|
||||
|
||||
**الأفضل لـ:**
|
||||
@@ -58,7 +58,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
اعرض الاتجاهات مع مرور الوقت.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-line-chart.png" alt="مخطط خطي" />
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-line-chart.png" alt="مخطط خطي" />
|
||||
</Frame>
|
||||
|
||||
**الأفضل لـ:**
|
||||
@@ -78,7 +78,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
اعرض القيم الأساسية المفردة بشكل بارز.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-aggregate-chart.png" alt="مخطط تجميعي" />
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-aggregate-chart.png" alt="مخطط تجميعي" />
|
||||
</Frame>
|
||||
|
||||
**الأفضل لـ:**
|
||||
@@ -103,7 +103,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
ضمّن الأدوات والمحتوى الخارجي مباشرةً في لوحة المعلومات الخاصة بك.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-iframe.png" alt="iFrame" />
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-iframe.png" alt="iFrame" />
|
||||
</Frame>
|
||||
|
||||
**الأفضل لـ:**
|
||||
@@ -123,7 +123,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
أضف نصًا منسقًا ومحتوى مباشرةً إلى لوحة المعلومات الخاصة بك.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-rich-text.png" alt="نص منسق" />
|
||||
<img src="/images/user-guide/dashboard/dashboard-widget-rich-text.png" alt="نص منسق" />
|
||||
</Frame>
|
||||
|
||||
**الأفضل لـ:**
|
||||
@@ -139,7 +139,7 @@ import { ChartIcon } from '/snippets/chart-icon.mdx';
|
||||
* تحرير بأسلوب Markdown
|
||||
|
||||
<Note>
|
||||
**قريبًا**: مخططات القياس والجداول غير متاحة بعد ولكنها على خريطة الطريق لدينا.
|
||||
**قريبًا**: مخططات القياس والجداول غير متاحة بعد ولكنها على خريطة الطريق لدينا.
|
||||
</Note>
|
||||
|
||||
### تكرار الأدوات
|
||||
|
||||
@@ -47,7 +47,6 @@ description: أسئلة شائعة حول لوحات القيادة في Twenty.
|
||||
|
||||
<Accordion title="ما مدى تكرار تحديث بيانات لوحة القيادة؟">
|
||||
تتحدّث عناصر واجهة المستخدم تلقائيًا مع تغيّر بيانات نظام إدارة علاقات العملاء (CRM):
|
||||
|
||||
* تحديثات فورية لمعظم المقاييس
|
||||
* استخدم زر التحديث لإجراء تحديث يدوي عند الحاجة
|
||||
* تُحفَظ البيانات التاريخية لتحليل الاتجاهات
|
||||
|
||||
@@ -9,7 +9,7 @@ image: /images/user-guide/reporting/pie-chart.png
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
لوحات المعلومات حاليًا في الإصدار التجريبي. قم بتفعيلها ضمن **الإعدادات → التحديثات → الوصول المبكر**.
|
||||
لوحات المعلومات حاليًا في الإصدار التجريبي. قم بتفعيلها ضمن **الإعدادات → التحديثات → الوصول المبكر**.
|
||||
</Note>
|
||||
|
||||
## فهم لوحات المعلومات
|
||||
@@ -45,10 +45,9 @@ image: /images/user-guide/reporting/pie-chart.png
|
||||
* نص منسق
|
||||
|
||||
<Note>
|
||||
**القيود الحالية**:
|
||||
|
||||
* تصدير لوحات المعلومات ومشاركتها مع المستخدمين الخارجيين (غير مستخدمي Twenty) غير متاحين في الوقت الحالي.
|
||||
* مخططات القياس والجداول غير متاحة بعد.
|
||||
**القيود الحالية**:
|
||||
* تصدير لوحات المعلومات ومشاركتها مع المستخدمين الخارجيين (غير مستخدمي Twenty) غير متاحين في الوقت الحالي.
|
||||
* مخططات القياس والجداول غير متاحة بعد.
|
||||
</Note>
|
||||
|
||||
## البدء
|
||||
|
||||
@@ -141,9 +141,9 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
<Note>لن يتم إنشاء خيارات التحديد الجديدة تلقائيًا بواسطة الاستيراد. يجب إضافتها ضمن **الإعدادات → نموذج البيانات** قبل الاستيراد.</Note>
|
||||
|
||||
<Warning>
|
||||
**الاستيراد يستبدل، لا يضيف.**
|
||||
**الاستيراد يستبدل، لا يضيف.**
|
||||
|
||||
إذا كان السجل يحتوي بالفعل على `VALUE2` و`VALUE3` محدّدين، ثم استوردت `[\"VALUE1\"]`، فسيحتوي السجل على `VALUE1` فقط بعد الاستيراد. يتم استبدال التحديدات السابقة، وليس دمجها.
|
||||
إذا كان السجل يحتوي بالفعل على `VALUE2` و`VALUE3` محدّدين، ثم استوردت `[\"VALUE1\"]`، فسيحتوي السجل على `VALUE1` فقط بعد الاستيراد. يتم استبدال التحديدات السابقة، وليس دمجها.
|
||||
</Warning>
|
||||
|
||||
### حقول الأرقام
|
||||
|
||||
+10
-10
@@ -44,12 +44,12 @@ description: استيراد العلاقات بين السجلات عبر CSV.
|
||||
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
|
||||
|
||||
<Warning>
|
||||
**قيد في الاستيراد/التصدير**: العلاقات التي تشير إلى أنواع كائنات متعددة (مثل الملاحظات → الأشخاص/الشركات/الفرص) **غير مدعومة بعد** في استيراد أو تصدير CSV.
|
||||
**قيد في الاستيراد/التصدير**: العلاقات التي تشير إلى أنواع كائنات متعددة (مثل الملاحظات → الأشخاص/الشركات/الفرص) **غير مدعومة بعد** في استيراد أو تصدير CSV.
|
||||
|
||||
* **الاستيراد**: يمكن استيراد علاقات واحد إلى متعدد فقط التي تشير إلى نوع كائن واحد على كل جانب
|
||||
* **التصدير**: تُترك الأعمدة الخاصة بالعلاقات التي تشير إلى أنواع كائنات متعددة فارغة حاليًا
|
||||
* **الاستيراد**: يمكن استيراد علاقات واحد إلى متعدد فقط التي تشير إلى نوع كائن واحد على كل جانب
|
||||
* **التصدير**: تُترك الأعمدة الخاصة بالعلاقات التي تشير إلى أنواع كائنات متعددة فارغة حاليًا
|
||||
|
||||
هذا على خارطة الطريق لدينا.
|
||||
هذا على خارطة الطريق لدينا.
|
||||
</Warning>
|
||||
|
||||
### ما ليس مدعومًا اليوم
|
||||
@@ -137,12 +137,12 @@ Jane,Doe,jane@widgets.co,https://widgets.co
|
||||
## قاعدة ترتيب الاستيراد
|
||||
|
||||
<Warning>
|
||||
**احرص دائمًا على استيراد جانب "الواحد" أولًا!**
|
||||
**احرص دائمًا على استيراد جانب "الواحد" أولًا!**
|
||||
|
||||
1. **الشركات** أولًا (من دون تبعيات)
|
||||
2. **الأشخاص** ثانيًا (مرتبطون بالشركات)
|
||||
3. **الفرص** ثالثًا (مرتبطة بالشركات/الأشخاص)
|
||||
4. **الكائنات المخصّصة** وفقًا لتبعياتها
|
||||
1. **الشركات** أولًا (من دون تبعيات)
|
||||
2. **الأشخاص** ثانيًا (مرتبطون بالشركات)
|
||||
3. **الفرص** ثالثًا (مرتبطة بالشركات/الأشخاص)
|
||||
4. **الكائنات المخصّصة** وفقًا لتبعياتها
|
||||
|
||||
يجب أن يكون السجل الأصل موجودًا قبل أن تتمكّن من الإشارة إليه.
|
||||
يجب أن يكون السجل الأصل موجودًا قبل أن تتمكّن من الإشارة إليه.
|
||||
</Warning>
|
||||
|
||||
+2
-2
@@ -50,9 +50,9 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
### السجلات المحذوفة حذفًا غير نهائي
|
||||
|
||||
<Warning>
|
||||
**السجلات المحذوفة تُحتسب ضمن التفرّد.**
|
||||
**السجلات المحذوفة تُحتسب ضمن التفرّد.**
|
||||
|
||||
السجلات المحذوفة حذفًا غير نهائي (مرئية ضمن Command Menu → See deleted records) مُدرجة ضمن فحوصات التفرّد. إذا قمت باستيراد سجل بالقيمة الفريدة نفسها لسجل محذوف، فسيتم **استعادة** السجل المحذوف مع البيانات الجديدة.
|
||||
السجلات المحذوفة حذفًا غير نهائي (مرئية ضمن Command Menu → See deleted records) مُدرجة ضمن فحوصات التفرّد. إذا قمت باستيراد سجل بالقيمة الفريدة نفسها لسجل محذوف، فسيتم **استعادة** السجل المحذوف مع البيانات الجديدة.
|
||||
</Warning>
|
||||
|
||||
## اكتشاف التكرارات أثناء الاستيراد
|
||||
|
||||
@@ -71,12 +71,12 @@ description: دليل شامل لاستكشاف أخطاء استيراد CSV و
|
||||
3. ستقوم Twenty بتحديث السجل الموجود بدلًا من إنشاء نسخة مكرّرة
|
||||
|
||||
<Note>
|
||||
**يمكنك تحديث الحقول الفريدة أيضًا.**
|
||||
**يمكنك تحديث الحقول الفريدة أيضًا.**
|
||||
|
||||
* إذا أبقيت على `id` لكن غيّرت `email` → فسيتم تحديث البريد الإلكتروني
|
||||
* إذا أبقيت على `email` لكن غيّرت `id` → فسيتم تحديث المعرّف
|
||||
* إذا أبقيت على `id` لكن غيّرت `email` → فسيتم تحديث البريد الإلكتروني
|
||||
* إذا أبقيت على `email` لكن غيّرت `id` → فسيتم تحديث المعرّف
|
||||
|
||||
طالما أن أحد المعرّفات الفريدة يطابق، تقوم Twenty بتحديث السجل.
|
||||
طالما أن أحد المعرّفات الفريدة يطابق، تقوم Twenty بتحديث السجل.
|
||||
</Note>
|
||||
|
||||
### كيفية منع هذا الخطأ
|
||||
@@ -88,9 +88,9 @@ description: دليل شامل لاستكشاف أخطاء استيراد CSV و
|
||||
3. تحقّق مما إذا كانت السجلات موجودة مسبقًا في Twenty
|
||||
|
||||
<Warning>
|
||||
**السجلات المحذوفة حذفًا ناعمًا تُحتسب ضمن التفرد.**
|
||||
**السجلات المحذوفة حذفًا ناعمًا تُحتسب ضمن التفرد.**
|
||||
|
||||
تحقّق من قائمة الأوامر → اعرض السجلات المحذوفة. السجلات هناك ما تزال تفرض شرط التفرد. احذفها نهائيًا أو استعدها وقم بتحديثها.
|
||||
تحقّق من قائمة الأوامر → اعرض السجلات المحذوفة. السجلات هناك ما تزال تفرض شرط التفرد. احذفها نهائيًا أو استعدها وقم بتحديثها.
|
||||
</Warning>
|
||||
|
||||
لمزيد من التفاصيل: [قيود التفرد](/l/ar/user-guide/data-migration/capabilities/uniqueness-constraints)
|
||||
@@ -296,9 +296,9 @@ Can't connect to company. No unique record found with condition: id = 7776ee49-f
|
||||
* تحقّق من الأخطاء الإملائية وحساسية حالة الأحرف
|
||||
|
||||
<Warning>
|
||||
**سيفشل الاستيراد إذا تمت الإشارة إلى سجل غير موجود.**
|
||||
**سيفشل الاستيراد إذا تمت الإشارة إلى سجل غير موجود.**
|
||||
|
||||
استورد دائمًا كائنات الأصل قبل كائنات التابع.
|
||||
استورد دائمًا كائنات الأصل قبل كائنات التابع.
|
||||
</Warning>
|
||||
|
||||
لمزيد من التفاصيل: [استيراد العلاقات](/l/ar/user-guide/data-migration/capabilities/import-relations)
|
||||
@@ -372,9 +372,9 @@ Too many records. Up to 10000 allowed
|
||||
3. استخدم البريد الإلكتروني نفسه الذي استخدموه للانضمام إلى Twenty
|
||||
|
||||
<Warning>
|
||||
**يجب على المستخدمين قبول الدعوات قبل الاستيراد.**
|
||||
**يجب على المستخدمين قبول الدعوات قبل الاستيراد.**
|
||||
|
||||
إذا لم يقبل المستخدم دعوته، فستكون علاقات المستخدم فارغة في السجلات التي تشير إليه.
|
||||
إذا لم يقبل المستخدم دعوته، فستكون علاقات المستخدم فارغة في السجلات التي تشير إليه.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
+9
-9
@@ -12,23 +12,23 @@ description: دليل كامل خطوة بخطوة لاستيراد الشركا
|
||||
### قائمة المتطلبات المسبقة
|
||||
|
||||
<Check>
|
||||
يجب أن يكون ملفك بتنسيق CSV أو XLSX أو XLS
|
||||
يجب أن يكون ملفك بتنسيق CSV أو XLSX أو XLS
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
يحتوي الملف على أقل من 10,000 سجل
|
||||
يحتوي الملف على أقل من 10,000 سجل
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
لا توجد نطاقات مكررة في ملفك
|
||||
لا توجد نطاقات مكررة في ملفك
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
جميع الحقول المخصصة موجودة في **Settings → Data Model**
|
||||
جميع الحقول المخصصة موجودة في **Settings → Data Model**
|
||||
</Check>
|
||||
|
||||
<Note>
|
||||
هل تحتاج إلى استيراد أكثر من 10,000 شركة؟ قسّم إلى ملفات متعددة أو استخدم [استيراد عبر API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api).
|
||||
هل تحتاج إلى استيراد أكثر من 10,000 شركة؟ قسّم إلى ملفات متعددة أو استخدم [استيراد عبر API](/l/ar/user-guide/data-migration/how-tos/import-data-via-api).
|
||||
</Note>
|
||||
|
||||
## الخطوة 1: جهّز بيانات شركتك
|
||||
@@ -46,9 +46,9 @@ description: دليل كامل خطوة بخطوة لاستيراد الشركا
|
||||
### تنسيق النطاق
|
||||
|
||||
<Warning>
|
||||
**استخدم التنسيق `https://domain.com` للنطاقات.**
|
||||
**استخدم التنسيق `https://domain.com` للنطاقات.**
|
||||
|
||||
هذا يطابق التنسيق المستخدم عند إنشاء الشركات تلقائيًا من مزامنة البريد الإلكتروني/التقويم، مما يمنع التكرارات لاحقًا.
|
||||
هذا يطابق التنسيق المستخدم عند إنشاء الشركات تلقائيًا من مزامنة البريد الإلكتروني/التقويم، مما يمنع التكرارات لاحقًا.
|
||||
</Warning>
|
||||
|
||||
**أعمدة النطاق:**
|
||||
@@ -74,7 +74,7 @@ Widget Co,https://widgets.co,widgets.co,New York,USA,50
|
||||
```
|
||||
|
||||
<Note>
|
||||
**نصيحة احترافية:** انقر **Download sample file** أثناء الاستيراد للاطلاع على أسماء الأعمدة الدقيقة التي تتوقعها Twenty.
|
||||
**نصيحة احترافية:** انقر **Download sample file** أثناء الاستيراد للاطلاع على أسماء الأعمدة الدقيقة التي تتوقعها Twenty.
|
||||
</Note>
|
||||
|
||||
## الخطوة 2: الوصول إلى ميزة الاستيراد
|
||||
@@ -123,7 +123,7 @@ Widget Co,https://widgets.co,widgets.co,New York,USA,50
|
||||
3. أو أنشئ خيارات جديدة عند الحاجة
|
||||
|
||||
<Note>
|
||||
تستخدم خيارات الاختيار **أسماء API**، وليس تسميات العرض. تحقّق من **Settings → Data Model** → فعِّل **Advanced mode** لمشاهدة أسماء API.
|
||||
تستخدم خيارات الاختيار **أسماء API**، وليس تسميات العرض. تحقّق من **Settings → Data Model** → فعِّل **Advanced mode** لمشاهدة أسماء API.
|
||||
</Note>
|
||||
|
||||
## الخطوة 6: مراجعة الأخطاء وإصلاحها
|
||||
|
||||
+12
-12
@@ -12,29 +12,29 @@ description: دليل كامل خطوة بخطوة لاستيراد الأشخا
|
||||
### قائمة المتطلبات المسبقة
|
||||
|
||||
<Check>
|
||||
يجب أن يكون ملفك بتنسيق CSV أو XLSX أو XLS
|
||||
يجب أن يكون ملفك بتنسيق CSV أو XLSX أو XLS
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
يحتوي الملف على أقل من 10,000 سجل
|
||||
يحتوي الملف على أقل من 10,000 سجل
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
لا توجد عناوين بريد إلكتروني مكررة في ملفك
|
||||
لا توجد عناوين بريد إلكتروني مكررة في ملفك
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
**استيراد الشركات أولًا** (إذا كنت تربط الأشخاص بالشركات)
|
||||
**استيراد الشركات أولًا** (إذا كنت تربط الأشخاص بالشركات)
|
||||
</Check>
|
||||
|
||||
<Check>
|
||||
جميع الحقول المخصصة موجودة في **Settings → Data Model**
|
||||
جميع الحقول المخصصة موجودة في **Settings → Data Model**
|
||||
</Check>
|
||||
|
||||
<Warning>
|
||||
**استيراد الشركات قبل الأشخاص**
|
||||
**استيراد الشركات قبل الأشخاص**
|
||||
|
||||
إذا كنت تريد ربط الأشخاص بالشركات، فاستورد الشركات أولًا. يجب أن تكون الشركة موجودة قبل أن تتمكن من الإشارة إليها.
|
||||
إذا كنت تريد ربط الأشخاص بالشركات، فاستورد الشركات أولًا. يجب أن تكون الشركة موجودة قبل أن تتمكن من الإشارة إليها.
|
||||
</Warning>
|
||||
|
||||
## الخطوة 1: جهّز بيانات جهات الاتصال الخاصة بك
|
||||
@@ -81,7 +81,7 @@ description: دليل كامل خطوة بخطوة لاستيراد الأشخا
|
||||
| `companyId` | UUID | `c776ee49-f608-4a77-8cc8-6fe96ae1e43f` |
|
||||
|
||||
<Note>
|
||||
**استخدم تنسيق عنوان URL للنطاق** (`https://acme.com`)، وليس التسمية. هذا يطابق كيفية تخزين الشركات في Twenty.
|
||||
**استخدم تنسيق عنوان URL للنطاق** (`https://acme.com`)، وليس التسمية. هذا يطابق كيفية تخزين الشركات في Twenty.
|
||||
</Note>
|
||||
|
||||
### بنية CSV نموذجية
|
||||
@@ -93,7 +93,7 @@ Jane,Doe,jane@widgets.co,CTO,https://widgets.co,2125551234,US
|
||||
```
|
||||
|
||||
<Note>
|
||||
**نصيحة احترافية:** انقر **Download sample file** أثناء الاستيراد أو صدّر عددًا قليلًا من الأشخاص الموجودين للاطلاع على أسماء الأعمدة الدقيقة التي تتوقعها Twenty.
|
||||
**نصيحة احترافية:** انقر **Download sample file** أثناء الاستيراد أو صدّر عددًا قليلًا من الأشخاص الموجودين للاطلاع على أسماء الأعمدة الدقيقة التي تتوقعها Twenty.
|
||||
</Note>
|
||||
|
||||
## الخطوة 2: الوصول إلى ميزة الاستيراد
|
||||
@@ -145,9 +145,9 @@ Jane,Doe,jane@widgets.co,CTO,https://widgets.co,2125551234,US
|
||||
3. سيقوم Twenty بربط كل شخص بالشركة المطابقة
|
||||
|
||||
<Warning>
|
||||
**طابق معرّفًا فريدًا واحدًا فقط للعلاقات.**
|
||||
**طابق معرّفًا فريدًا واحدًا فقط للعلاقات.**
|
||||
|
||||
لا تقم بتعيين كلٍّ من `companyId` و`companyDomain` معًا. اختر واحدًا — ويفضَّل استخدام النطاق لأنه مقروء للبشر.
|
||||
لا تقم بتعيين كلٍّ من `companyId` و`companyDomain` معًا. اختر واحدًا — ويفضَّل استخدام النطاق لأنه مقروء للبشر.
|
||||
</Warning>
|
||||
|
||||
## الخطوة 5: تعيين قيم حقل الاختيار
|
||||
@@ -159,7 +159,7 @@ Jane,Doe,jane@widgets.co,CTO,https://widgets.co,2125551234,US
|
||||
3. أو أنشئ خيارات جديدة عند الحاجة
|
||||
|
||||
<Note>
|
||||
تستخدم خيارات الاختيار **أسماء API**، وليس تسميات العرض. تحقّق من **الإعدادات → نموذج البيانات** → فعِّل **الوضع المتقدم** لمشاهدة أسماء API.
|
||||
تستخدم خيارات الاختيار **أسماء API**، وليس تسميات العرض. تحقّق من **الإعدادات → نموذج البيانات** → فعِّل **الوضع المتقدم** لمشاهدة أسماء API.
|
||||
</Note>
|
||||
|
||||
## الخطوة 6: مراجعة الأخطاء وإصلاحها
|
||||
|
||||
@@ -32,9 +32,9 @@ description: متى وكيف تستخدم واجهات API الخاصة بـ Twe
|
||||
| **الحد الأقصى للإنتاجية** | ~6,000 سجل/دقيقة |
|
||||
|
||||
<Warning>
|
||||
**خطّط لعملية الاستيراد وفق هذه الحدود.**
|
||||
**خطّط لعملية الاستيراد وفق هذه الحدود.**
|
||||
|
||||
بالنسبة إلى 100,000 سجل عند أقصى إنتاجية، توقّع نحو 17 دقيقة من وقت الاستيراد. أضِف وقتًا احتياطيًا لمعالجة الأخطاء وإعادة المحاولة.
|
||||
بالنسبة إلى 100,000 سجل عند أقصى إنتاجية، توقّع نحو 17 دقيقة من وقت الاستيراد. أضِف وقتًا احتياطيًا لمعالجة الأخطاء وإعادة المحاولة.
|
||||
</Warning>
|
||||
|
||||
## البدء
|
||||
@@ -48,9 +48,9 @@ description: متى وكيف تستخدم واجهات API الخاصة بـ Twe
|
||||
5. احفظه في مكان آمن
|
||||
|
||||
<Warning>
|
||||
**أبقِ مفتاح API سريًا.**
|
||||
**أبقِ مفتاح API سريًا.**
|
||||
|
||||
يمكن لأي شخص لديه مفتاح API الخاص بك الوصول إلى بيانات مساحة العمل الخاصة بك وتعديلها. لا تقم أبدًا بإيداعه في مستودعات الكود أو مشاركته علنًا.
|
||||
يمكن لأي شخص لديه مفتاح API الخاص بك الوصول إلى بيانات مساحة العمل الخاصة بك وتعديلها. لا تقم أبدًا بإيداعه في مستودعات الكود أو مشاركته علنًا.
|
||||
</Warning>
|
||||
|
||||
### الخطوة 2: اختر واجهة API الخاصة بك
|
||||
|
||||
+6
-9
@@ -51,9 +51,9 @@ description: دليل كامل خطوة بخطوة لربط السجلات أث
|
||||
4. **المهام/الملاحظات** (مرتبطة بأي مما سبق)
|
||||
|
||||
<Warning>
|
||||
**إذا كان سجل الأصل غير موجود، فسيفشل الاستيراد.**
|
||||
**إذا كان سجل الأصل غير موجود، فسيفشل الاستيراد.**
|
||||
|
||||
تحقّق دائمًا من استيراد الشركات قبل استيراد الأشخاص الذين لديهم مراجع للشركات.
|
||||
تحقّق دائمًا من استيراد الشركات قبل استيراد الأشخاص الذين لديهم مراجع للشركات.
|
||||
</Warning>
|
||||
|
||||
## الخطوة 3: تدوين المعرّف الفريد للأصل
|
||||
@@ -117,9 +117,9 @@ Bob,Johnson,bob@techstart.io,Developer,https://techstart.io
|
||||
| المعرف | UUID | `c776ee49-f608-4a77-8cc8-6fe96ae1e43f` |
|
||||
|
||||
<Warning>
|
||||
**تنسيق النطاق مهم!**
|
||||
**تنسيق النطاق مهم!**
|
||||
|
||||
استخدم `https://domain.com` (وليس فقط `domain.com`). هذا يتوافق مع طريقة تخزين Twenty لنطاقات الشركات ويمنع أخطاء المطابقة.
|
||||
استخدم `https://domain.com` (وليس فقط `domain.com`). هذا يتوافق مع طريقة تخزين Twenty لنطاقات الشركات ويمنع أخطاء المطابقة.
|
||||
</Warning>
|
||||
|
||||
### قواعد مهمة
|
||||
@@ -178,7 +178,6 @@ Review proposal,jane@yourcompany.com
|
||||
<AccordionGroup>
|
||||
<Accordion title="ماذا لو لم يكن لدى سجل الأصل نطاق أو بريد إلكتروني؟">
|
||||
لديك خياران:
|
||||
|
||||
1. استخدم `id` الخاص بـ Twenty (صدّر سجلات الأصل للحصول على معرّفاتها)
|
||||
2. أنشئ حقلًا فريدًا مخصصًا في نموذج البيانات لتخزين معرّف خارجي من نظامك السابق
|
||||
</Accordion>
|
||||
@@ -197,7 +196,6 @@ Review proposal,jane@yourcompany.com
|
||||
|
||||
<Accordion title="ماذا يحدث إذا كان سجل الأصل غير موجود؟">
|
||||
سيعرض الاستيراد خطأً لذلك الصف. يمكنك إما:
|
||||
|
||||
* استورد سجل الأصل أولًا، ثم أعد الاستيراد
|
||||
* أصلِح قيمة المرجع
|
||||
* أزل الصف من الاستيراد
|
||||
@@ -205,7 +203,6 @@ Review proposal,jane@yourcompany.com
|
||||
|
||||
<Accordion title="لماذا لا يتم ربط العلاقة بشكل صحيح؟">
|
||||
أسباب شائعة:
|
||||
|
||||
* تنسيق غير صحيح (استخدم `https://domain.com` للنطاقات)
|
||||
* عدم تطابق حالة الأحرف (تحقّق من التهجئة الدقيقة)
|
||||
* الأصل غير موجود (استورد الأصول أولًا)
|
||||
@@ -214,9 +211,9 @@ Review proposal,jane@yourcompany.com
|
||||
</AccordionGroup>
|
||||
|
||||
<Warning>
|
||||
**تذكير: السجلات المحذوفة حذفًا ناعمًا تُحتسب ضمن التفرد.**
|
||||
**تذكير: السجلات المحذوفة حذفًا ناعمًا تُحتسب ضمن التفرد.**
|
||||
|
||||
إذا كنت تتلقى أخطاء "not found" ولكن يبدو أن السجل موجود، فتحقّق من قائمة الأوامر → عرض السجلات المحذوفة. قد يكون السجل الأصل قد حُذِف حذفًا منطقيًا.
|
||||
إذا كنت تتلقى أخطاء "not found" ولكن يبدو أن السجل موجود، فتحقّق من قائمة الأوامر → عرض السجلات المحذوفة. قد يكون السجل الأصل قد حُذِف حذفًا منطقيًا.
|
||||
</Warning>
|
||||
|
||||
## استكشاف الأخطاء وإصلاحها
|
||||
|
||||
+14
-15
@@ -59,17 +59,17 @@ description: دليل خطوة بخطوة لترحيل بياناتك من أي
|
||||
4. اضبط إعدادات الحقول (فريد، مطلوب، خيارات الاختيار، إلخ.)
|
||||
|
||||
<Warning>
|
||||
**يجب أن تكون الحقول موجودة قبل الاستيراد.**
|
||||
**يجب أن تكون الحقول موجودة قبل الاستيراد.**
|
||||
|
||||
يُنشئ استيراد CSV سجلات، وليس حقولًا. أنشِئ جميع الحقول المخصّصة ضمن الإعدادات → نموذج البيانات قبل الاستيراد.
|
||||
يُنشئ استيراد CSV سجلات، وليس حقولًا. أنشِئ جميع الحقول المخصّصة ضمن الإعدادات → نموذج البيانات قبل الاستيراد.
|
||||
</Warning>
|
||||
|
||||
### ادعُ فريقك
|
||||
|
||||
<Warning>
|
||||
**ادعُ المستخدمين قبل استيراد البيانات.**
|
||||
**ادعُ المستخدمين قبل استيراد البيانات.**
|
||||
|
||||
إذا كانت بياناتك تتضمن مراجع للمستخدمين (مالك الحساب، المكلَّف، إلخ.)، فيجب أن يكون هؤلاء المستخدمون موجودين في Twenty قبل الاستيراد. وإلا فلن يمكن تعيين تلك العلاقات.
|
||||
إذا كانت بياناتك تتضمن مراجع للمستخدمين (مالك الحساب، المكلَّف، إلخ.)، فيجب أن يكون هؤلاء المستخدمون موجودين في Twenty قبل الاستيراد. وإلا فلن يمكن تعيين تلك العلاقات.
|
||||
</Warning>
|
||||
|
||||
1. انتقل إلى **الإعدادات → الأعضاء**
|
||||
@@ -116,9 +116,9 @@ description: دليل خطوة بخطوة لترحيل بياناتك من أي
|
||||
| **حقول الاختيار** | استخدم أسماء API، وليس تسميات العرض |
|
||||
|
||||
<Warning>
|
||||
**تنسيق النطاق مهم.**
|
||||
**تنسيق النطاق مهم.**
|
||||
|
||||
استخدم `https://domain.com` (وليس `domain.com` أو `www.domain.com`). هذا يتوافق مع تنسيق Twenty ويمنع التكرارات عند توصيل مزامنة البريد الإلكتروني/التقويم.
|
||||
استخدم `https://domain.com` (وليس `domain.com` أو `www.domain.com`). هذا يتوافق مع تنسيق Twenty ويمنع التكرارات عند توصيل مزامنة البريد الإلكتروني/التقويم.
|
||||
</Warning>
|
||||
|
||||
اطّلع على [كيفية إعداد ملفات CSV](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files) لمعرفة متطلبات التنسيق الكاملة لجميع أنواع الحقول.
|
||||
@@ -149,17 +149,16 @@ Jane,Doe,jane@widgets.co,https://widgets.co
|
||||
## الخطوة 6: الاستيراد إلى Twenty
|
||||
|
||||
<Warning>
|
||||
**ترتيب الاستيراد مهم!**
|
||||
**ترتيب الاستيراد مهم!**
|
||||
|
||||
استورد دائمًا بهذا الترتيب:
|
||||
استورد دائمًا بهذا الترتيب:
|
||||
1. **الشركات** أولًا (من دون تبعيات)
|
||||
2. **الأشخاص** ثانيًا (مرتبطون بالشركات)
|
||||
3. **الفرص** ثالثًا (مرتبطة بالشركات/الأشخاص)
|
||||
4. **الملاحظات والمهام** (مرتبطة بالسجلات)
|
||||
5. **الكائنات المخصّصة** وفقًا لتبعياتها
|
||||
|
||||
1. **الشركات** أولًا (من دون تبعيات)
|
||||
2. **الأشخاص** ثانيًا (مرتبطون بالشركات)
|
||||
3. **الفرص** ثالثًا (مرتبطة بالشركات/الأشخاص)
|
||||
4. **الملاحظات والمهام** (مرتبطة بالسجلات)
|
||||
5. **الكائنات المخصّصة** وفقًا لتبعياتها
|
||||
|
||||
يجب أن يكون السجل الأصل موجودًا قبل أن تتمكّن من الإشارة إليه.
|
||||
يجب أن يكون السجل الأصل موجودًا قبل أن تتمكّن من الإشارة إليه.
|
||||
</Warning>
|
||||
|
||||
### استيراد كل كائن
|
||||
|
||||
+2
-2
@@ -29,9 +29,9 @@ description: دليل خطوة بخطوة لترحيل مثيل Twenty ذاتي
|
||||
## الخطوة 3: دعوة جميع المستخدمين
|
||||
|
||||
<Warning>
|
||||
**هام: قم بدعوة المستخدمين قبل استيراد البيانات.**
|
||||
**هام: قم بدعوة المستخدمين قبل استيراد البيانات.**
|
||||
|
||||
يجب على المستخدمين قبول دعواتهم قبل أن تستورد أي سجلات تُشير إليهم (مثل حقول مالك الحساب). إذا لم يكن المستخدمون موجودين بعد، فلن يمكن تعيين تلك العلاقات.
|
||||
يجب على المستخدمين قبول دعواتهم قبل أن تستورد أي سجلات تُشير إليهم (مثل حقول مالك الحساب). إذا لم يكن المستخدمون موجودين بعد، فلن يمكن تعيين تلك العلاقات.
|
||||
</Warning>
|
||||
|
||||
1. انتقل إلى **الإعدادات → الأعضاء** في مثيلك السحابي
|
||||
|
||||
+6
-7
@@ -210,15 +210,14 @@ Jane,Doe,jane@widgets.co,https://widgets.co
|
||||
* بالنسبة لأعضاء مساحة العمل، استخدم **البريد الإلكتروني** الخاص بهم (وليس الاسم)
|
||||
|
||||
<Warning>
|
||||
**ترتيب الاستيراد مهم!**
|
||||
**ترتيب الاستيراد مهم!**
|
||||
|
||||
استورد طرف "الواحد" قبل طرف "الكثير":
|
||||
استورد طرف "الواحد" قبل طرف "الكثير":
|
||||
1. **الشركات** أولاً
|
||||
2. **الأشخاص** ثانيًا (مع مرجع الشركة)
|
||||
3. **الفرص** ثالثًا
|
||||
|
||||
1. **الشركات** أولاً
|
||||
2. **الأشخاص** ثانيًا (مع مرجع الشركة)
|
||||
3. **الفرص** ثالثًا
|
||||
|
||||
يجب أن يوجد السجل الأب قبل أن تتمكن من الإشارة إليه.
|
||||
يجب أن يوجد السجل الأب قبل أن تتمكن من الإشارة إليه.
|
||||
</Warning>
|
||||
|
||||
اطلع على [كيفية استيراد العلاقات](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) للحصول على تعليمات مفصلة.
|
||||
|
||||
+5
-5
@@ -25,11 +25,11 @@ description: دليل كامل خطوة بخطوة لتحديث السجلات
|
||||
| مفقود من ملفك | **إنشاء** سجل جديد (بمُعرّف يتم إنشاؤه تلقائيًا) |
|
||||
|
||||
<Warning>
|
||||
**يتم استبدال حقول الاختيار المتعدد، ولا يتم دمجها.**
|
||||
**يتم استبدال حقول الاختيار المتعدد، ولا يتم دمجها.**
|
||||
|
||||
إذا كان السجل محدّدًا فيه `Option A` و`Option B`، وقمتَ باستيراد `[\"Option C\"]`، فسيحتوي السجل على `Option C` فقط بعد الاستيراد. تستبدل عملية الاستيراد جميع الاختيارات السابقة — ولا تضيف إليها.
|
||||
إذا كان السجل محدّدًا فيه `Option A` و`Option B`، وقمتَ باستيراد `[\"Option C\"]`، فسيحتوي السجل على `Option C` فقط بعد الاستيراد. تستبدل عملية الاستيراد جميع الاختيارات السابقة — ولا تضيف إليها.
|
||||
|
||||
للاحتفاظ بالقيم الحالية، ضمّنها جميعًا في عملية الاستيراد: `[\"Option A\",\"Option B\",\"Option C\"]`
|
||||
للاحتفاظ بالقيم الحالية، ضمّنها جميعًا في عملية الاستيراد: `[\"Option A\",\"Option B\",\"Option C\"]`
|
||||
</Warning>
|
||||
|
||||
## الخطوة 1: تصدير بياناتك الحالية
|
||||
@@ -80,9 +80,9 @@ id,email,firstName,lastName,jobTitle
|
||||
```
|
||||
|
||||
<Warning>
|
||||
**لا تغيّر قيم المعرّفات الفريدة.**
|
||||
**لا تغيّر قيم المعرّفات الفريدة.**
|
||||
|
||||
إذا غيّرتَ `john@acme.com` إلى `john.smith@acme.com`، فسيقوم Twenty بإنشاء سجل جديد بدلاً من تحديث السجل الموجود.
|
||||
إذا غيّرتَ `john@acme.com` إلى `john.smith@acme.com`، فسيقوم Twenty بإنشاء سجل جديد بدلاً من تحديث السجل الموجود.
|
||||
</Warning>
|
||||
|
||||
## الخطوة 3: استيراد الملف المُحدَّث
|
||||
|
||||
@@ -44,16 +44,15 @@ import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
<Note>**تُحتسب السجلات المحذوفة ضمن التفرّد.** السجلات المحذوفة حذفاً ناعماً (المرئية ضمن قائمة الأوامر → عرض السجلات المحذوفة) تُدرج ضمن عمليات التحقق من التفرّد. إذا قمتَ باستيراد سجل بالقيمة الفريدة نفسها لسجل محذوف، فسيتم استعادة السجل المحذوف.</Note>
|
||||
|
||||
<Warning>
|
||||
**ترتيب الاستيراد مهم!**
|
||||
**ترتيب الاستيراد مهم!**
|
||||
|
||||
عند استيراد أجسام مترابطة، ارفع الملفات بهذا الترتيب:
|
||||
عند استيراد أجسام مترابطة، ارفع الملفات بهذا الترتيب:
|
||||
1. **الشركات** أولاً (جانب "الواحد" من العلاقات)
|
||||
2. **الأفراد** ثانياً (مرتبطون بالشركات عبر companyId)
|
||||
3. **الفرص** ثالثاً (مرتبطة بالشركات/الأفراد)
|
||||
4. **الأجسام المخصّصة** ذات العلاقات أخيراً
|
||||
|
||||
1. **الشركات** أولاً (جانب "الواحد" من العلاقات)
|
||||
2. **الأفراد** ثانياً (مرتبطون بالشركات عبر companyId)
|
||||
3. **الفرص** ثالثاً (مرتبطة بالشركات/الأفراد)
|
||||
4. **الأجسام المخصّصة** ذات العلاقات أخيراً
|
||||
|
||||
لماذا؟ يجب أن يكون جانب "الواحد" من علاقة واحد إلى متعدد موجودًا قبل أن تتمكن من الإشارة إليه. على سبيل المثال، يجب أن يكون سجل الشركة موجودًا قبل أن تستورد شخصًا يحمل معرّف تلك الشركة.
|
||||
لماذا؟ يجب أن يكون جانب "الواحد" من علاقة واحد إلى متعدد موجودًا قبل أن تتمكن من الإشارة إليه. على سبيل المثال، يجب أن يكون سجل الشركة موجودًا قبل أن تستورد شخصًا يحمل معرّف تلك الشركة.
|
||||
</Warning>
|
||||
|
||||
يرجى الرجوع إلى [هذه المقالة](/l/ar/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv) للاطلاع على دليل خطوة بخطوة حول كيفية المتابعة.
|
||||
|
||||
@@ -30,7 +30,7 @@ description: اربط السجلات عبر كائنات مختلفة باستخ
|
||||
<img src="/images/user-guide/fields/one-to-many-morph.png" style={{width:'100%'}} />
|
||||
|
||||
<Warning>
|
||||
**قيد الاستيراد/التصدير**: العلاقات التي تشير إلى أنواع كائنات متعددة غير مدعومة بعد لاستيراد/تصدير CSV. هذا على خارطة الطريق لدينا.
|
||||
**قيد الاستيراد/التصدير**: العلاقات التي تشير إلى أنواع كائنات متعددة غير مدعومة بعد لاستيراد/تصدير CSV. هذا على خارطة الطريق لدينا.
|
||||
</Warning>
|
||||
|
||||
### متعدد-إلى-متعدد
|
||||
@@ -44,7 +44,7 @@ description: اربط السجلات عبر كائنات مختلفة باستخ
|
||||
<img src="/images/user-guide/fields/junction-relation-diagram.png" style={{width:'100%'}} />
|
||||
|
||||
<Warning>
|
||||
**ميزة المختبر**: يجب تمكين علاقات الربط في **الإعدادات → التحديثات → المختبر** قبل الاستخدام.
|
||||
**ميزة المختبر**: يجب تمكين علاقات الربط في **الإعدادات → التحديثات → المختبر** قبل الاستخدام.
|
||||
</Warning>
|
||||
|
||||
راجع [كيفية إنشاء علاقات متعدد-إلى-متعدد](/l/ar/user-guide/data-model/how-tos/create-many-to-many-relations) للحصول على دليل كامل خطوة بخطوة.
|
||||
|
||||
@@ -35,7 +35,7 @@ description: دليل خطوة بخطوة لإضافة حقول مخصصة إل
|
||||
3. اسحب لإعادة ترتيب الخيارات
|
||||
|
||||
<Note>
|
||||
**استخدم أسماء واجهة برمجة التطبيقات (API) لعمليات الاستيراد.** فعّل **الوضع المتقدم** في الإعدادات لعرض أسماء واجهة برمجة التطبيقات. راجع [تعيين الحقول](/l/ar/user-guide/data-migration/capabilities/field-mapping).
|
||||
**استخدم أسماء واجهة برمجة التطبيقات (API) لعمليات الاستيراد.** فعّل **الوضع المتقدم** في الإعدادات لعرض أسماء واجهة برمجة التطبيقات. راجع [تعيين الحقول](/l/ar/user-guide/data-migration/capabilities/field-mapping).
|
||||
</Note>
|
||||
|
||||
### لحقول العملة
|
||||
@@ -51,7 +51,7 @@ description: دليل خطوة بخطوة لإضافة حقول مخصصة إل
|
||||
فعّل **فريد** لمنع تكرار القيم عبر السجلات.
|
||||
|
||||
<Note>
|
||||
إذا وُجدت قيم مكررة (بما في ذلك في السجلات المحذوفة)، ستظهر رسالة خطأ. أزل التكرارات أولًا.
|
||||
إذا وُجدت قيم مكررة (بما في ذلك في السجلات المحذوفة)، ستظهر رسالة خطأ. أزل التكرارات أولًا.
|
||||
</Note>
|
||||
|
||||
### تعيين القيم الافتراضية
|
||||
|
||||
@@ -6,7 +6,7 @@ description: دليل خطوة بخطوة لإنشاء كائنات مخصصة
|
||||
تتيح لك الكائنات المخصصة تخزين معلومات فريدة لعملك لا تغطيها الكائنات القياسية. على سبيل المثال: المشاريع، المنتجات، التذاكر، أو القوائم.
|
||||
|
||||
<Note>
|
||||
**لست متأكدًا مما إذا كنت بحاجة إلى كائن أم حقل؟** اطّلع على [فهم نموذج البيانات لديك](/l/ar/user-guide/data-model/overview) للحصول على إرشادات.
|
||||
**لست متأكدًا مما إذا كنت بحاجة إلى كائن أم حقل؟** اطّلع على [فهم نموذج البيانات لديك](/l/ar/user-guide/data-model/overview) للحصول على إرشادات.
|
||||
</Note>
|
||||
|
||||
## الخطوات
|
||||
|
||||
+3
-3
@@ -6,11 +6,11 @@ description: اربط السجلات حيث يمكن ربط عناصر كثير
|
||||
تتيح علاقات متعدد-إلى-متعدد ربط سجلات متعددة على كلا الجانبين. على سبيل المثال: يمكن للعديد من الأشخاص العمل على العديد من المشاريع، ويمكن لكل مشروع أن يضم العديد من الأشخاص.
|
||||
|
||||
<Warning>
|
||||
**ميزة المختبر**: علاقات الربط متاحة حاليًا في المختبر. فعِّلها في **الإعدادات → التحديثات → المختبر** قبل اتباع هذا الدليل.
|
||||
**ميزة المختبر**: علاقات الربط متاحة حاليًا في المختبر. فعِّلها في **الإعدادات → التحديثات → المختبر** قبل اتباع هذا الدليل.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
تتطلب هذه الميزة أيضًا تفعيل **الوضع المتقدم** (زر التبديل في أسفل يمين صفحة الإعدادات).
|
||||
تتطلب هذه الميزة أيضًا تفعيل **الوضع المتقدم** (زر التبديل في أسفل يمين صفحة الإعدادات).
|
||||
</Note>
|
||||
|
||||
## متى نستخدم علاقات متعدد-إلى-متعدد
|
||||
@@ -56,7 +56,7 @@ People ←→ Project Assignments ←→ Projects
|
||||
4. انقر على **حفظ**
|
||||
|
||||
<Tip>
|
||||
**اتفاقية التسمية**: استخدم اسمًا يصف العلاقة، مثل "تعيين مشروع" أو "عضوية الفريق". هذا يجعل نموذج البيانات أسهل في الفهم.
|
||||
**اتفاقية التسمية**: استخدم اسمًا يصف العلاقة، مثل "تعيين مشروع" أو "عضوية الفريق". هذا يجعل نموذج البيانات أسهل في الفهم.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 2: إنشاء علاقات من كائن الربط
|
||||
|
||||
@@ -6,7 +6,7 @@ description: دليل خطوة بخطوة لربط الكائنات باستخد
|
||||
تربط حقول العلاقة السجلات من كائنات مختلفة—على سبيل المثال، ربط الأشخاص بالشركات.
|
||||
|
||||
<Note>
|
||||
**لا يمكن تغيير أسماء العلاقات بعد إنشائها** (إذ تؤثر في واجهة برمجة التطبيقات API). خطط لأسماء العلاقات بعناية.
|
||||
**لا يمكن تغيير أسماء العلاقات بعد إنشائها** (إذ تؤثر في واجهة برمجة التطبيقات API). خطط لأسماء العلاقات بعناية.
|
||||
</Note>
|
||||
|
||||
## قبل أن تبدأ
|
||||
@@ -51,7 +51,7 @@ description: دليل خطوة بخطوة لربط الكائنات باستخد
|
||||
تظل الروابط محفوظة لكنها مخفية. أعد التنشيط لاستعادتها.
|
||||
|
||||
<Note>
|
||||
**حذف العلاقة لا يحذف السجلات.** تتم إزالة الرابط بينهما فقط.
|
||||
**حذف العلاقة لا يحذف السجلات.** تتم إزالة الرابط بينهما فقط.
|
||||
</Note>
|
||||
|
||||
## ذات صلة
|
||||
|
||||
@@ -6,148 +6,147 @@ description: الأسئلة الشائعة حول نموذج بيانات Twenty
|
||||
## إدارة الكائنات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل يمكنني حذف كائن مخصص؟">
|
||||
نعم، يمكن حذف الكائنات المخصصة. يمكنك أيضًا إلغاء تنشيطها أولًا، مما يخفي الكائن وبياناته من الواجهة مع الاحتفاظ بالبيانات.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني حذف كائن مخصص؟">
|
||||
نعم، يمكن حذف الكائنات المخصصة. يمكنك أيضًا إلغاء تنشيطها أولًا، مما يخفي الكائن وبياناته من الواجهة مع الاحتفاظ بالبيانات.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني حذف الكائنات القياسية (الأشخاص، الشركات، إلخ)؟">
|
||||
لا، لا يمكن حذف الكائنات القياسية. يمكنك فقط إلغاء تنشيطها، مما يخفيها من الواجهة مع الاحتفاظ بالبيانات.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني حذف الكائنات القياسية (الأشخاص، الشركات، إلخ)؟">
|
||||
لا، لا يمكن حذف الكائنات القياسية. يمكنك فقط إلغاء تنشيطها، مما يخفيها من الواجهة مع الاحتفاظ بالبيانات.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="كم عدد الكائنات المخصصة التي يمكنني إنشاؤها؟">
|
||||
يمكنك إنشاء ما تشاء من الكائنات والحقول المخصصة — لن يتغير السعر.
|
||||
</Accordion>
|
||||
<Accordion title="كم عدد الكائنات المخصصة التي يمكنني إنشاؤها؟">
|
||||
يمكنك إنشاء ما تشاء من الكائنات والحقول المخصصة — لن يتغير السعر.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إعادة تسمية كائن قياسي؟">
|
||||
يمكنك تغيير تسمية الكائنات القياسية (People وCompanies وOpportunities)، لكن لا يمكنك تغيير أسماء API الخاصة بها. أسماء API ثابتة لضمان الاتساق عبر جميع مساحات عمل Twenty.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إعادة تسمية كائن قياسي؟">
|
||||
يمكنك تغيير تسمية الكائنات القياسية (People وCompanies وOpportunities)، لكن لا يمكنك تغيير أسماء API الخاصة بها. أسماء API ثابتة لضمان الاتساق عبر جميع مساحات عمل Twenty.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني تغيير أيقونة الكائن؟">
|
||||
نعم، يمكنك تغيير الأيقونة لكلٍ من الكائنات القياسية والمخصصة ضمن **الإعدادات → نموذج البيانات**.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني تغيير أيقونة الكائن؟">
|
||||
نعم، يمكنك تغيير الأيقونة لكلٍ من الكائنات القياسية والمخصصة ضمن **الإعدادات → نموذج البيانات**.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إعادة ترتيب الكائنات في شريط التنقل الأيسر؟">
|
||||
ليس بعد. ترتيب الكائنات في التنقل ثابت حاليًا، ولكن هذه الميزة مخطط لها لإصدارات قادمة.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إعادة ترتيب الكائنات في شريط التنقل الأيسر؟">
|
||||
ليس بعد. ترتيب الكائنات في التنقل ثابت حاليًا، ولكن هذه الميزة مخطط لها لإصدارات قادمة.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إخفاء الكائنات من شريط التنقل الأيسر؟">
|
||||
تظهر كل الكائنات النشطة في التنقل. يمكنك إلغاء تفعيل الكائنات التي لا تحتاجها تحت **الإعدادات → نموذج البيانات**.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إخفاء الكائنات من شريط التنقل الأيسر؟">
|
||||
تظهر كل الكائنات النشطة في التنقل. يمكنك إلغاء تفعيل الكائنات التي لا تحتاجها تحت **الإعدادات → نموذج البيانات**.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## قدرات الحقول
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل يمكنني تغيير نوع الحقل بعد إنشائه؟">
|
||||
لا، لا يمكن تغيير أنواع الحقول بعد إنشائها. إذا كنت بحاجة إلى نوع مختلف، فأنشئ حقلًا جديدًا بالنوع الصحيح، وانقل بياناتك، ثم عطّل الحقل القديم.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني تغيير نوع الحقل بعد إنشائه؟">
|
||||
لا، لا يمكن تغيير أنواع الحقول بعد إنشائها. إذا كنت بحاجة إلى نوع مختلف، فأنشئ حقلًا جديدًا بالنوع الصحيح، وانقل بياناتك، ثم عطّل الحقل القديم.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="لماذا أحتاج إلى أسماء مفردة وجمع مختلفة؟">
|
||||
تستخدم واجهة GraphQL كلا النموذجين لعمليات مختلفة:
|
||||
|
||||
* `createPerson` (مفرد) لأعمال سجلات فردية
|
||||
* `createPeople` (جمع) للعمليات الجماعية
|
||||
<Accordion title="لماذا أحتاج إلى أسماء مفردة وجمع مختلفة؟">
|
||||
تستخدم واجهة GraphQL كلا النموذجين لعمليات مختلفة:
|
||||
* `createPerson` (مفرد) لأعمال سجلات فردية
|
||||
* `createPeople` (جمع) للعمليات الجماعية
|
||||
|
||||
هذا يخلق قيودًا عندما تكون الأشكال المفردة والجمع متشابهة، ولكنه يحسن تجربة المطور.
|
||||
</Accordion>
|
||||
هذا يخلق قيودًا عندما تكون الأشكال المفردة والجمع متشابهة، ولكنه يحسن تجربة المطور.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="لماذا يتم حماية بعض أسماء الحقول؟">
|
||||
بعض أسماء الحقول مثل `Type` أو `Application` محجوزة لاستخدام النظام. اختر أسماء بديلة مثل `Category` أو `Classification` بدلاً من ذلك.
|
||||
</Accordion>
|
||||
<Accordion title="لماذا يتم حماية بعض أسماء الحقول؟">
|
||||
بعض أسماء الحقول مثل `Type` أو `Application` محجوزة لاستخدام النظام. اختر أسماء بديلة مثل `Category` أو `Classification` بدلاً من ذلك.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ماذا يحدث عند إلغاء تنشيط حقل؟">
|
||||
* يتم إخفاء الحقل من الواجهة
|
||||
* تظل البيانات محفوظة
|
||||
* لا يزال بإمكانك الوصول إلى الحقل عبر API
|
||||
* تظل العلاقات الحالية موجودة، ولكن لا يمكنك إنشاء علاقات جديدة
|
||||
* يمكنك إعادة تنشيط الحقل لاحقًا
|
||||
</Accordion>
|
||||
<Accordion title="ماذا يحدث عند إلغاء تنشيط حقل؟">
|
||||
* يتم إخفاء الحقل من الواجهة
|
||||
* تظل البيانات محفوظة
|
||||
* لا يزال بإمكانك الوصول إلى الحقل عبر API
|
||||
* تظل العلاقات الحالية موجودة، ولكن لا يمكنك إنشاء علاقات جديدة
|
||||
* يمكنك إعادة تنشيط الحقل لاحقًا
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني جعل الحقل إلزاميًا؟">
|
||||
حاليًا، لا يمكنك جعل الحقول المخصصة إلزامية. جميع الحقول تقبل القيم الفارغة. يمكنك استخدام سير العمل لفرض الحقول الإلزامية عبر إرسال التنبيهات أو حظر الإجراءات عندما تكون الحقول فارغة.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني جعل الحقل إلزاميًا؟">
|
||||
حاليًا، لا يمكنك جعل الحقول المخصصة إلزامية. جميع الحقول تقبل القيم الفارغة. يمكنك استخدام سير العمل لفرض الحقول الإلزامية عبر إرسال التنبيهات أو حظر الإجراءات عندما تكون الحقول فارغة.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ما الفرق بين الفريد والإلزامي؟">
|
||||
* **فريد**: يجب ألّا تتكرر القيمة نفسها في هذا الحقل عبر السجلات
|
||||
* **إلزامي**: يجب أن يحتوي الحقل على قيمة (غير مدعوم حاليًا للحقول المخصصة)
|
||||
</Accordion>
|
||||
<Accordion title="ما الفرق بين الفريد والإلزامي؟">
|
||||
* **فريد**: يجب ألّا تتكرر القيمة نفسها في هذا الحقل عبر السجلات
|
||||
* **إلزامي**: يجب أن يحتوي الحقل على قيمة (غير مدعوم حاليًا للحقول المخصصة)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إنشاء حقول صيغة؟">
|
||||
حقول الصيغ قادمة في **الربع الأول من عام 2026**. في الوقت الحالي، يمكنك استخدام سير العمل لحساب وتحديث قيم الحقول تلقائيًا.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إنشاء حقول صيغة؟">
|
||||
حقول الصيغ قادمة في **الربع الأول من عام 2026**. في الوقت الحالي، يمكنك استخدام سير العمل لحساب وتحديث قيم الحقول تلقائيًا.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكن أن يكون لدي حقول متداخلة داخل الكائنات الخاصة بي؟">
|
||||
الحقول المتداخلة قادمة في **الربع الأول من عام 2026**. حاليًا، يمكنك استخدام سير العمل لإحضار قيم الحقول من الكائنات ذات الصلة. مثلاً، لعرض صناعة شركة على سجل شخص، قم بإنشاء حقل مخصص للأشخاص واستخدم سير العمل لمزامنة القيمة.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكن أن يكون لدي حقول متداخلة داخل الكائنات الخاصة بي؟">
|
||||
الحقول المتداخلة قادمة في **الربع الأول من عام 2026**. حاليًا، يمكنك استخدام سير العمل لإحضار قيم الحقول من الكائنات ذات الصلة. مثلاً، لعرض صناعة شركة على سجل شخص، قم بإنشاء حقل مخصص للأشخاص واستخدم سير العمل لمزامنة القيمة.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إعادة ترتيب الحقول داخل الكائنات؟">
|
||||
سيكون إعادة ترتيب الحقول متاحًا مع التخطيطات المخصصة في **الربع الرابع من عام 2025**. حاليًا، تظهر الحقول بترتيب أبجدي.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إعادة ترتيب الحقول داخل الكائنات؟">
|
||||
سيكون إعادة ترتيب الحقول متاحًا مع التخطيطات المخصصة في **الربع الرابع من عام 2025**. حاليًا، تظهر الحقول بترتيب أبجدي.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## العلاقات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل يمكنني إنشاء علاقة تشير إلى الكائن نفسه؟">
|
||||
نعم! العلاقات ذاتية الإشارة مدعومة ومُوصى بها لحالات الاستخدام مثل التسلسلات الهرمية للحسابات. على سبيل المثال، أنشئ علاقة من Companies إلى Companies لتتبع حسابات الأصل/الفرع.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إنشاء علاقة تشير إلى الكائن نفسه؟">
|
||||
نعم! العلاقات ذاتية الإشارة مدعومة ومُوصى بها لحالات الاستخدام مثل التسلسلات الهرمية للحسابات. على سبيل المثال، أنشئ علاقة من Companies إلى Companies لتتبع حسابات الأصل/الفرع.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إنشاء علاقات عديدة-إلى-عدة؟">
|
||||
العلاقات عديدة-إلى-عديدة قادمة في **النصف الأول من عام 2026**. حاليًا، أنشئ كائنًا وسيطًا مع علاقتين واحد-إلى-عديد كحل بديل.
|
||||
<Accordion title="هل يمكنني إنشاء علاقات عديدة-إلى-عدة؟">
|
||||
العلاقات عديدة-إلى-عديدة قادمة في **النصف الأول من عام 2026**. حاليًا، أنشئ كائنًا وسيطًا مع علاقتين واحد-إلى-عديد كحل بديل.
|
||||
|
||||
على سبيل المثال، لربط People وProjects (عديد-إلى-عديد)، أنشئ كائنًا باسم "Project Assignments" يحتوي على:
|
||||
على سبيل المثال، لربط People وProjects (عديد-إلى-عديد)، أنشئ كائنًا باسم "Project Assignments" يحتوي على:
|
||||
* علاقة إلى People (عديد من التعيينات → شخص واحد)
|
||||
* علاقة إلى Projects (عديد من التعيينات → مشروع واحد)
|
||||
</Accordion>
|
||||
|
||||
* علاقة إلى People (عديد من التعيينات → شخص واحد)
|
||||
* علاقة إلى Projects (عديد من التعيينات → مشروع واحد)
|
||||
</Accordion>
|
||||
<Accordion title="ما هي العلاقات إلى أنواع كائنات متعددة (علاقات Morph)؟">
|
||||
تتيح هذه أن يرتبط كائن واحد بأنواع كائنات متعددة مختلفة عبر حقل واحد. على سبيل المثال، يمكن إرفاق Notes بـ People وCompanies وOpportunities في الوقت نفسه.
|
||||
|
||||
<Accordion title="ما هي العلاقات إلى أنواع كائنات متعددة (علاقات Morph)؟">
|
||||
تتيح هذه أن يرتبط كائن واحد بأنواع كائنات متعددة مختلفة عبر حقل واحد. على سبيل المثال، يمكن إرفاق Notes بـ People وCompanies وOpportunities في الوقت نفسه.
|
||||
يرتبط كل Note بشخص واحد وشركة واحدة وفرصة واحدة في الوقت نفسه.
|
||||
|
||||
يرتبط كل Note بشخص واحد وشركة واحدة وفرصة واحدة في الوقت نفسه.
|
||||
اعرف المزيد في [حقول العلاقات](/l/ar/user-guide/data-model/capabilities/relation-fields).
|
||||
</Accordion>
|
||||
|
||||
اعرف المزيد في [حقول العلاقات](/l/ar/user-guide/data-model/capabilities/relation-fields).
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إنشاء علاقات متعددة بين الكائنات نفسها؟">
|
||||
نعم، يمكنك إنشاء علاقات متعددة بين الكائنين نفسيهما. على سبيل المثال، يمكن أن يكون لدى Company علاقتا "Primary Contact" و"Billing Contact" مع People.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إنشاء علاقات متعددة بين الكائنات نفسها؟">
|
||||
نعم، يمكنك إنشاء علاقات متعددة بين الكائنين نفسيهما. على سبيل المثال، يمكن أن يكون لدى Company علاقتا "Primary Contact" و"Billing Contact" مع People.
|
||||
</Accordion>
|
||||
<Accordion title="ماذا يحدث للعلاقات عند حذف سجل؟">
|
||||
عند حذف سجل، تتم إزالة رابط العلاقة من السجلات المرتبطة. السجلات المرتبطة نفسها لا تُحذف.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ماذا يحدث للعلاقات عند حذف سجل؟">
|
||||
عند حذف سجل، تتم إزالة رابط العلاقة من السجلات المرتبطة. السجلات المرتبطة نفسها لا تُحذف.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني إنشاء علاقات دائرية؟">
|
||||
على الرغم من أنها ممكنة تقنيًا، يجب تجنّب العلاقات الدائرية (A → B → C → A) لأنها قد تسبب التباسًا ومشكلات محتملة في الأداء.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني إنشاء علاقات دائرية؟">
|
||||
على الرغم من أنها ممكنة تقنيًا، يجب تجنّب العلاقات الدائرية (A → B → C → A) لأنها قد تسبب التباسًا ومشكلات محتملة في الأداء.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## الوصول والصلاحيات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="أين يمكنني رؤية وتعديل نموذج البيانات الخاص بي؟">
|
||||
انتقل إلى **الإعدادات → نموذج البيانات** لعرض جميع الكائنات والحقول الخاصة بك وتحريرها.
|
||||
</Accordion>
|
||||
<Accordion title="أين يمكنني رؤية وتعديل نموذج البيانات الخاص بي؟">
|
||||
انتقل إلى **الإعدادات → نموذج البيانات** لعرض جميع الكائنات والحقول الخاصة بك وتحريرها.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="لماذا لا أستطيع رؤية نموذج البيانات تحت الإعدادات؟">
|
||||
اتصل بمسؤول المساحة لديك. يكون الوصول إلى نموذج البيانات عادة محظورًا على المسؤولين فقط.
|
||||
</Accordion>
|
||||
<Accordion title="لماذا لا أستطيع رؤية نموذج البيانات تحت الإعدادات؟">
|
||||
اتصل بمسؤول المساحة لديك. يكون الوصول إلى نموذج البيانات عادة محظورًا على المسؤولين فقط.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## إدارة البيانات
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="هل هناك حد لعدد السجلات التي يمكنني امتلاكها؟">
|
||||
لا يوجد حد صارم لعدد السجلات. ومع ذلك، قد تؤثر مجموعات البيانات الكبيرة جدًا في الأداء في بعض طرق العرض. استخدم عوامل التصفية وطرق العرض لإدارة مجموعات البيانات الكبيرة بفعالية.
|
||||
</Accordion>
|
||||
<Accordion title="هل هناك حد لعدد السجلات التي يمكنني امتلاكها؟">
|
||||
لا يوجد حد صارم لعدد السجلات. ومع ذلك، قد تؤثر مجموعات البيانات الكبيرة جدًا في الأداء في بعض طرق العرض. استخدم عوامل التصفية وطرق العرض لإدارة مجموعات البيانات الكبيرة بفعالية.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني استيراد بيانات إلى الكائنات المخصصة؟">
|
||||
نعم، يمكنك استيراد بيانات CSV إلى أي كائن، بما في ذلك الكائنات المخصصة. تدعم عملية الاستيراد تعيين الحقول للحقول المخصصة. اطّلع على [كيفية إعداد ملفات CSV الخاصة بك](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files).
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني استيراد بيانات إلى الكائنات المخصصة؟">
|
||||
نعم، يمكنك استيراد بيانات CSV إلى أي كائن، بما في ذلك الكائنات المخصصة. تدعم عملية الاستيراد تعيين الحقول للحقول المخصصة. اطّلع على [كيفية إعداد ملفات CSV الخاصة بك](/l/ar/user-guide/data-migration/how-tos/prepare-your-csv-files).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="هل يمكنني تصدير تكوين نموذج البيانات الخاص بي؟">
|
||||
حاليًا، لا يوجد تصدير مدمج لتكوين نموذج البيانات. تواصل مع الدعم إذا كنت بحاجة إلى نقل نموذج البيانات بين مساحات العمل.
|
||||
</Accordion>
|
||||
<Accordion title="هل يمكنني تصدير تكوين نموذج البيانات الخاص بي؟">
|
||||
حاليًا، لا يوجد تصدير مدمج لتكوين نموذج البيانات. تواصل مع الدعم إذا كنت بحاجة إلى نقل نموذج البيانات بين مساحات العمل.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## هل تحتاج إلى المزيد من المساعدة؟
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user