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

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

View File

@@ -0,0 +1,22 @@
---
title: Meilleures pratiques
---
Ce document décrit les meilleures pratiques à suivre lors de travaux sur le backend.
## Suivez une approche modulaire
Le backend suit une approche modulaire, qui est un principe fondamental lors de l'utilisation de NestJS. Assurez-vous de décomposer votre code en modules réutilisables pour maintenir une base de code propre et organisée.
Chaque module doit encapsuler une fonctionnalité particulière et avoir un périmètre bien défini. Cette approche modulaire permet une séparation claire des préoccupations et supprime les complexités inutiles.
## Exposez des services à utiliser dans les modules
Créez toujours des services avec une responsabilité claire et unique, ce qui améliore la lisibilité et la maintenabilité du code. Nommez les services de manière descriptive et cohérente.
Vous devez également exposer les services que vous souhaitez utiliser dans d'autres modules. Exposer des services à d'autres modules est possible grâce au puissant système d'injection de dépendance de NestJS, et favorise un couplage lâche entre les composants.
## Évitez d'utiliser le type `any`
Lorsque vous déclarez une variable comme `any`, le vérificateur de types de TypeScript ne procède à aucune vérification de type, ce qui permet d'affecter n'importe quel type de valeur à la variable. TypeScript utilise l'inférence de type pour déterminer le type d'une variable en fonction de sa valeur. En le déclarant comme `any`, TypeScript ne peut plus inférer le type. Cela rend difficile la détection des erreurs liées aux types pendant le développement, conduisant à des erreurs d'exécution et rendant le code moins maintenable, moins fiable et plus difficile à comprendre pour d'autres.
C'est pourquoi tout devrait avoir un type. Donc si vous créez un nouvel objet avec un prénom et un nom, vous devriez créer une interface ou un type contenant un prénom et un nom qui définit la forme de l'objet que vous manipulez.

View File

@@ -0,0 +1,39 @@
---
title: Objets personnalisés
---
Les objets sont des structures qui vous permettent de stocker des données (enregistrements, attributs et valeurs) spécifiques à une organisation. Twenty fournit à la fois des objets standard et personnalisés.
Les objets standard sont des objets intégrés avec un ensemble d'attributs disponibles pour tous les utilisateurs. Les exemples d'objets standard dans Twenty incluent Société et Personne. Les objets standard ont des champs standard également disponibles pour tous les utilisateurs de Twenty, comme Company.displayName.
Les objets personnalisés sont des objets que vous pouvez créer pour stocker des informations uniques à votre organisation. Ils ne sont pas intégrés ; les membres de votre espace de travail peuvent créer et personnaliser des objets personnalisés pour contenir des informations que les objets standard ne conviennent pas.
## Schéma de haut niveau
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Schéma de haut niveau" />
</div>
<br />
## Comment ça marche
Les objets personnalisés proviennent de tables de métadonnées qui déterminent la forme, le nom et le type des objets. Toutes ces informations sont présentes dans la base de données des schémas de métadonnées, composée de tables :
* **DataSource** : Détaille où les données sont présentes.
* **Objet** : Décrit l'objet et lie à une DataSource.
* **Champ** : Décrit les champs d'un objet et se connecte à l'objet.
Pour ajouter un objet personnalisé, le workspaceMember interrogera l'API /metadata. Cela met à jour les métadonnées en conséquence et calcule un schéma GraphQL basé sur les métadonnées, en le stockant dans un cache GQL pour une utilisation ultérieure.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/add-custom-objects.jpeg" alt="Interroger l'API /metadata pour ajouter des objets personnalisés" />
</div>
<br />
Pour obtenir des données, le processus implique de faire des requêtes via le point de terminaison /graphql et de les transmettre via le Query Resolver.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Interroger le point de terminaison /graphql pour obtenir des données" />
</div>

View File

@@ -0,0 +1,46 @@
---
title: Drapeaux de fonctionnalité
---
Les drapeaux de fonctionnalité sont utilisés pour masquer les fonctionnalités expérimentales. Pour Twenty, ils sont définis au niveau de l'espace de travail et non au niveau de l'utilisateur.
## Ajout d'un nouveau drapeau de fonctionnalité
Dans `FeatureFlagKey.ts` ajoutez l'indicateur de fonctionnalité :
```ts
type FeatureFlagKey =
| 'IS_FEATURENAME_ENABLED'
| ...;
```
Ajoutez-le également à l'énumération dans `feature-flag.entity.ts` :
```ts
enum FeatureFlagKeys {
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
...
}
```
Pour appliquer un drapeau de fonctionnalité à une fonctionnalité **back-end**, utilisez :
```ts
@Gate({
featureFlag: 'IS_FEATURENAME_ENABLED',
})
```
Pour appliquer un drapeau de fonctionnalité à une fonctionnalité **front-end**, utilisez :
```ts
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
```
## Configurer les drapeaux de fonctionnalité pour le déploiement
Modifiez l'enregistrement correspondant dans la Table `core.featureFlag` :
| iD | clé | workspaceId | valeur |
| --------- | ------------------------ | ----------- | ------ |
| Aléatoire | `IS_FEATURENAME_ENABLED` | WorkspaceID | `vrai` |

View File

@@ -0,0 +1,125 @@
---
title: Architecture des Dossiers
info: Un regard détaillé sur l'architecture des dossiers de notre serveur
---
La structure du répertoire backend est la suivante :
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
```
## Compétence
Définit les permissions et inclut des gestionnaires pour chaque entité.
## Décorateurs
Définit des décorateurs personnalisés dans NestJS pour des fonctionnalités supplémentaires.
Voir [décorateurs personnalisés](https://docs.nestjs.com/custom-decorators) pour plus de détails.
## Filtres
Inclut des filtres d'exception pour gérer les exceptions qui pourraient se produire dans les points de terminaison GraphQL.
## Gardes
Voir [gardiens](https://docs.nestjs.com/guards) pour plus de détails.
## Santé
Inclut une API REST disponible publiquement (healthz) qui renvoie un JSON pour confirmer si la base de données fonctionne comme prévu.
## Métadonnées
Définit des objets personnalisés et met une API GraphQL à disposition (graphql/metadata).
## Espace de travail
Génère et sert un schéma GraphQL personnalisé basé sur les métadonnées.
### Structure du répertoire de l'espace de travail
```
workspace
└───workspace-schema-builder
└───factories
└───graphql-types
└───database
└───interfaces
└───object-definitions
└───services
└───storage
└───utils
└───workspace-resolver-builder
└───factories
└───interfaces
└───workspace-query-builder
└───factories
└───interfaces
└───workspace-query-runner
└───interfaces
└───utils
└───workspace-datasource
└───workspace-manager
└───workspace-migration-runner
└───utils
└───workspace.module.ts
└───workspace.factory.spec.ts
└───workspace.factory.ts
```
La racine du répertoire de l'espace de travail inclut le fichier `workspace.factory.ts`, qui contient la fonction `createGraphQLSchema`. Cette fonction génère un schéma spécifique à l'espace de travail en utilisant les métadonnées pour adapter un schéma pour chaque espace de travail. En séparant la construction du schéma et du résolveur, nous utilisons la fonction `makeExecutableSchema`, qui combine ces éléments distincts.
Cette stratégie n'est pas seulement une question d'organisation, mais aide également à l'optimisation, comme la mise en cache des définitions de type générées pour améliorer les performances et la scalabilité.
### Constructeur de schéma d'espace de travail
Génère le schéma GraphQL, et inclut :
#### Usines :
Constructeurs spécialisés pour générer des constructions liées à GraphQL.
* La type.factory traduit les métadonnées de champs en types GraphQL en utilisant le `TypeMapperService`.
* La type-definition.factory crée des objets d'entrée ou de sortie GraphQL dérivés de `objectMetadata`.
#### Types GraphQL
Inclut des énumérations, des entrées, des objets et des scalaires, et sert de blocs de construction pour la construction du schéma.
#### Interfaces et définitions d'objets
Contient les plans pour les entités GraphQL, et inclut à la fois des types prédéfinis et personnalisés comme `MONEY` ou `URL`.
#### Services
Contient le service responsable de l'association du FieldMetadataType avec son type scalaire ou modificateur de requête GraphQL approprié.
#### Stockage
Inclut la classe `TypeDefinitionsStorage` qui contient des définitions de type réutilisables, empêchant la duplication des types GraphQL.
### Constructeur de résolveur d'espace de travail
Crée des fonctions de résolveur pour interroger et modifier le schéma GraphQL.
Chaque usine de ce répertoire est responsable de la production d'un type de résolveur distinct, comme le `FindManyResolverFactory`, conçu pour une application adaptable à plusieurs tables.
### Exécuteur de requêtes d'espace de travail
Exécute les requêtes générées sur la base de données et analyse le résultat.

View File

@@ -0,0 +1,41 @@
---
title: File d'attente de messages
---
Les files d'attente facilitent les opérations asynchrones. Elles peuvent être utilisées pour effectuer des tâches de fond telles que l'envoi d'un e-mail de bienvenue lors de l'enregistrement.
Chaque cas d'utilisation aura sa propre classe de file d'attente étendue à partir de `MessageQueueServiceBase`.
Actuellement, nous ne supportons que `bull-mq`[bull-mq](https://bullmq.io/) comme pilote de file d'attente.
## Étapes pour créer et utiliser une nouvelle file d'attente
1. Ajoutez un nom de file d'attente pour votre nouvelle file sous l'énumération `MESSAGE_QUEUES`.
2. Fournissez l'implémentation de la fabrique de la file d'attente avec le nom de la file comme dépendance.
3. Injectez la file d'attente que vous avez créée dans le module/service requis en utilisant le nom de la file comme dépendance.
4. Ajoutez une classe worker avec une injection basée sur des jetons, comme pour le producteur.
### Exemple d'utilisation
```ts
class Resolver {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {}
async onSomeAction() {
//business logic
await this.queue.add(someData);
}
}
//async worker
class CustomWorker {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {
this.initWorker();
}
async initWorker() {
await this.queue.work(async ({ id, data }) => {
//worker logic
});
}
}
```

View File

@@ -0,0 +1,87 @@
---
title: Commandes Backend
icon: terminal
---
## Commandes utiles
Ces commandes doivent être exécutées depuis le dossier packages/twenty-server.
Depuis n'importe quel autre dossier, vous pouvez exécuter `npx nx {command} twenty-server` (ou `npx nx run twenty-server:{command}`).
### Configuration initiale
```
npx nx database:reset twenty-server # setup the database with dev seeds
```
### Démarrer le serveur
```
npx nx run twenty-server:start
```
### Analyse
```
npx nx run twenty-server:lint # pass --fix to fix lint errors
```
### Test
```
npx nx run twenty-server:test:unit # run unit tests
npx nx run twenty-server:test:integration # run integration tests
```
Remarque: vous pouvez exécuter `npx nx run twenty-server:test:integration:with-db-reset` si vous avez besoin de réinitialiser la base de données avant de lancer les tests d'intégration.
### Réinitialiser la base de données
Si vous souhaitez réinitialiser et peupler la base de données, vous pouvez exécuter la commande suivante :
```bash
npx nx run twenty-server:database:reset
```
### Migrations
#### Pour les objets dans les schémas Core/Metadata (TypeORM)
```bash
npx nx run twenty-server:database:migrate:generate
```
## Écosystème Tech
Twenty utilise principalement NestJS pour le backend.
Prisma a été le premier ORM que nous avons utilisé. Mais pour permettre aux utilisateurs de créer des champs et objets personnalisés, un niveau inférieur avait plus de sens car nous avons besoin d'un contrôle granulaire. Le projet utilise maintenant TypeORM.
Voici à quoi ressemble maintenant la pile technologique.
**Noyau**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
**Base de données**
* [Postgres](https://www.postgresql.org/)
**Intégrations tierces**
* [Sentry](https://sentry.io/welcome/) pour suivre les bugs.
**Tests**
* [Jest](https://jestjs.io/)
**Outils**
* [Yarn](https://yarnpkg.com/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
**Développement**
* [AWS EKS](https://aws.amazon.com/eks/)

View File

@@ -0,0 +1,85 @@
---
title: Application Zapier
---
Synchronisez facilement Twenty avec plus de 3000 applications à l'aide de [Zapier](https://zapier.com/). Automatisez les tâches, boostez la productivité et dynamisez vos relations client !
## À propos de Zapier
Zapier est un outil qui vous permet d'automatiser des flux de travail en connectant les applications que votre équipe utilise quotidiennement. Le concept fondamental de Zapier est l'automatisation des flux de travail, appelés Zaps, et inclut des déclencheurs et des actions.
Vous pouvez en savoir plus sur le fonctionnement de Zapier [ici](https://zapier.com/how-it-works).
## Installation
### Étape 1 : Installez les packages Zapier
```bash
cd packages/twenty-zapier
yarn
```
### Étape 2 : Connectez-vous avec le CLI
Utilisez vos identifiants Zapier pour vous connecter en utilisant le CLI :
```bash
zapier login
```
### Étape 3 : Configurer les variables d'environnement
Depuis le dossier `packages/twenty-zapier`, exécutez :
```bash
cp .env.example .env
```
Exécutez l'application localement, rendez-vous sur [http://localhost:3000/settings/api-webhooks](http://localhost:3000/settings/api-webhooks) et générez une clé API.
Remplacez la valeur **YOUR_API_KEY** dans le fichier `.env` par la clé API que vous venez de générer.
## Développement
<Warning>
Assurez-vous d'exécuter `yarn build` avant toute commande `zapier`.
</Warning>
### Test
```bash
yarn test
```
### Analyse
```bash
yarn format
```
### Surveillez et compilez pendant que vous éditez le code
```bash
yarn watch
```
### Validez votre application Zapier
```bash
yarn validate
```
### Déployez votre application Zapier
```bash
yarn deploy
```
### Liste de toutes les commandes CLI de Zapier
```bash
zapier
```

View File

@@ -0,0 +1,79 @@
---
title: Signalements de bogues, demandes et pull requests
icon: bug
info: Signalez des problèmes, demandez des fonctionnalités et contribuez au code
---
## Signaler des bogues
Pour signaler un bogue, veuillez [créer un problème sur GitHub](https://github.com/twentyhq/twenty/issues/new).
Vous pouvez également demander de l'aide sur [Discord](https://discord.gg/cx5n4Jzs57).
## Demandes de fonctionnalités
Si vous n'êtes pas sûr qu'il s'agisse d'un bogue, et que vous pensez que cela ressemble plus à une demande de fonctionnalité, alors vous devriez probablement [ouvrir une discussion à la place](https://github.com/twentyhq/twenty/discussions/new).
## Soumettre une pull request
La contribution au code de Twenty commence par une pull request (PR).
### Avant de démarrer
1. Consultez les [problèmes existants](https://github.com/twentyhq/twenty/issues) pour repérer les travaux liés
2. Pour les nouvelles fonctionnalités, ouvrez d’abord un problème pour en discuter
3. Consultez notre [Code de conduite](https://github.com/twentyhq/twenty/blob/main/.github/CODE_OF_CONDUCT.md)
### Forker et cloner
1. Forkez le dépôt sur GitHub
2. Clonez votre fork:
```bash
git clone https://github.com/YOUR_USERNAME/twenty.git
cd twenty
```
3. Ajoutez le remote upstream:
```bash
git remote add upstream https://github.com/twentyhq/twenty.git
```
### Créer une branche
```bash
git checkout -b feature/your-feature-name
```
Utilisez des noms de branche descriptifs:
* `feature/add-export-button`
* `fix/login-redirect-issue`
* `docs/update-api-guide`
### Apportez vos modifications
1. Écrivez un code propre et bien documenté
2. Suivez le style de code existant
3. Ajoutez des tests pour les nouvelles fonctionnalités
4. Mettez à jour la documentation si nécessaire
### Soumettez votre PR
1. Poussez votre branche:
```bash
git push origin feature/your-feature-name
```
2. Ouvrez une PR sur GitHub
3. Remplissez le modèle de PR
4. Liez les problèmes associés
### Liste de contrôle de la PR
* [ ] Le code respecte les directives de style du projet
* [ ] Les tests réussissent localement
* [ ] La documentation est à jour
* [ ] La description de la PR explique les modifications

View File

@@ -0,0 +1,329 @@
---
title: Meilleures pratiques
icon: star
---
Ce document décrit les meilleures pratiques à suivre lors de votre travail sur l'interface frontend.
## Gestion de l'état
React et Jotai gèrent la gestion de l'état dans la base de code.
### Utilisez les atomes Jotai pour stocker l'état
C'est une bonne pratique de créer autant d'atomes que nécessaire pour stocker votre état.
<Warning>
Il vaut mieux utiliser des atomes supplémentaires plutôt que d'essayer d'être trop concis en perçant les propriétés.
</Warning>
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
export const myAtomState = createAtomState<string>({
key: 'myAtomState',
defaultValue: 'default value',
});
export const MyComponent = () => {
const [myAtom, setMyAtom] = useAtomState(myAtomState);
return (
<div>
<input
value={myAtom}
onChange={(e) => setMyAtom(e.target.value)}
/>
</div>
);
}
```
### Ne pas utiliser `useRef` pour stocker l'état
Évitez d'utiliser `useRef` pour stocker l'état.
Si vous souhaitez stocker un état, vous devez utiliser `useState` ou des atomes Jotai avec `useAtomState`.
Consultez [comment gérer les re-rendus](#managing-re-renders) si vous avez l'impression d'avoir besoin de `useRef` pour empêcher certains re-rendus.
## Gestion des re-rendus
Les re-rendus peuvent être difficiles à gérer dans React.
Voici quelques règles à suivre pour éviter les re-rendus inutiles.
Gardez à l'esprit que vous pouvez **toujours** éviter les re-rendus en comprenant leur cause.
### Travaillez au niveau racine
Éviter les re-rendus dans les nouvelles fonctionnalités est désormais facile en les éliminant au niveau racine.
Le composant latéral `PageChangeEffect` contient juste un `useEffect` qui contient toute la logique à exécuter sur un changement de page.
De cette manière, vous savez qu'il n'y a qu'un seul endroit qui peut déclencher un re-rendu.
### Réfléchissez toujours à deux fois avant d'ajouter `useEffect` dans votre code de base
Les re-rendus sont souvent causés par un `useEffect` inutile.
Vous devriez vous demander si vous avez besoin de `useEffect`, ou si vous pouvez déplacer la logique dans une fonction de gestion d'événements.
Vous trouverez généralement facile de déplacer la logique dans une fonction `handleClick` ou `handleChange`.
Vous pouvez également les trouver dans des bibliothèques comme Apollo : `onCompleted`, `onError`, etc.
### Utilisez un composant frère pour extraire `useEffect` ou une logique de récupération de données
Si vous avez le sentiment que vous avez besoin d'ajouter un `useEffect` à votre composant racine, vous devriez envisager de l'extraire dans un composant de voiture latérale.
Vous pouvez appliquer la même chose à la logique de récupération de données, avec les hooks Apollo.
```tsx
// ❌ Bad, will cause re-renders even if data is not changing,
// because useEffect needs to be re-evaluated
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <div>{data}</div>;
};
export const App = () => (
<PageComponent />
);
```
```tsx
// ✅ Good, will not cause re-renders if data is not changing,
// because useEffect is re-evaluated in another sibling component
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
return <div>{data}</div>;
};
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <></>;
};
export const App = () => (
<>
<PageData />
<PageComponent />
</>
);
```
### Utilisez les familles d'atomes et les sélecteurs
Les familles d'atomes et les sélecteurs sont un excellent moyen d'éviter les re-rendus.
Ils sont utiles lorsque vous devez stocker une liste d'articles.
### Vous ne devriez pas utiliser `React.memo(MyComponent)`
Évitez d'utiliser `React.memo()` car cela ne résout pas la cause du re-rendu, mais brise plutôt la chaîne de re-rendu, ce qui peut entraîner un comportement inattendu et rendre le code très difficile à refactoriser.
### Limitez l'utilisation de `useCallback` ou `useMemo`
Ils ne sont souvent pas nécessaires et rendront le code plus difficile à lire et à maintenir pour un gain de performance imperceptible.
## Console.logs
Les déclarations `console.log` sont précieuses pendant le développement, offrant des insights en temps réel sur les valeurs de variables et le flux de code. Mais, les laisser dans le code de production peut entraîner plusieurs problèmes :
1. **Performance** : Un journalisation excessive peut affecter les performances d'exécution, notamment sur les applications côté client.
2. **Sécurité** : La journalisation de données sensibles peut exposer des informations critiques à toute personne qui inspecte la console du navigateur.
3. **Propreté** : Remplir la console de logs peut masquer les avertissements ou erreurs importants que les développeurs ou outils doivent voir.
4. **Professionnalisme** : Les utilisateurs finaux ou clients vérifiant la console et voyant une myriade de déclarations de log pourraient remettre en question la qualité et la finition du code.
Assurez-vous de supprimer tous les `console.logs` avant de pousser le code en production.
## Nommage
### Nom des variables
Les noms de variables doivent décrire précisément l'objectif ou la fonction de la variable.
#### Le problème avec les noms génériques
Les noms génériques en programmation ne sont pas idéaux car ils manquent de spécificité, ce qui conduit à une ambiguïté et réduit la lisibilité du code. De tels noms ne parviennent pas à transmettre l'objectif de la variable ou de la fonction, rendant difficile pour les développeurs de comprendre l'intention du code sans une enquête plus approfondie. Cela peut entraîner un temps de débogage accru, une plus grande vulnérabilité aux erreurs et des difficultés de maintenance et de collaboration. Pendant ce temps, des noms descriptifs rendent le code explicite et plus facile à naviguer, améliorant la qualité du code et la productivité des développeurs.
```tsx
// ❌ Bad, uses a generic name that doesn't communicate its
// purpose or content clearly
const [value, setValue] = useState('');
```
```tsx
// ✅ Good, uses a descriptive name
const [email, setEmail] = useState('');
```
#### Certains mots à éviter dans les noms de variables
* factice
### Gestionnaires d'événements
Les noms des gestionnaires d'événements doivent commencer par `handle`, tandis que `on` est un préfixe utilisé pour nommer les événements dans les propriétés des composants.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## Props optionnels
Évitez de passer la valeur par défaut pour un prop optionnel.
**EXEMPLE**
Prenez le composant`EmailField` défini ci-dessous :
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
<TextInput value={value} disabled={disabled} fullWidth />
);
```
**Utilisation**
```tsx
// ❌ Bad, passing in the same value as the default value adds no value
const Form = () => <EmailField value="username@email.com" disabled={false} />;
```
```tsx
// ✅ Good, assumes the default value
const Form = () => <EmailField value="username@email.com" />;
```
## Composant en tant que props
Essayez autant que possible de transmettre des composants non instanciés comme props, afin que les enfants puissent décider eux-mêmes des props qu'ils ont besoin de passer.
L'exemple le plus courant pour cela est les composants icône :
```tsx
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
// In MyComponent
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
const theme = useTheme();
return (
<div>
<MyIcon size={theme.icon.size.md}>
</div>
)
};
```
Pour que React comprenne qu'un composant est un composant, vous devez utiliser PascalCase, pour l'instancier plus tard avec `<MyIcon>`
## Forage de Props : Gardez-le Minimal
Le forage de props, dans le contexte de React, fait référence à la pratique consistant à passer des variables d'état et leurs setters à travers de nombreuses couches de composants, même si les composants intermédiaires ne les utilisent pas. Bien que parfois nécessaire, un forage de props excessif peut entraîner :
1. **Lisibilité Réduite** : Retrouver l'origine d'un prop ou l'endroit où il est utilisé peut devenir complexe dans une structure de composants profondément imbriquée.
2. **Défis de Maintenance** : Des modifications dans la structure des props d'un composant peuvent nécessiter des ajustements dans plusieurs composants, même s'ils n'utilisent pas directement le prop.
3. **Réutilisabilité Réduite du Composant** : Un composant recevant beaucoup de props uniquement pour les transmettre devient moins polyvalent et plus difficile à réutiliser dans différents contextes.
Si vous sentez que vous utilisez un forage de props excessif, voir [les meilleures pratiques de gestion d'état](#state-management).
## Imports
Lors de l'importation, optez pour les alias désignés plutôt que de spécifier des chemins complets ou relatifs.
**Les Alias**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**Utilisation**
```tsx
// ❌ Bad, specifies the entire relative path
import {
CatalogDecorator
} from '../../../../../testing/decorators/CatalogDecorator';
import {
ComponentDecorator
} from '../../../../../testing/decorators/ComponentDecorator';
```
```tsx
// ✅ Good, utilises the designated aliases
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from 'twenty-ui/testing';
```
## Validation de Schéma
[Zod](https://github.com/colinhacks/zod) est le validateur de schéma pour les objets non typés:
```js
const validationSchema = z
.object({
exist: z.boolean(),
email: z
.string()
.email('Email must be a valid email'),
password: z
.string()
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
})
.required();
type Form = z.infer<typeof validationSchema>;
```
## Changements Radicaux
Effectuez toujours des tests manuels approfondis avant de continuer pour garantir que les modifications n'ont pas causé de perturbations ailleurs, étant donné que les tests n'ont pas encore été intégrés de manière extensive.

View File

@@ -0,0 +1,110 @@
---
title: Architecture des Dossiers
icon: folder-tree
info: Un aperçu détaillé de notre architecture de dossiers
---
Dans ce guide, vous explorerez les détails de la structure du répertoire de projet et comment elle contribue à l'organisation et à la maintenabilité de Twenty.
En suivant cette convention d'architecture de dossiers, il est plus facile de trouver les fichiers liés à des fonctionnalités spécifiques et de s'assurer que l'application est évolutive et maintenable.
```
front
└───modules
│ └───module1
│ │ └───submodule1
│ └───module2
│ └───ui
│ │ └───display
│ │ └───inputs
│ │ │ └───buttons
│ │ └───...
└───pages
└───...
```
## Pages
Comprend les composants de haut niveau définis par les routes de l'application. Ils importent des composants de plus bas niveau depuis le dossier `modules` (plus de détails ci-dessous).
## Modules
Chaque module représente une fonctionnalité ou un groupe de fonctionnalités, comprenant ses composants spécifiques, ses états, et sa logique opérationnelle.
Ils doivent tous suivre la structure ci-dessous. Vous pouvez imbriquer des modules dans des modules (appelés sous-modules) et les mêmes règles s'appliqueront.
```
module1
└───components
│ └───component1
│ └───component2
└───constants
└───contexts
└───graphql
│ └───fragments
│ └───queries
│ └───mutations
└───hooks
│ └───internal
└───states
│ └───selectors
└───types
└───utils
```
### Contextes
Un contexte est un moyen de transmettre des données à travers l'arborescence de composants sans avoir à transmettre les propriétés manuellement à chaque niveau.
Voir [React Context](https://react.dev/reference/react#context-hooks) pour plus de détails.
### GraphQL
Comprend des fragments, des requêtes et des mutations.
Voir [GraphQL](https://graphql.org/learn/) pour plus de détails.
* Fragments
Un fragment est une partie réutilisable d'une requête, que vous pouvez utiliser dans différents endroits. En utilisant des fragments, il est plus facile d'éviter de dupliquer du code.
Voir [GraphQL Fragments](https://graphql.org/learn/queries/#fragments) pour plus de détails.
* Requêtes
Voir [GraphQL Queries](https://graphql.org/learn/queries/) pour plus de détails.
* Mutations
Voir [GraphQL Mutations](https://graphql.org/learn/queries/#mutations) pour plus de détails.
### Hooks
Voir [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) pour plus de détails.
### États
Contient la logique de gestion des états. [Jotai](https://jotai.org) gère cela.
* Sélecteurs : Les atomes dérivés (à l'aide de `createAtomSelector`) calculent des valeurs à partir d'autres atomes et sont automatiquement mémoïsés.
La gestion de l'état intégrée de React gère toujours l'état au sein d'un composant.
### Utilitaires
Devrait juste contenir des fonctions pures réutilisables. Autrement, créez des hooks personnalisés dans le dossier `hooks`.
## UI
Contient tous les composants d'interface utilisateur réutilisables utilisés dans l'application.
Ce dossier peut contenir des sous-dossiers, comme `data`, `display`, `feedback`, et `input` pour des types de composants spécifiques. Chaque composant doit être autonome et réutilisable, de sorte que vous puissiez l'utiliser dans différentes parties de l'application.
En séparant les composants UI des autres composants dans le dossier `modules`, il est plus facile de maintenir un design cohérent et d'effectuer des changements de l'interface utilisateur sans affecter d'autres parties (logique métier) de la base de code.
## Interface et dépendances
Vous pouvez importer le code d'autres modules depuis n'importe quel module, sauf le dossier `ui`. Cela permettra de garder son code facile à tester.
### Interne
Chaque partie (hooks, états, ...) d'un module peut avoir un dossier `internal`, qui contient des parties utilisées uniquement au sein du module.

View File

@@ -0,0 +1,91 @@
---
title: Commandes Frontend
icon: terminal
---
## Commandes utiles
### Lancement de l'application
```bash
npx nx start twenty-front
```
### Régénérer le schéma GraphQL basé sur le schéma API GraphQL
```bash
npx nx run twenty-front:graphql:generate --configuration=metadata
```
OU
```bash
npx nx run twenty-front:graphql:generate
```
### Analyse
```bash
npx nx run twenty-front:lint # pass --fix to fix lint errors
```
## Traductions
```bash
npx nx run twenty-front:lingui:extract
npx nx run twenty-front:lingui:compile
```
### Test
```bash
npx nx run twenty-front:test # run jest tests
npx nx run twenty-front:storybook:serve:dev # run storybook
npx nx run twenty-front:storybook:test # run tests # (needs yarn storybook:serve:dev to be running)
npx nx run twenty-front:storybook:coverage # (needs yarn storybook:serve:dev to be running)
```
## Écosystème Tech
Le projet a une stack simple et propre, avec un code boilerplate minimal.
**Application**
* [React](https://react.dev/)
* [Apollo](https://www.apollographql.com/docs/)
* [GraphQL Codegen](https://the-guild.dev/graphql/codegen)
* [Jotai](https://jotai.org/)
* [TypeScript](https://www.typescriptlang.org/)
**Tests**
* [Jest](https://jestjs.io/)
* [Storybook](https://storybook.js.org/)
**Outils**
* [Yarn](https://yarnpkg.com/)
* [Craco](https://craco.js.org/docs/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
## Architecture
### Routage
[React Router](https://reactrouter.com/) gère le routage.
Pour éviter les [re-renders](/l/fr/developers/contribute/capabilities/frontend-development/best-practices-front#managing-re-renders) inutiles toute la logique de routage est dans un `useEffect` dans `PageChangeEffect`.
### Gestion de l'État
[Jotai](https://jotai.org/) gère la gestion de l'état.
Voir [les meilleures pratiques](/l/fr/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) pour plus d'informations sur la gestion de l'état.
## Tests
[Jest](https://jestjs.io/) sert de guide pour les tests unitaires tandis que [Storybook](https://storybook.js.org/) est utilisé pour les tests de composants.
Jest est principalement utilisé pour tester les fonctions utilitaires, et non les composants eux-mêmes.
Storybook est utilisé pour tester le comportement des composants isolés, ainsi que pour afficher le système de design.

View File

@@ -0,0 +1,178 @@
---
title: Raccourcis clavier
---
## Introduction
Lorsque vous devez écouter une touche de raccourci, vous utilisez normalement l'événement `onKeyDown`.
Cependant, dans `twenty-front`, vous pourriez avoir des conflits entre les mêmes raccourcis utilisés dans différents composants, montés en même temps.
Par exemple, si vous avez une page qui écoute la touche Entrée, et une fenêtre modale qui écoute aussi la touche Entrée, avec un composant Select à l'intérieur de cette fenêtre qui écoute également la touche Entrée, vous risquez d'avoir un conflit lorsque tous sont montés en même temps.
## Le hook `useScopedHotkeys`
Pour résoudre ce problème, nous avons un hook personnalisé qui permet d'écouter les raccourcis sans aucun conflit.
Vous l'insérez dans un composant et il écoutera les raccourcis uniquement lorsque le composant est monté ET lorsque le **périmètre du raccourci** spécifié est actif.
## Comment écouter les raccourcis en pratique ?
Deux étapes sont nécessaires pour configurer l'écoute des raccourcis :
1. Définir le [périmètre du raccourci](#what-is-a-hotkey-scope-) qui écoutera les raccourcis
2. Utiliser le hook `useScopedHotkeys` pour écouter les raccourcis
La configuration des périmètres de raccourcis est nécessaire même sur des pages simples, car d'autres éléments de l'interface utilisateur comme le menu de gauche ou le menu de commandes pourraient également écouter les raccourcis.
## Cas d'utilisation des raccourcis
En général, vous aurez deux cas d'utilisation nécessitant des raccourcis :
1. Dans une page ou un composant monté dans une page
2. Dans un composant de type modal qui prend le focus à la suite d'une action utilisateur
Le deuxième cas d'utilisation peut se produire de manière récursive : un menu déroulant dans une fenêtre modale par exemple.
### Écouter les raccourcis dans une page
Exemple :
```tsx
const PageListeningEnter = () => {
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
// 1. Set the hotkey scope in a useEffect
useEffect(() => {
setHotkeyScopeAndMemorizePreviousScope(
ExampleHotkeyScopes.ExampleEnterPage,
);
// Revert to the previous hotkey scope when the component is unmounted
return () => {
goBackToPreviousHotkeyScope();
};
}, [goBackToPreviousHotkeyScope, setHotkeyScopeAndMemorizePreviousScope]);
// 2. Use the useScopedHotkeys hook
useScopedHotkeys(
Key.Enter,
() => {
// Some logic executed on this page when the user presses Enter
// ...
},
ExampleHotkeyScopes.ExampleEnterPage,
);
return <div>My page that listens for Enter</div>;
};
```
### Écouter les raccourcis dans un composant de type modal
Pour cet exemple, nous allons utiliser un composant modal qui écoute la touche Échap pour informer son parent de le fermer.
Ici, l'interaction utilisateur change le périmètre.
```tsx
const ExamplePageWithModal = () => {
const [showModal, setShowModal] = useState(false);
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
const handleOpenModalClick = () => {
// 1. Set the hotkey scope when user opens the modal
setShowModal(true);
setHotkeyScopeAndMemorizePreviousScope(
ExampleHotkeyScopes.ExampleModal,
);
};
const handleModalClose = () => {
// 1. Revert to the previous hotkey scope when the modal is closed
setShowModal(false);
goBackToPreviousHotkeyScope();
};
return <div>
<h1>My page with a modal</h1>
<button onClick={handleOpenModalClick}>Open modal</button>
{showModal && <MyModalComponent onClose={handleModalClose} />}
</div>;
};
```
Ensuite, dans le composant modal :
```tsx
const MyDropdownComponent = ({ onClose }: { onClose: () => void }) => {
// 2. Use the useScopedHotkeys hook to listen for Escape.
// Note that escape is a common hotkey that could be used by many other components
// So it's important to use a hotkey scope to avoid conflicts
useScopedHotkeys(
Key.Escape,
() => {
onClose()
},
ExampleHotkeyScopes.ExampleModal,
);
return <div>My modal component</div>;
};
```
Il est important d'utiliser ce masque lorsque vous n'êtes pas sûr que le simple fait d'utiliser un useEffect avec mount/unmount sera suffisant pour éviter les conflits.
Ces conflits peuvent être difficiles à déboger, et cela peut se produire plus souvent avec useEffects.
## Qu'est-ce qu'un périmètre de raccourci ?
Un périmètre de raccourci est une chaîne de caractères qui représente un contexte dans lequel les raccourcis sont actifs. Il est généralement encodé sous forme d'enum.
Lorsque vous modifiez le périmètre de raccourci, les raccourcis qui écoutent ce périmètre seront activés et ceux qui écoutent d'autres périmètres seront désactivés.
Vous ne pouvez définir qu'un seul périmètre à la fois.
Par exemple, les périmètres de raccourcis pour chaque page sont définis dans l'`enum PageHotkeyScope` :
```tsx
export enum PageHotkeyScope {
Settings = 'settings',
CreateWorkspace = 'create-workspace',
SignInUp = 'sign-in-up',
CreateProfile = 'create-profile',
PlanRequired = 'plan-required',
ShowPage = 'show-page',
PersonShowPage = 'person-show-page',
CompanyShowPage = 'company-show-page',
CompaniesPage = 'companies-page',
PeoplePage = 'people-page',
OpportunitiesPage = 'opportunities-page',
ProfilePage = 'profile-page',
WorkspaceMemberPage = 'workspace-member-page',
TaskPage = 'task-page',
}
```
En interne, le périmètre actuellement sélectionné est stocké dans un atome Jotai qui est partagé dans toute l'application :
```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
key: 'currentHotkeyScopeState',
defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```
Mais cet atome ne doit jamais être manipulé manuellement ! Nous verrons comment l'utiliser dans la prochaine section.
## Comment cela fonctionne-t-il en interne ?
Nous avons créé un léger emballage au-dessus de [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) qui le rend plus performant et évite les rendus inutiles.
Nous créons également un atome Jotai pour gérer l'état du périmètre des raccourcis et le rendre disponible partout dans l'application.

View File

@@ -0,0 +1,8 @@
---
title: Storybook
description: Parcourir la bibliothèque de composants UI de Twenty
---
Consultez notre bibliothèque de composants complète et la documentation dans Storybook.
[Ouvrir Storybook →](https://storybook.twenty.com)

View File

@@ -0,0 +1,291 @@
---
title: Guide de style
icon: paintbrush
---
Ce document inclut les règles à suivre lors de l'écriture de code.
L'objectif ici est d'avoir une base de code cohérente, facile à lire et à maintenir.
Pour cela, il vaut mieux être un peu plus verbeux que trop concis.
Gardez toujours à l'esprit que les gens lisent le code plus souvent qu'ils ne l'écrivent, surtout dans un projet open source où tout le monde peut contribuer.
Il existe de nombreuses règles qui ne sont pas définies ici, mais qui sont vérifiées automatiquement par des linters.
## React
### Utilisez des composants fonctionnels
Utilisez toujours des composants fonctionnels TSX.
N'utilisez pas `import` par défaut avec `const`, car c'est plus difficile à lire et plus difficile à importer avec l'autocomplétion du code.
```tsx
// ❌ Bad, harder to read, harder to import with code completion
const MyComponent = () => {
return <div>Hello World</div>;
};
export default MyComponent;
// ✅ Good, easy to read, easy to import with code completion
export function MyComponent() {
return <div>Hello World</div>;
};
```
### Propriétés
Créez le type des props et nommez-le `(NomDuComposant)Props` s'il n'est pas nécessaire de l'exporter.
Utilisez la déstructuration des props.
```tsx
// ❌ Bad, no type
export const MyComponent = (props) => <div>Hello {props.name}</div>;
// ✅ Good, type
type MyComponentProps = {
name: string;
};
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
#### Évitez d'utiliser `React.FC` ou `React.FunctionComponent` pour définir les types de props.
```tsx
/* ❌ - Bad, defines the component type annotations with `FC`
* - With `React.FC`, the component implicitly accepts a `children` prop
* even if it's not defined in the prop type. This might not always be
* desirable, especially if the component doesn't intend to render
* children.
*/
const EmailField: React.FC<{
value: string;
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
```
```tsx
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
* component's props
* - This method doesn't automatically include the children prop. If
* you want to include it, you have to specify it in OwnProps.
*/
type EmailFieldProps = {
value: string;
};
const EmailField = ({ value }: EmailFieldProps) => (
<TextInput value={value} disabled fullWidth />
);
```
#### Pas de propagation de props à variable unique dans les éléments JSX
Évitez d'utiliser la propagation de props à variable unique dans les éléments JSX, comme `{...props}`. Cette pratique résulte souvent en un code moins lisible et plus difficile à maintenir car il n'est pas clair quels props le composant reçoit.
```tsx
/* ❌ - Bad, spreads a single variable prop into the underlying component
*/
const MyComponent = (props: OwnProps) => {
return <OtherComponent {...props} />;
}
```
```tsx
/* ✅ - Good, Explicitly lists all props
* - Enhances readability and maintainability
*/
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
};
```
Raisonnement :
* D'un coup d'œil, il est plus clair quels props le code transmet, le rendant plus facile à comprendre et à maintenir.
* Cela aide à éviter le couplage serré entre les composants via leurs props.
* Les outils de linting facilitent l'identification des props mal orthographiés ou inutilisées lorsque vous listez explicitement les props.
## JavaScript
### Utilisez l'opérateur de coalescence nulle `??`
```tsx
// ❌ Bad, can return 'default' even if value is 0 or ''
const value = process.env.MY_VALUE || 'default';
// ✅ Good, will return 'default' only if value is null or undefined
const value = process.env.MY_VALUE ?? 'default';
```
### Utilisez la chaîne facultative `?.`
```tsx
// ❌ Bad
onClick && onClick();
// ✅ Good
onClick?.();
```
## TypeScript
### Utilisez `type` au lieu de `interface`
Utilisez toujours `type` au lieu de `interface`, car ils se chevauchent presque toujours, et `type` est plus flexible.
```tsx
// ❌ Bad
interface MyInterface {
name: string;
}
// ✅ Good
type MyType = {
name: string;
};
```
### Utilisez des littéraux de chaîne au lieu d'enums
[Les littéraux de chaîne](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) sont la méthode de référence pour gérer des valeurs semblables à des enums dans TypeScript. Ils sont plus faciles à étendre avec Pick et Omit, et offrent une meilleure expérience développeur, en particulier avec l'auto-complétion du code.
Vous pouvez voir pourquoi TypeScript recommande d'éviter les enums [ici](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums).
```tsx
// ❌ Bad, utilizes an enum
enum Color {
Red = "red",
Green = "green",
Blue = "blue",
}
let color = Color.Red;
```
```tsx
// ✅ Good, utilizes a string literal
let color: "red" | "green" | "blue" = "red";
```
#### GraphQL et bibliothèques internes
Vous devriez utiliser les enums générés par le codegen GraphQL.
Il est également préférable d'utiliser un enum lors de l'utilisation d'une bibliothèque interne, afin que la bibliothèque interne n'ait pas à exposer un type de littéral de chaîne qui n'est pas lié à l'API interne.
Exemple :
```TSX
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
```
## Stylisme
### Utilisez StyledComponents
Styliser les composants avec [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
<div className="my-class">Hello World</div>
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
Préfixez les composants stylisés avec "Styled" pour les différencier des composants "réels".
```tsx
// ❌ Bad
const Title = styled.div`
color: red;
`;
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
### Thematisation
Utiliser le thème pour la majorité du stylisme des composants est l'approche préférée.
#### Unités de mesure
Évitez d'utiliser des valeurs `px` ou `rem` directement dans les composants stylisés. Les valeurs nécessaires sont généralement déjà définies dans le thème, il est donc recommandé d'utiliser le thème à ces fins.
#### Couleurs
Évitez d'introduire de nouvelles couleurs ; utilisez plutôt la palette existante du thème. Si la palette ne correspond pas, veuillez laisser un commentaire pour que l'équipe puisse rectifier cela.
```tsx
// ❌ Bad, directly specifies style values without utilizing the theme
const StyledButton = styled.button`
color: #333333;
font-size: 1rem;
font-weight: 400;
margin-left: 4px;
border-radius: 50px;
`;
```
```tsx
// ✅ Good, utilizes the theme
const StyledButton = styled.button`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
margin-left: ${({ theme }) => theme.spacing(1)};
border-radius: ${({ theme }) => theme.border.rounded};
`;
```
## Application d'interdiction d'importations de type
Évitez les importations de type. Pour appliquer cette norme, une règle Oxlint vérifie et signale toutes les importations de type. Cela aide à maintenir la cohérence et la lisibilité dans le code TypeScript.
```tsx
// ❌ Bad
import { type Meta, type StoryObj } from '@storybook/react';
// ❌ Bad
import type { Meta, StoryObj } from '@storybook/react';
// ✅ Good
import { Meta, StoryObj } from '@storybook/react';
```
### Pourquoi éviter les importations de type
* **Cohérence** : En évitant les importations de type et en utilisant une seule approche pour les importations de type et de valeur, la base de code reste cohérente dans son style d'importation de module.
* **Lisibilité** : Les importations sans type améliorent la lisibilité du code en clarifiant quand vous importez des valeurs ou des types. Cela réduit l'ambiguïté et facilite la compréhension de l'objectif des symboles importés.
* **Maintenabilité** : Cela améliore la maintenabilité de la base de code car les développeurs peuvent identifier et localiser les importations uniquement de type lors de la révision ou de la modification du code.
### Règle Oxlint
Une règle Oxlint, `typescript/consistent-type-imports`, impose la convention des imports "type-only". Cette règle génère des erreurs ou des avertissements pour toutes les violations d'importations de type.
Veillez à ce que cette règle aborde spécifiquement les rares cas particuliers où se produisent des importations de type involontaires. TypeScript lui-même déconseille cette pratique, comme mentionné dans les [notes de version de TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). Dans la majorité des situations, vous ne devriez pas avoir besoin d'utiliser des importations uniquement de type.
Pour garantir la conformité de votre code à cette règle, assurez-vous d'exécuter Oxlint dans le cadre de votre flux de travail de développement.

View File

@@ -0,0 +1,61 @@
---
title: Travailler avec Figma
info: Apprenez comment vous pouvez collaborer avec le Figma de Twenty
---
Figma est un outil de conception d'interface collaboratif qui aide à surmonter la barrière de communication entre designers et développeurs.
Ce guide explique comment vous pouvez collaborer avec Figma.
## Accès
1. **Accédez au lien partagé :** Vous pouvez accéder au fichier Figma du projet [ici](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty).
2. **Se connecter :** Si vous n'êtes pas déjà connecté, Figma vous invitera à le faire.
Les fonctionnalités clés ne sont disponibles que pour les utilisateurs connectés, telles que le mode développeur et la possibilité de sélectionner un cadre dédié.
<Warning>
Vous ne pourrez pas collaborer efficacement sans compte.
</Warning>
## Structure de Figma
Sur la barre latérale gauche, vous pouvez accéder aux différentes pages du Figma de Twenty. Voici comment elles sont organisées :
* **Page des composants :** C'est la première page. Le designer l'utilise pour créer et organiser les éléments de design réutilisables utilisés dans tout le fichier design. Par exemple, boutons, icônes, symboles ou tout autre composant réutilisable. Elle sert à maintenir la cohérence dans le design.
* **Page principale :** La deuxième page est la page principale, qui montre l'interface utilisateur complète du projet. Vous pouvez appuyer sur ***Play*** pour utiliser le prototype complet de l'application.
* **Pages des fonctionnalités :** Les autres pages sont généralement dédiées aux fonctionnalités en cours de développement. Elles contiennent le design de fonctionnalités ou modules spécifiques de l'application ou du site web. Elles sont généralement encore en cours de développement.
## Conseils utiles
Avec un accès en lecture seule, vous ne pouvez pas modifier le design, mais vous pouvez accéder à toutes les fonctionnalités utiles pour convertir les designs en code.
### Utiliser le mode Dev
Le Mode Dev de Figma améliore la productivité des développeurs en fournissant une navigation simple dans le design, une gestion efficace des ressources, des outils de communication performants, des intégrations de boîte à outils, des extraits de code rapides et des informations clés sur les calques, comblant ainsi le fossé entre design et développement. Vous pouvez en savoir plus sur le mode Dev [ici](https://www.figma.com/dev-mode/).
Basculez vers le mode « Développeur » dans la partie droite de la barre d'outils pour voir les spécifications du design, copier du CSS et accéder aux ressources.
### Utilisez le Prototype
Cliquez sur n'importe quel élément du canvas et appuyez sur le bouton « Play » en haut à droite de l'interface pour accéder à la vue du prototype. Le mode Prototype vous permet d'interagir avec le design comme s'il s'agissait du produit final. Il montre le flux entre les écrans et comment les éléments de l'interface tels que les boutons, les liens ou les menus se comportent lorsqu'ils sont interactifs.
1. **Comprendre les transitions et animations :** En mode Prototype, vous pouvez voir toutes les transitions ou animations ajoutées par un designer entre écrans ou éléments UI, fournissant des instructions visuelles claires aux développeurs sur le comportement et le style attendus.
2. **Clarification de l'implémentation :** Un prototype peut aussi aider à réduire les ambiguïtés. Les développeurs peuvent interagir avec lui pour mieux comprendre la fonctionnalité ou l'apparence de certains éléments.
Pour des détails et instructions plus complets sur l'apprentissage de la plateforme Figma, vous pouvez visiter la [Documentation officielle de Figma](https://help.figma.com/hc/fr).
### Mesurer les distances
Sélectionnez un élément, maintenez la touche `Option` (Mac) ou `Alt` (Windows), puis survolez un autre élément pour voir la distance entre eux.
### Extension Figma pour VSCode (Recommandée)
[Figma pour VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension)
vous permet de naviguer et d'inspecter les fichiers design, de collaborer avec les designers, de suivre les changements et d'accélérer la mise en œuvre - le tout sans quitter votre éditeur de texte.
Cela fait partie de nos extensions recommandées.
## Collaboration
1. **Utilisation des commentaires :** N'hésitez pas à utiliser la fonction commentaire en cliquant sur l'icône de bulle dans la partie gauche de la barre d'outils.
2. **Conversation par curseur :** Une fonctionnalité intéressante de Figma est la conversation par curseur. Il suffit d'appuyer sur `;` sur Mac et `/` sur Windows pour envoyer un message si vous voyez quelqu'un d'autre utiliser Figma en même temps que vous.

View File

@@ -0,0 +1,318 @@
---
title: Configuration Locale
icon: laptop-code
description: Le guide pour les contributeurs (ou les développeurs curieux) qui souhaitent exécuter Twenty localement.
---
## Prérequis
<Tabs>
<Tab title="Linux et macOS">
Avant de pouvoir installer et utiliser Twenty, assurez-vous d'installer ce qui suit sur votre ordinateur :
* [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` ne fonctionnera pas, vous devez utiliser `yarn` à la place. Yarn est maintenant intégré avec Node.js, vous n'avez donc pas besoin de l'installer séparément.
Vous devez uniquement exécuter `corepack enable` pour activer Yarn si vous ne l'avez pas encore fait.
</Warning>
</Tab>
<Tab title="Windows (WSL)">
1. Installer WSL
Ouvrez PowerShell en tant qu'administrateur et exécutez :
```powershell
wsl --install
```
Vous devriez maintenant voir une invite pour redémarrer votre ordinateur. Si ce n'est pas le cas, redémarrez-le manuellement.
Au redémarrage, une fenêtre PowerShell s'ouvrira et installera Ubuntu. Cela peut prendre un peu de temps.
Une invite pour créer un nom d'utilisateur et un mot de passe pour votre installation Ubuntu s'affichera.
2. Installez et configurez Git
```bash
sudo apt-get install git
git config --global user.name "Your Name"
git config --global user.email "youremail@domain.com"
```
3. Installez nvm, Node.js et Yarn
<Warning>
Utilisez `nvm` pour installer la version correcte de `node`. Le fichier `.nvmrc` garantit que tous les contributeurs utilisent la même version.
</Warning>
```bash
sudo apt-get install curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
```
Fermez et rouvrez votre terminal pour utiliser nvm. Ensuite, exécutez les commandes suivantes.
```bash
nvm install # installs recommended node version
nvm use # use recommended node version
corepack enable
```
</Tab>
</Tabs>
---
## Étape 1 : Cloner le dépôt Git
Dans votre terminal, exécutez la commande suivante.
<Tabs>
<Tab title="SSH (Recommandé)">
Si vous n'avez pas encore configuré les clés SSH, vous pouvez apprendre à le faire [ici](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh).
```bash
git clone git@github.com:twentyhq/twenty.git
```
</Tab>
<Tab title="HTTPS">
```bash
git clone https://github.com/twentyhq/twenty.git
```
</Tab>
</Tabs>
## Étape 2 : Positionnez-vous à la racine
```bash
cd twenty
```
Vous devez exécuter toutes les commandes des étapes suivantes depuis la racine du projet.
## Étape 3 : Configurez une base de données PostgreSQL
<Tabs>
<Tab title="Linux">
**Option 1 (préférée) :** Pour préparer votre base de données localement :
Utilisez le lien suivant pour installer PostgreSQL sur votre machine Linux : [Installation de PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Remarque : Vous devrez peut-être ajouter `sudo -u postgres` à la commande précédente `psql` pour éviter les erreurs de permission.
**Option 2 :** Si vous avez installé Docker :
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Mac OS">
**Option 1 (préférée):** Pour préparer votre base de données localement avec `brew` :
```bash
brew install postgresql@16
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
brew services start postgresql@16
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Vous pouvez vérifier si le serveur PostgreSQL fonctionne en exécutant :
```bash
brew services list
```
L'installateur peut ne pas créer l'utilisateur `postgres` par défaut lors de l'installation
via Homebrew sur macOS. Au lieu de cela, il crée un rôle PostgreSQL correspondant à votre nom d'utilisateur macOS
(par exemple, "john").
Pour vérifier et créer l'utilisateur `postgres` si nécessaire, suivez ces étapes :
```bash
# Connect to PostgreSQL
psql postgres
or
psql -U $(whoami) -d postgres
```
Une fois à l'invite psql (postgres=#), exécutez :
```bash
# List existing PostgreSQL roles
\du
```
Vous verrez une sortie similaire à :
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
john | Superuser | {}
```
Si vous ne voyez pas de rôle `postgres` listé, passez à l'étape suivante.
Créez le rôle `postgres` manuellement :
```bash
CREATE ROLE postgres WITH SUPERUSER LOGIN;
```
Cela crée un rôle superutilisateur nommé `postgres` avec un accès de connexion.
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
postgres | Superuser | {}
john | Superuser | {}
```
**Option 2 :** Si vous avez installé Docker :
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
Toutes les étapes suivantes doivent être exécutées dans le terminal WSL (dans votre machine virtuelle)
**Option 1 :** Pour provisionner PostgreSQL localement :
Utilisez le lien suivant pour installer PostgreSQL sur votre machine virtuelle Linux : [Installation de PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Remarque : Vous devrez peut-être ajouter `sudo -u postgres` à la commande précédente `psql` pour éviter les erreurs de permission.
**Option 2 :** Si vous avez installé Docker :
Exécuter Docker sur WSL ajoute une couche de complexité supplémentaire.
N'utilisez cette option que si vous êtes à l'aise avec les étapes supplémentaires impliquées, y compris l'activation de [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
Vous pouvez maintenant accéder à la base de données à l'adresse `localhost:5432`.
Si vous avez utilisé l'option Docker ci-dessus, les identifiants par défaut sont l'utilisateur `postgres` et le mot de passe `postgres`. Pour les installations natives de PostgreSQL, utilisez les identifiants et les rôles configurés sur votre machine.
## Étape 4 : Configuration d'une base de données Redis (cache)
Twenty nécessite un cache Redis pour offrir les meilleures performances.
<Tabs>
<Tab title="Linux">
**Option 1** : Pour provisionner votre Redis localement :
Utilisez le lien suivant pour installer Redis sur votre machine Linux : [Installation de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Option 2 :** Si vous avez installé Docker :
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Mac OS">
**Option 1 (préférée) :** Pour provisionner votre Redis localement avec `brew` :
```bash
brew install redis
```
Démarrez votre serveur Redis :
```bash
brew services start redis
```
**Option 2 :** Si vous avez installé Docker :
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
**Option 1 :** Pour provisionner votre Redis localement :
Utilisez le lien suivant pour installer Redis sur votre machine virtuelle Linux : [Installation de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Option 2 :** Si vous avez installé Docker :
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
Si vous avez besoin d'une interface graphique cliente, nous recommandons [Redis Insight](https://redis.io/insight/) (version gratuite disponible).
## Étape 5 : Configurer les variables d'environnement
Utilisez des variables d'environnement ou des fichiers `.env` pour configurer votre projet. Plus d'informations [ici](/l/fr/developers/self-host/capabilities/setup).
Copiez les fichiers `.env.example` dans `/front` et `/server` :
```bash
cp ./packages/twenty-front/.env.example ./packages/twenty-front/.env
cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
```
<Info>
**Mode multi-espaces de travail:** Par défaut, Twenty s'exécute en mode mono-espace de travail, où un seul espace de travail peut être créé. Pour activer la prise en charge multi-espaces de travail (utile pour tester des fonctionnalités basées sur des sous-domaines), définissez `IS_MULTIWORKSPACE_ENABLED=true` dans le fichier `.env` de votre serveur. Voir [Mode multi-espaces de travail](/l/fr/developers/self-host/capabilities/setup#multi-workspace-mode) pour plus de détails.
</Info>
## Étape 6 : Installation des dépendances
Pour construire le serveur Twenty et injecter des données dans votre base de données, exécutez la commande suivante :
```bash
yarn
```
Notez que `npm` ou `pnpm` ne fonctionneront pas
## Étape 7 : Exécution du projet
<Tabs>
<Tab title="Linux">
Selon votre distribution Linux, le serveur Redis peut être démarré automatiquement.
Sinon, consultez le guide d'installation de [Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) pour votre distribution.
</Tab>
<Tab title="Mac OS">
Redis devrait déjà être en cours d'exécution. Sinon, exécutez :
```bash
brew services start redis
```
</Tab>
<Tab title="Windows (WSL)">
Selon votre distribution Linux, le serveur Redis peut être démarré automatiquement.
Sinon, vérifiez le [guide d'installation de Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) pour votre distribution.
</Tab>
</Tabs>
Configurez votre base de données avec la commande suivante :
```bash
npx nx database:reset twenty-server
```
Démarrez le serveur, le worker et les services frontend :
```bash
npx nx start twenty-server
npx nx worker twenty-server
npx nx start twenty-front
```
Alternativement, vous pouvez démarrer tous les services en même temps :
```bash
npx nx start
```
## Étape 8 : Utiliser Twenty
**Frontend**
Le frontend de Twenty sera en cours d'exécution à [http://localhost:3001](http://localhost:3001).
Vous pouvez vous connecter en utilisant le compte démo par défaut : `tim@apple.dev` (mot de passe : `tim@apple.dev`)
**Backend**
* Le serveur de Twenty sera en fonctionnement à [http://localhost:3000](http://localhost:3000)
* L'API GraphQL est accessible à [http://localhost:3000/graphql](http://localhost:3000/graphql)
* L'API REST est joignable à [http://localhost:3000/rest](http://localhost:3000/rest)
## Résolution des problèmes
Si vous rencontrez un problème, consultez [Dépannage](/l/fr/developers/self-host/capabilities/troubleshooting) pour des solutions.