i18n - docs translations (#15720)
Created by Github action <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a full French documentation set covering developers (API, webhooks, backend/frontend, self‑hosting) and user guide (CRM essentials, data model, workflows, settings, integrations, pricing, reporting). > > - **Docs (FR i18n)**: > - **Developers**: Add `API`, `Webhooks`, backend (best practices, custom objects, feature flags, architecture, commands, Zapier), frontend (best practices, architecture, commands, hotkeys, style guide, Storybook, Figma), self‑hosting (Docker Compose, upgrade guide, cloud providers), introduction. > - **User Guide**: Add getting started (what is Twenty, create/configure workspace, migration, import/export), CRM essentials (contacts/accounts, pipelines, views), data model (objects, fields, relations, table views), collaboration (emails/calendars, notes, tasks), integrations API (overview, integrations), workflows (getting started, features, credits, internal automations, services), settings (profile, permissions, members, domains, releases, email/calendar setup), pricing (billing/FAQ), reporting overview, resources (GitHub, glossary), introduction. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9ba8c2457156e8d25bf41c055731acface4b5277. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: github-actions <github-actions@twenty.com> Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
committed by
GitHub
parent
0fc538ac09
commit
154fb4665e
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: API
|
||||
image: /images/docs/getting-started/api.png
|
||||
info: Découvrez comment utiliser nos API.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/docs/getting-started/api.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
L'API de Twenty permet aux développeurs d'interagir de manière programmée avec la plateforme CRM de Twenty. En utilisant l'API, vous pouvez intégrer Twenty avec d'autres systèmes, automatiser la synchronisation des données, et construire des solutions personnalisées autour de vos données clients. L'API fournit des points d'accès pour **créer, lire, mettre à jour et supprimer** des objets CRM principaux (comme les personnes et les entreprises) ainsi que pour accéder à la configuration des métadonnées.
|
||||
|
||||
**API Playground :** Vous pouvez désormais accéder à l'API Playground dans les paramètres de l'application. Pour essayer les appels API en temps réel, connectez-vous à votre espace de travail Twenty et naviguez vers **Paramètres → APIs & Webhooks**. Cela ouvre le API Playground intégré et les paramètres pour les clés API.
|
||||
**[Accédez aux paramètres de l'API](https://app.twenty.com/settings)**
|
||||
|
||||
## Authentification
|
||||
|
||||
L'API de Twenty utilise des clés API pour l'authentification. Chaque requête vers des points d'accès protégés doit inclure une clé API dans l'en-tête.
|
||||
|
||||
- **Clés API :** Vous pouvez générer une nouvelle clé API depuis la page de **paramètres de l'API** de votre application Twenty. Chaque clé API est un jeton secret qui accorde l'accès à vos données CRM, alors gardez-le en sécurité. Si une clé est compromise, révoquez-la dans les paramètres et générez-en une nouvelle.
|
||||
- **En-tête Auth :** Une fois que vous avez une clé API, incluez-la dans l'en-tête `Authorization` de vos requêtes HTTP. Utilisez le schéma de jeton Bearer. Par exemple :
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
Remplacez `VOTRE_CLÉ_API` par la clé que vous avez obtenue. Cet en-tête doit être présent sur **toutes les requêtes API**. Si le jeton est manquant ou invalide, l'API répondra avec une erreur d'authentification (HTTP 401 Non autorisé).
|
||||
|
||||
## Points d'accès API
|
||||
|
||||
Toutes les ressources peuvent être consultées via REST ou GraphQL.
|
||||
|
||||
- **Cloud :** `https://api.twenty.com/` ou votre domaine / sous-domaine personnalisé
|
||||
- **Instances auto-hébergées :** Si vous exécutez Twenty sur votre propre serveur, utilisez votre propre domaine à la place de `api.twenty.com` (par exemple, `https://<votre-domaine>/rest/`).
|
||||
|
||||
Les points d'accès sont groupés en deux catégories : **Core API** et **Metadata API**. L'**API principale** traite les données CRM primaires (personnes, entreprises, notes, tâches), tandis que l'**API des métadonnées** couvre les données de configuration (comme les champs personnalisés ou les définitions d'objets). La plupart des intégrations utiliseront principalement l'API principale.
|
||||
|
||||
### API principale
|
||||
|
||||
Accessible sur `/rest/` ou `/graphql/`.
|
||||
L'**API principale** sert de l'interface unifiée pour gérer les entités CRM principales (personnes, entreprises, notes, tâches) et leurs relations, offrant des modèles d'interaction **REST et GraphQL**.
|
||||
|
||||
### API de métadonnées
|
||||
|
||||
Accessible sur `/rest/metadata/` ou `/metadata/`.
|
||||
Les points d'accès de l'API de métadonnées permettent de récupérer des informations sur votre schéma et vos paramètres. Par exemple, vous pouvez récupérer les définitions des champs personnalisés, des schémas d'objets, etc.
|
||||
|
||||
- **Exemples de points d'accès :**
|
||||
|
||||
- `GET /rest/metadata/objects` – Liste tous les types d'objets et leurs métadonnées (champs, relations).
|
||||
- `GET /rest/metadata/objects/{objectName}` – Obtenez les métadonnées d'un objet spécifique (par exemple, `people`, `companies`).
|
||||
- `GET /rest/metadata/picklists` – Récupérer les options de champ de liste déroulante définies dans le CRM.
|
||||
|
||||
Typiquement, les points d'accès des métadonnées sont utilisés pour comprendre la structure des données (pour des intégrations dynamiques ou la création de formulaires) plutôt que pour gérer des enregistrements réels. Ils sont principalement en lecture seule dans la plupart des cas. Une authentification est également requise pour ceux-ci (utilisez votre clé API).
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Webhooks
|
||||
image: /images/docs/getting-started/webhooks.png
|
||||
info: Découvrez comment utiliser nos Webhooks.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/docs/getting-started/webhooks.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Les Webhooks dans Twenty complètent l'API en permettant des **notifications en temps réel** à vos propres applications lors de certains événements dans votre CRM. Au lieu de sonder continuellement l'API pour les changements, vous pouvez configurer des webhooks pour que Twenty **pousse** des données vers votre système chaque fois que des événements spécifiques se produisent (par exemple, lorsqu'un nouvel enregistrement est créé ou qu'un enregistrement existant est mis à jour). Cela aide à synchroniser instantanément et efficacement les systèmes externes avec Twenty.
|
||||
|
||||
Avec les webhooks, Twenty enverra une requête HTTP POST à une URL que vous spécifiez, contenant des détails sur l'événement. Vous pouvez ensuite traiter ces données dans votre application (par exemple, pour mettre à jour votre base de données externe, déclencher des flux de travail ou envoyer des alertes).
|
||||
|
||||
## Configuration d'un Webhook
|
||||
|
||||
Pour créer un webhook dans Twenty, utilisez les paramètres **APIs & Webhooks** dans votre application Twenty :
|
||||
|
||||
1. **Naviguer vers les Paramètres :** Dans votre application Twenty, allez dans **Paramètres → APIs & Webhooks**.
|
||||
2. **Créer un Webhook :** Dans **Webhooks**, cliquez sur **+ Créer un webhook**.
|
||||
3. **Entrez l'URL :** Fournissez l'URL de l'endpoint sur votre serveur où vous souhaitez que Twenty envoie les requêtes webhook. Cela doit être une URL publiquement accessible qui peut gérer les requêtes POST.
|
||||
4. **Enregistrer :** Cliquez sur **Enregistrer** pour créer le webhook. Le nouveau webhook sera actif immédiatement.
|
||||
|
||||
Vous pouvez créer plusieurs webhooks si vous avez besoin d'envoyer différents événements vers différents endpoints. Chaque webhook est essentiellement un abonnement pour tous les événements pertinents (actuellement, Twenty envoie tous types d'événements à l'URL donnée ; filtrer des types d'événements spécifiques peut être configurable dans l'interface utilisateur). Si vous devez supprimer un webhook, vous pouvez le supprimer de la même page des paramètres (sélectionnez le webhook et choisissez supprimer).
|
||||
|
||||
## Événements et Charges utiles
|
||||
|
||||
Une fois qu'un webhook est configuré, Twenty enverra une requête HTTP POST à l'URL que vous avez spécifiée chaque fois qu'un événement déclencheur se produit dans vos données CRM. Les événements communs qui déclenchent les webhooks incluent :
|
||||
|
||||
- **Enregistrement Créé :** par exemple, une nouvelle personne est ajoutée (`person.created`), une nouvelle entreprise est créée (`company.created`), une note est créée (`note.created`), etc.
|
||||
- **Enregistrement Mis à Jour :** par exemple, les informations d'une personne existante sont mises à jour (`person.updated`), un enregistrement d'entreprise est édité (`company.updated`), etc.
|
||||
- **Enregistrement Supprimé :** par exemple, une personne ou une entreprise est supprimée (`person.deleted`, `company.deleted`).
|
||||
- **Autres Événements :** Si applicable, d'autres événements d'objet ou déclencheurs personnalisés (par exemple, si des tâches ou d'autres objets sont mis à jour, des types d'événements similaires seraient utilisés comme `task.created`, `note.updated`, etc.).
|
||||
|
||||
La requête POST du webhook contient une charge utile JSON dans son corps. La charge utile comprendra généralement au moins deux choses : le type d'événement et les données relatives à cet événement (souvent l'enregistrement qui a été créé/mise à jour). Par exemple, un webhook pour une nouvelle personne créée pourrait envoyer une charge utile comme :
|
||||
|
||||
```
|
||||
{
|
||||
"event": "person.created",
|
||||
"data": {
|
||||
"id": "abc12345",
|
||||
"firstName": "Alice",
|
||||
"lastName": "Doe",
|
||||
"email": "alice@example.com",
|
||||
"createdAt": "2025-02-10T15:30:45Z",
|
||||
"createdBy": "user_123"
|
||||
},
|
||||
"timestamp": "2025-02-10T15:30:50Z"
|
||||
}
|
||||
```
|
||||
|
||||
Dans cet exemple :
|
||||
|
||||
- `"event"` spécifie ce qui s'est passé (`person.created`).
|
||||
- `"data"` contient les détails du nouvel enregistrement (les mêmes informations que vous obtiendriez si vous demandiez cette personne via l'API).
|
||||
- `"timestamp"` est le moment où l'événement s'est produit (en UTC).
|
||||
|
||||
Votre endpoint devrait être prêt à recevoir de telles données JSON via POST. En général, vous allez analyser le JSON, regarder le type d'événement `"event"` pour comprendre ce qui s'est passé, et ensuite utiliser les `"data"` en conséquence (par exemple, créer un nouveau contact dans votre système ou mettre à jour un contact existant).
|
||||
|
||||
**Remarque :** Il est important de répondre avec un **statut HTTP 2xx** depuis votre endpoint webhook pour accuser réception avec succès. Si l'expéditeur du webhook de Twenty ne reçoit pas une réponse 2xx, il peut considérer que la livraison a échoué. (À l'avenir, une logique de réessai pourrait tenter de renvoyer les webhooks échoués, donc essayez toujours de retourner un 200 OK aussi rapidement que possible après le traitement des données.)
|
||||
|
||||
## Validation des Webhooks
|
||||
|
||||
Pour garantir la sécurité de vos endpoints webhook, Twenty inclut une signature dans l'en-tête `X-Twenty-Webhook-Signature`.
|
||||
|
||||
Cette signature est un hachage HMAC SHA256 de la charge utile de la requête, calculé en utilisant votre clé secrète.
|
||||
|
||||
Pour valider la signature, vous devrez :
|
||||
|
||||
1. Concaténer le timestamp (de l'en-tête `X-Twenty-Webhook-Timestamp`), un deux-points, et la chaîne JSON de la charge utile
|
||||
2. Calculer le hachage HMAC SHA256 en utilisant votre clé secrète comme clé ()
|
||||
3. Comparer le résultat de la somme de contrôle hexadécimale avec l'en-tête de signature
|
||||
|
||||
Voici un exemple en Node.js :
|
||||
|
||||
```javascript
|
||||
const crypto = require("crypto");
|
||||
const timestamp = "1735066639761";
|
||||
const payload = JSON.stringify({...});
|
||||
const secret = "your-secret";
|
||||
const stringToSign = `${timestamp}:${JSON.stringify(payload)}`;
|
||||
const signature = crypto.createHmac("sha256", secret)
|
||||
.update(stringToSign)
|
||||
.digest("hex");
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Meilleures pratiques
|
||||
image: /images/user-guide/tips/light-bulb.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/tips/light-bulb.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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 type de TypeScript ne procède à aucune vérification, rendant possible l'assignation de 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 deviner 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.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Objets personnalisés
|
||||
image: /images/user-guide/objects/objects.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/objects/objects.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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="High level schema" />
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
## Comment cela fonctionne
|
||||
|
||||
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="Query the /metadata API to add custom objects" />
|
||||
</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="Query the /graphql endpoint to fetch data" />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Drapeaux de fonctionnalité
|
||||
image: /images/user-guide/table-views/table.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/table-views/table.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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é sur une fonctionnalité de **backend**, utilisez :
|
||||
|
||||
```ts
|
||||
@Gate({
|
||||
featureFlag: 'IS_FEATURENAME_ENABLED',
|
||||
})
|
||||
```
|
||||
|
||||
Pour appliquer un drapeau de fonctionnalité sur une fonctionnalité de **frontend**, 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` |
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
---
|
||||
title: Architecture des Dossiers
|
||||
info: Un regard détaillé sur l'architecture des dossiers de notre serveur
|
||||
image: /images/user-guide/fields/field.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/fields/field.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
La structure du répertoire backend est la suivante :
|
||||
|
||||
```
|
||||
server
|
||||
└───ability
|
||||
└───constants
|
||||
└───core
|
||||
└───database
|
||||
└───decorators
|
||||
└───filters
|
||||
└───guards
|
||||
└───health
|
||||
└───integrations
|
||||
└───metadata
|
||||
└───workspace
|
||||
└───utils
|
||||
```
|
||||
|
||||
## Capacité
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: Commandes Backend
|
||||
image: /images/user-guide/kanban-views/kanban.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/kanban-views/kanban.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## 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 <commande> twenty-server` (ou `npx nx run twenty-server:<commande>`).
|
||||
|
||||
### 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:typeorm migration:generate src/database/typeorm/core/migrations/nameOfYourMigration -d src/database/typeorm/core/core.datasource.ts
|
||||
```
|
||||
|
||||
#### Pour les objets de l'Espace de travail
|
||||
|
||||
Il n'y a pas de fichiers de migrations, les migrations sont générées automatiquement pour chaque espace de travail, stockées dans la base de données et appliquées avec cette commande
|
||||
|
||||
```bash
|
||||
npx nx run twenty-server:command workspace:sync-metadata -f
|
||||
```
|
||||
|
||||
<Warning>
|
||||
|
||||
Cette action supprimera la base de données et relancera les migrations et les semences.
|
||||
|
||||
Assurez-vous de sauvegarder toutes les données que vous souhaitez conserver avant d'exécuter cette commande.
|
||||
|
||||
</Warning>
|
||||
|
||||
## É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/)
|
||||
- [ESLint](https://eslint.org/)
|
||||
|
||||
**Développement**
|
||||
|
||||
- [AWS EKS](https://aws.amazon.com/eks/)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Application Zapier
|
||||
image: /images/user-guide/integrations/plug.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/integrations/plug.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Bugs et demandes
|
||||
image: /images/user-guide/api/api.png
|
||||
info: Demandez de l'aide sur GitHub ou Discord
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/api/api.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## 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).
|
||||
@@ -0,0 +1,333 @@
|
||||
---
|
||||
title: Meilleures pratiques
|
||||
image: /images/user-guide/tips/light-bulb.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/tips/light-bulb.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
Ce document décrit les meilleures pratiques à suivre lors de votre travail sur l'interface frontend.
|
||||
|
||||
## Gestion de l'état
|
||||
|
||||
React et Recoil gèrent la gestion de l'état dans la base de code.
|
||||
|
||||
### Utilisez `useRecoilState` 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
|
||||
export const myAtomState = atom({
|
||||
key: 'myAtomState',
|
||||
default: 'default value',
|
||||
});
|
||||
|
||||
export const MyComponent = () => {
|
||||
const [myAtom, setMyAtom] = useRecoilState(myAtomState);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={myAtom}
|
||||
onChange={(e) => setMyAtom(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 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 `useRecoilState`.
|
||||
|
||||
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 secondaire `PageChangeEffect` contient seulement un `useEffect` qui retient toute la logique à exécuter lors d'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.
|
||||
|
||||
### Toujours réfléchir à deux fois avant d'ajouter `useEffect` dans votre code.
|
||||
|
||||
Les re-rendus sont souvent causés par des `useEffect` inutiles.
|
||||
|
||||
Vous devriez réfléchir 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 la logique de récupération des données
|
||||
|
||||
Si vous pensez devoir ajouter un `useEffect` dans votre composant racine, vous devriez envisager de l'extraire dans un composant secondaire.
|
||||
|
||||
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] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
setData(someDependency);
|
||||
}
|
||||
}, [someDependency]);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, will not cause re-renders if data is not changing,
|
||||
// because useEffect is re-evaluated in another sibling component
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const PageData = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
setData(someDependency);
|
||||
}
|
||||
}, [someDependency]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageData />
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
);
|
||||
```
|
||||
|
||||
### Utilisez les états de famille de recoil et les sélecteurs de famille de recoil
|
||||
|
||||
Les états et sélecteurs de famille recoil 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.
|
||||
|
||||
## Nomination
|
||||
|
||||
### 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.
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: Architecture des Dossiers
|
||||
info: Un aperçu détaillé de notre architecture de dossiers
|
||||
image: /images/user-guide/fields/field.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/fields/field.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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 plus bas niveau du dossier des 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. [RecoilJS](https://recoiljs.org) gère cela.
|
||||
|
||||
- Sélecteurs : Voir [RecoilJS Selectors](https://recoiljs.org/docs/basic-tutorial/selectors) pour plus de détails.
|
||||
|
||||
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 pour le dossier `ui`. Cela permettra de garder son code facile à tester.
|
||||
|
||||
### Interne
|
||||
|
||||
Chaque partie (hooks, états, ...) d'un module peut avoir un dossier `interne`, qui contient des parties utilisées uniquement au sein du module.
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Commandes Frontend
|
||||
image: /images/user-guide/create-workspace/workspace-cover.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/create-workspace/workspace-cover.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## 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)
|
||||
- [Recoil](https://recoiljs.org/docs/introduction/core-concepts)
|
||||
- [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/)
|
||||
- [ESLint](https://eslint.org/)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Routage
|
||||
|
||||
[React Router](https://reactrouter.com/) gère le routage.
|
||||
|
||||
Pour éviter les [re-renders](/developers/frontend-development/best-practices-front#managing-re-renders) inutiles, toute la logique de routage est dans un `useEffect` dans `PageChangeEffect`.
|
||||
|
||||
### Gestion de l'État
|
||||
|
||||
[Recoil](https://recoiljs.org/docs/introduction/core-concepts) gère la gestion de l'état.
|
||||
|
||||
Voir [les meilleures pratiques](/developers/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.
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
title: Raccourcis clavier
|
||||
image: /images/user-guide/table-views/table.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/table-views/table.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## 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 schéma lorsque vous n'êtes pas sûr que l'utilisation simple d'un useEffect avec montage/démontage suffira à éviter les conflits.
|
||||
|
||||
Ces conflits peuvent être difficiles à déboguer et peuvent survenir plus souvent qu'on ne le croit avec les 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 sélectionné est stocké dans un état Recoil qui est partagé dans toute l'application :
|
||||
|
||||
```tsx
|
||||
export const currentHotkeyScopeState = createState<HotkeyScope>({
|
||||
key: 'currentHotkeyScopeState',
|
||||
defaultValue: INITIAL_HOTKEYS_SCOPE,
|
||||
});
|
||||
```
|
||||
|
||||
Mais cet état Recoil 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 état Recoil pour gérer l'état du périmètre des raccourcis et le rendre disponible partout dans l'application.
|
||||
@@ -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)
|
||||
@@ -0,0 +1,295 @@
|
||||
---
|
||||
title: Guide de style
|
||||
image: /images/user-guide/notes/notes_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/notes/notes_header.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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 pour le développeur, notamment avec l'autocompletion de 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 [styled-components](https://emotion.sh/docs/styled).
|
||||
|
||||
```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 ESLint 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 ESLint
|
||||
|
||||
Une règle ESLint, `@typescript-eslint/consistent-type-imports`, applique la norme d'importation sans type. 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 avec cette règle, assurez-vous d'exécuter ESLint dans le cadre de votre flux de travail de développement.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: Travailler avec Figma
|
||||
info: Apprenez comment vous pouvez collaborer avec le Figma de Twenty
|
||||
image: /images/user-guide/objects/objects.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/objects/objects.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: Vue d'ensemble
|
||||
description: Documentation technique pour les contributeurs et développeurs travaillant avec Twenty
|
||||
---
|
||||
|
||||
## Commencer
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Local Setup" href="/developers/local-setup" img="/images/user-guide/fields/field.png">
|
||||
Le guide pour les contributeurs (ou les développeurs curieux) qui veulent exécuter Twenty localement (sur un ordinateur portable, PC...)
|
||||
</Card>
|
||||
|
||||
<Card title="Self-Hosting" href="/developers/self-hosting/docker-compose" img="/images/user-guide/integrations/plug.png">
|
||||
Apprenez à héberger Twenty sur votre propre serveur
|
||||
</Card>
|
||||
|
||||
<Card title="API and Webhooks" href="/developers/api-and-webhooks/api" img="/images/user-guide/api/api.png">
|
||||
APIs REST et GraphQL, webhooks et intégrations
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Contribution
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Bugs and Requests" href="/developers/bug-and-requests" img="/images/user-guide/api/api.png">
|
||||
Demandez de l'aide sur GitHub ou Discord
|
||||
</Card>
|
||||
|
||||
<Card title="Frontend Development" href="/developers/frontend-development/frontend-commands" img="/images/user-guide/create-workspace/workspace-cover.png">
|
||||
Commandes frontales, Figma, Meilleures pratiques React...
|
||||
</Card>
|
||||
|
||||
<Card title="Backend Development" href="/developers/backend-development/server-commands" img="/images/user-guide/kanban-views/kanban.png">
|
||||
NestJS, Objets personnalisés, Files d'attente...
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Autres méthodes
|
||||
image: /images/user-guide/notes/notes_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/notes/notes_header.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
Ce document est maintenu par la communauté. Il pourrait contenir des problèmes.
|
||||
</Warning>
|
||||
|
||||
## Kubernetes via Terraform et Manifests
|
||||
|
||||
La documentation communautaire pour le déploiement de Kubernetes est disponible [ici](https://github.com/twentyhq/twenty/tree/main/packages/twenty-docker/k8s)
|
||||
|
||||
### Coolify
|
||||
|
||||
Déployer Twenty sur les serveurs avec Coolify. (l'image officielle sur Coolify sera bientôt disponible)
|
||||
|
||||
[Documentation Coolify](https://coolify.io/docs/get-started/introduction)
|
||||
|
||||
### EasyPanel
|
||||
|
||||
Déployez Twenty sur EasyPanel avec le modèle maintenu par la communauté ci-dessous.
|
||||
|
||||
[Déployer sur EasyPanel](https://easypanel.io/docs/templates/twenty)
|
||||
|
||||
### Elest.io
|
||||
|
||||
Déployez Twenty sur les serveurs avec Elest.io en utilisant le lien ci-dessous.
|
||||
|
||||
[Déployer sur Elest.io](https://elest.io/open-source/twenty)
|
||||
|
||||
### Twenty sur Railway
|
||||
|
||||
Déployez Twenty sur Railway avec le modèle maintenu par la communauté ci-dessous.
|
||||
|
||||
[](https://railway.com/deploy/nAL3hA)
|
||||
|
||||
## Autres
|
||||
|
||||
N'hésitez pas à ouvrir une PR pour ajouter d'autres options de fournisseur cloud.
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: 1-Clic avec Docker Compose
|
||||
image: /images/user-guide/objects/objects.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/objects/objects.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
Les conteneurs Docker sont pour l'hébergement en production ou l'auto-hébergement, pour la contribution veuillez consulter le [Configuration Locale](https://docs.twenty.com/developers/local-setup).
|
||||
</Warning>
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Ce guide fournit des instructions pas-à-pas pour installer et configurer l'application Twenty à l'aide de Docker Compose. L'objectif est de simplifier le processus et d'éviter les erreurs courantes qui pourraient compromettre votre configuration.
|
||||
|
||||
**Important :** Modifiez uniquement les paramètres explicitement mentionnés dans ce guide. Modifier d'autres configurations peut entraîner des problèmes.
|
||||
|
||||
Consultez les documents [Configurer les Variables d’Environnement](https://docs.twenty.com/developers/self-hosting/setup) pour la configuration avancée. Toutes les variables d'environnement doivent être déclarées dans le fichier docker-compose.yml au niveau du serveur et/ou du travailleur, selon la variable.
|
||||
|
||||
## Exigences du Système
|
||||
|
||||
- RAM : Assurez-vous que votre environnement dispose d'au moins 2 Go de RAM. Une mémoire insuffisante peut provoquer des plantages des processus.
|
||||
- Docker & Docker Compose : Assurez-vous que les deux sont installés et à jour.
|
||||
|
||||
## Option 1 : Script en une seule ligne
|
||||
|
||||
Installez la dernière version stable de Twenty avec une seule commande :
|
||||
|
||||
```bash
|
||||
bash <(curl -sL https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/scripts/install.sh)
|
||||
```
|
||||
|
||||
Pour installer une version ou une branche spécifique :
|
||||
|
||||
```bash
|
||||
VERSION=vx.y.z BRANCH=branch-name bash <(curl -sL https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/scripts/install.sh)
|
||||
```
|
||||
|
||||
- Remplacez x.y.z par le numéro de version désiré.
|
||||
- Remplacez nom-branche par le nom de la branche que vous souhaitez installer.
|
||||
|
||||
## Option 2 : Étapes manuelles
|
||||
|
||||
Suivez ces étapes pour une installation manuelle.
|
||||
|
||||
### Étape 1 : Configurez le Fichier Environnement
|
||||
|
||||
1. **Créez le Fichier .env**
|
||||
|
||||
Copiez le fichier d'exemple d'environnement vers un nouveau fichier .env dans votre répertoire de travail :
|
||||
|
||||
```bash
|
||||
curl -o .env https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example
|
||||
```
|
||||
|
||||
2. **Générez des Jetons Secrets**
|
||||
|
||||
Exécutez la commande suivante pour générer une chaîne aléatoire unique :
|
||||
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
**Important :** Gardez cette valeur secrète / ne la partagez pas.
|
||||
|
||||
3. **Mettez à jour le `.env`**
|
||||
|
||||
Remplacez la valeur de l'espace réservé dans votre fichier .env par le jeton généré :
|
||||
|
||||
```ini
|
||||
APP_SECRET=first_random_string
|
||||
```
|
||||
|
||||
4. **Définissez le Mot de Passe de Postgres**
|
||||
|
||||
Mettez à jour la valeur `PG_DATABASE_PASSWORD` dans le fichier .env avec un mot de passe fort sans caractères spéciaux.
|
||||
|
||||
```ini
|
||||
PG_DATABASE_PASSWORD=my_strong_password
|
||||
```
|
||||
|
||||
### Étape 2 : Obtenez le Fichier Docker Compose
|
||||
|
||||
Téléchargez le fichier `docker-compose.yml` dans votre répertoire de travail :
|
||||
|
||||
```bash
|
||||
curl -o docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/docker-compose.yml
|
||||
```
|
||||
|
||||
### Étape 3 : Lancer l'Application
|
||||
|
||||
Démarrez les conteneurs Docker :
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Étape 4 : Accéder à l'Application
|
||||
|
||||
Si vous hébergez twentyCRM sur votre ordinateur, ouvrez votre navigateur et allez à [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
Si vous l'hébergez sur un serveur, vérifiez que le serveur fonctionne et que tout est en ordre avec
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Exposer Twenty à un Accès Externe
|
||||
|
||||
Par défaut, Twenty fonctionne sur `localhost` au port `3000`. Pour y accéder via un domaine externe ou une adresse IP, vous devez configurer le `SERVER_URL` dans votre fichier `.env`.
|
||||
|
||||
#### Comprendre `SERVER_URL`
|
||||
|
||||
- **Protocole :** Utilisez `http` ou `https` selon votre configuration.
|
||||
- Utilisez `http` si vous n'avez pas configuré SSL.
|
||||
- Utilisez `https` si vous avez configuré SSL.
|
||||
- **Domaine/IP :** Il s'agit du nom de domaine ou de l'adresse IP où votre application est accessible.
|
||||
- **Port :** Incluez le numéro de port si vous n'utilisez pas les ports par défaut (`80` pour `http`, `443` pour `https`).
|
||||
|
||||
### Exigences SSL
|
||||
|
||||
Le SSL (HTTPS) est requis pour que certaines fonctionnalités des navigateurs fonctionnent correctement. Bien que ces fonctionnalités puissent fonctionner lors du développement local (car les navigateurs traitent localhost différemment), une configuration SSL appropriée est nécessaire lors de l'hébergement de Twenty sur un domaine régulier.
|
||||
|
||||
Par exemple, l'API du presse-papiers pourrait nécessiter un contexte sécurisé - certaines fonctionnalités comme les boutons de copie dans toute l'application pourraient ne pas fonctionner sans HTTPS activé.
|
||||
|
||||
Nous vous recommandons vivement de mettre en place Twenty derrière un proxy inverse avec terminaison SSL pour une sécurité et une fonctionnalité optimales.
|
||||
|
||||
#### Configurer `SERVER_URL`
|
||||
|
||||
1. **Déterminez Votre URL d'Accès**
|
||||
- **Sans Proxy Inverse (Accès Direct):**
|
||||
|
||||
Si vous accédez directement à l'application sans un proxy inverse :
|
||||
|
||||
```ini
|
||||
SERVER_URL=http://your-domain-or-ip:3000
|
||||
```
|
||||
|
||||
- **Avec Proxy Inverse (Ports Standards):**
|
||||
|
||||
Si vous utilisez un proxy inverse comme Nginx ou Traefik et que vous avez SSL configuré :
|
||||
|
||||
```ini
|
||||
SERVER_URL=https://your-domain-or-ip
|
||||
```
|
||||
|
||||
- **Avec Proxy Inverse (Ports Personnalisés):**
|
||||
|
||||
Si vous utilisez des ports non standards :
|
||||
|
||||
```ini
|
||||
SERVER_URL=https://your-domain-or-ip:custom-port
|
||||
```
|
||||
|
||||
2. **Mettez à jour le Fichier `.env`**
|
||||
|
||||
Ouvrez votre fichier `.env` et mettez à jour le `SERVER_URL`:
|
||||
|
||||
```ini
|
||||
SERVER_URL=http(s)://your-domain-or-ip:your-port
|
||||
```
|
||||
|
||||
**Exemples :**
|
||||
|
||||
- Accès direct sans SSL :
|
||||
```ini
|
||||
SERVER_URL=http://123.45.67.89:3000
|
||||
```
|
||||
- Accès via domaine avec SSL :
|
||||
```ini
|
||||
SERVER_URL=https://mytwentyapp.com
|
||||
```
|
||||
|
||||
3. **Redémarrez l'Application**
|
||||
|
||||
Pour que les changements prennent effet, redémarrez les conteneurs Docker :
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
#### Considérations
|
||||
|
||||
- **Configuration du Proxy Inverse :**
|
||||
|
||||
Assurez-vous que votre proxy inverse redirige les requêtes vers le port interne correct (`3000` par défaut). Configurez la terminaison SSL et tous les en-têtes nécessaires.
|
||||
|
||||
- **Paramètres de Pare-feu :**
|
||||
|
||||
Ouvrez les ports nécessaires dans votre pare-feu pour permettre l'accès externe.
|
||||
|
||||
- **Cohérence :**
|
||||
|
||||
Le `SERVER_URL` doit correspondre à la façon dont les utilisateurs accèdent à votre application dans leurs navigateurs.
|
||||
|
||||
#### Persistance
|
||||
|
||||
- **Volumes de Données :**
|
||||
|
||||
La configuration de Docker Compose utilise des volumes pour persister les données pour la base de données et le stockage du serveur.
|
||||
|
||||
- **Environnements sans État :**
|
||||
|
||||
Si vous déployez dans un environnement sans état (par exemple, certains services cloud), configurez un stockage externe pour persister les données.
|
||||
|
||||
## Résolution des problèmes
|
||||
|
||||
Si vous rencontrez un problème, consultez [Dépannage](https://docs.twenty.com/developers/self-hosting/troubleshooting) pour des solutions.
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
---
|
||||
title: Guide de mise à niveau
|
||||
image: /images/user-guide/notes/notes_header.png
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/notes/notes_header.png" alt="Header" />
|
||||
</Frame>
|
||||
|
||||
## Consignes générales
|
||||
|
||||
**Assurez-vous toujours de sauvegarder votre base de données avant de commencer le processus de mise à niveau** en exécutant `docker exec -it <db_container_name_or_id> pg_dumpall -U <postgres_user> > databases_backup.sql`.
|
||||
|
||||
Pour restaurer la sauvegarde, exécutez `cat databases_backup.sql | docker exec -i <db_container_name_or_id> psql -U <postgres_user>`.
|
||||
|
||||
Si vous avez utilisé Docker Compose, suivez ces étapes :
|
||||
|
||||
1. Dans un terminal, sur l'hôte où Twenty est en cours d'exécution, éteignez Twenty: `docker compose down`
|
||||
|
||||
2. Mettez à niveau la version en changeant la valeur `TAG` dans le fichier .env près de votre docker-compose. (Nous recommandons de consommer la version `major.minor` telle que `v0.53`)
|
||||
|
||||
3. Remettez Twenty en ligne avec `docker compose up -d`
|
||||
|
||||
Si vous souhaitez mettre à niveau votre instance de quelques versions, par exemple de la v0.33.0 à la v0.35.0, vous devez mettre à niveau votre instance de manière séquentielle, dans cet exemple de la v0.33.0 à la v0.34.0, puis de la v0.34.0 à la v0.35.0.
|
||||
|
||||
**Assurez-vous qu'après chaque version mise à niveau, votre sauvegarde ne soit pas corrompue.**
|
||||
|
||||
## Étapes de mise à niveau spécifiques à la version
|
||||
|
||||
## v1.0
|
||||
|
||||
Bonjour Twenty v1.0 ! 🎉
|
||||
|
||||
## v0.60
|
||||
|
||||
### Améliorations des performances
|
||||
|
||||
Toutes les interactions avec l'API des métadonnées ont été optimisées pour de meilleures performances, en particulier pour la manipulation des métadonnées d'objets et les opérations de création d'espaces de travail.
|
||||
|
||||
Nous avons remanié notre stratégie de mise en cache pour privilégier les accès cache plutôt que les requêtes de base de données lorsque c'est possible, améliorant ainsi significativement les performances des opérations de l'API des métadonnées.
|
||||
|
||||
Si vous rencontrez des problèmes d'exécution après la mise à niveau, vous devrez peut-être vider votre cache pour vous assurer qu'il est synchronisé avec les dernières modifications. Exécutez cette commande dans votre conteneur twenty-server :
|
||||
|
||||
```bash
|
||||
yarn command:prod cache:flush
|
||||
```
|
||||
|
||||
### v0.55
|
||||
|
||||
Mettez à niveau votre instance Twenty pour utiliser l'image v0.55
|
||||
|
||||
Vous n'avez plus besoin de lancer de commande, la nouvelle image se chargera automatiquement de l'exécution de toutes les migrations requises.
|
||||
|
||||
### Erreur `L'utilisateur n'a pas d'autorisation`
|
||||
|
||||
Si vous rencontrez des erreurs d'autorisation sur la plupart des requêtes après la mise à niveau, vous devrez peut-être vider votre cache pour recalculer les dernières autorisations.
|
||||
|
||||
Dans votre conteneur `twenty-server`, exécutez :
|
||||
|
||||
```bash
|
||||
yarn command:prod cache:flush
|
||||
```
|
||||
|
||||
Ce problème est spécifique à cette version de Twenty et ne devrait pas être nécessaire pour les futures mises à niveau.
|
||||
|
||||
### v0.54
|
||||
|
||||
Depuis la version `0.53`, aucune action manuelle n'est nécessaire.
|
||||
|
||||
#### Abandon du schéma de métadonnées
|
||||
|
||||
Nous avons fusionné le schéma `metadata` dans celui de `core` pour simplifier la récupération de données de `TypeORM`.
|
||||
Nous avons intégré l'étape de commande `migrate` dans la commande `upgrade`. Nous ne recommandons pas l'exécution manuelle de `migrate` dans l'un de vos conteneurs serveur/travailleur.
|
||||
|
||||
### Depuis la v0.53
|
||||
|
||||
À partir de `0.53`, la mise à niveau est effectuée de manière programmatique au sein du `DockerFile`, cela signifie que vous ne devez plus exécuter de commande manuellement.
|
||||
|
||||
Assurez-vous de continuer à mettre à jour votre instance de manière séquentielle, sans sauter de version majeure (par exemple, de `0.43.3` à `0.44.0` est autorisé, mais de `0.43.1` à `0.45.0` ne l'est pas), sinon cela pourrait entraîner une désynchronisation de la version de l'espace de travail pouvant entraîner une erreur d'exécution et des fonctionnalités manquantes.
|
||||
|
||||
Pour vérifier si un espace de travail a été correctement migré, vous pouvez consulter sa version dans la base de données dans la table `core.workspace`.
|
||||
|
||||
Il doit toujours être dans la plage de la version `major.minor` de votre instance Twenty actuelle, vous pouvez consulter la version de votre instance dans le panneau d'administration (à l'adresse `/settings/admin-panel`, accessible si votre utilisateur a la propriété `canAccessFullAdminPanel` définie comme vraie dans la base de données) ou en exécutant `echo $APP_VERSION` dans votre conteneur `twenty-server`.
|
||||
|
||||
Pour corriger une version d'espace de travail désynchronisée, vous devrez effectuer une mise à niveau à partir de la version correspondante de Twenty en suivant le guide de mise à niveau correspondant séquentiellement, et ainsi de suite jusqu'à atteindre la version souhaitée.
|
||||
|
||||
#### Suppression de `auditLog`
|
||||
|
||||
Nous avons supprimé l'objet standard auditLog, ce qui signifie que la taille de votre sauvegarde pourrait être considérablement réduite après cette migration.
|
||||
|
||||
### v0.51 à v0.52
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.52
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
#### J'ai un espace de travail bloqué dans la version entre `0.52.0` et `0.52.6`
|
||||
|
||||
Malheureusement, `0.52.0` et `0.52.6` ont été entièrement supprimés de dockerHub.
|
||||
Vous devrez mettre à jour manuellement la version de votre espace de travail à `0.51.0` dans la base de données et mettre à niveau en utilisant la version twenty `0.52.11` en suivant son guide de mise à niveau juste au-dessus.
|
||||
|
||||
### v0.50 à v0.51
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.51
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
### v0.44.0 à v0.50.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.50.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
#### Mutation docker-compose.yml
|
||||
|
||||
Cette version inclut une mutation `docker-compose.yml` pour donner au service `worker` accès au volume `server-local-data`.
|
||||
Veuillez mettre à jour votre `docker-compose.yml` local avec [docker-compose.yml v0.50.0](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml)
|
||||
|
||||
### v0.43.0 à v0.44.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.44.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
### v0.42.0 à v0.43.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.43.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade
|
||||
```
|
||||
|
||||
Dans cette version, nous avons également changé pour l'image postgres:16 dans `docker-compose.yml`.
|
||||
|
||||
#### (Option 1) Migration de base de données
|
||||
|
||||
Conserver l'image postgres-spilo existante est correct, mais vous devrez geler la version dans votre docker-compose.yml à 0.43.0.
|
||||
|
||||
#### (Option 2) Migration de base de données
|
||||
|
||||
Si vous souhaitez migrer votre base de données vers la nouvelle image postgres:16, suivez ces étapes :
|
||||
|
||||
1. Exportez votre base de données depuis l'ancien conteneur postgres-spilo
|
||||
|
||||
```
|
||||
docker exec -it twenty-db-1 sh
|
||||
pg_dump -U {YOUR_POSTGRES_USER} -d {YOUR_POSTGRES_DB} > databases_backup.sql
|
||||
exit
|
||||
docker cp twenty-db-1:/home/postgres/databases_backup.sql .
|
||||
```
|
||||
|
||||
Assurez-vous que votre fichier de sauvegarde n'est pas vide.
|
||||
|
||||
2. Mettez à jour votre docker-compose.yml pour utiliser l'image postgres:16 comme dans le fichier [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml).
|
||||
|
||||
3. Restaurez la base de données vers le nouveau conteneur postgres:16
|
||||
|
||||
```
|
||||
docker cp databases_backup.sql twenty-db-1:/databases_backup.sql
|
||||
docker exec -it twenty-db-1 sh
|
||||
psql -U {YOUR_POSTGRES_USER} -d {YOUR_POSTGRES_DB} -f databases_backup.sql
|
||||
exit
|
||||
```
|
||||
|
||||
### v0.41.0 à v0.42.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.42.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.42
|
||||
```
|
||||
|
||||
**Variables d'environnement**
|
||||
|
||||
- Supprimé : `FRONT_PORT`, `FRONT_PROTOCOL`, `FRONT_DOMAIN`, `PORT`
|
||||
- Ajouté : `FRONTEND_URL`, `NODE_PORT`, `MAX_NUMBER_OF_WORKSPACES_DELETED_PER_EXECUTION`, `MESSAGING_PROVIDER_MICROSOFT_ENABLED`, `CALENDAR_PROVIDER_MICROSOFT_ENABLED`, `IS_MICROSOFT_SYNC_ENABLED`
|
||||
|
||||
### v0.40.0 à v0.41.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.41.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.41
|
||||
```
|
||||
|
||||
**Variables d'environnement**
|
||||
|
||||
- Supprimé : `AUTH_MICROSOFT_TENANT_ID`
|
||||
|
||||
### v0.35.0 à v0.40.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.40.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.40
|
||||
```
|
||||
|
||||
**Variables d'environnement**
|
||||
|
||||
- Ajouté : `IS_EMAIL_VERIFICATION_REQUIRED`, `EMAIL_VERIFICATION_TOKEN_EXPIRES_IN`, `WORKFLOW_EXEC_THROTTLE_LIMIT`, `WORKFLOW_EXEC_THROTTLE_TTL`
|
||||
|
||||
### v0.34.0 à v0.35.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.35.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.35
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.35` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
**Variables d'environnement**
|
||||
|
||||
- Nous avons remplacé `ENABLE_DB_MIGRATIONS` par `DISABLE_DB_MIGRATIONS` (la valeur par défaut est désormais `false`, vous n'avez probablement rien à paramétrer)
|
||||
|
||||
### v0.33.0 à v0.34.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.34.0
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.34
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.34` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
**Variables d'environnement**
|
||||
|
||||
- Supprimé : `FRONT_BASE_URL`
|
||||
- Ajouté : `FRONT_DOMAIN`, `FRONT_PROTOCOL`, `FRONT_PORT`
|
||||
|
||||
Nous avons mis à jour notre méthode de gestion des URL frontend.
|
||||
Vous pouvez maintenant définir l'URL frontend en utilisant les variables `FRONT_DOMAIN`, `FRONT_PROTOCOL` et `FRONT_PORT`.
|
||||
Si FRONT_DOMAIN n'est pas défini, l'URL frontend reviendra à `SERVER_URL`.
|
||||
|
||||
### v0.32.0 à v0.33.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.33.0
|
||||
|
||||
```
|
||||
yarn command:prod cache:flush
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.33
|
||||
```
|
||||
|
||||
La commande `yarn command:prod cache:flush` videra le cache Redis.
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.33` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
À partir de cette version, l'image twenty-postgres pour la DB est devenue obsolète et twenty-postgres-spilo est utilisé à la place.
|
||||
Si vous souhaitez continuer à utiliser l'image twenty-postgres, remplacez simplement `twentycrm/twenty-postgres:${TAG}` par `twentycrm/twenty-postgres` dans docker-compose.yml.
|
||||
|
||||
### v0.31.0 à v0.32.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.32.0
|
||||
|
||||
**Migration de schéma et de données**
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.32
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.32` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
**Variables d'environnement**
|
||||
|
||||
Nous avons mis à jour notre méthode de gestion de la connexion Redis.
|
||||
|
||||
- Supprimé : `REDIS_HOST`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`
|
||||
- Ajouté : `REDIS_URL`
|
||||
|
||||
Mettez à jour votre fichier `.env` pour utiliser la nouvelle variable `REDIS_URL` au lieu des paramètres de connexion Redis individuels.
|
||||
|
||||
Nous avons également simplifié notre méthode de gestion des tokens JWT.
|
||||
|
||||
- Supprimé : `ACCESS_TOKEN_SECRET`, `LOGIN_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `FILE_TOKEN_SECRET`
|
||||
- Ajouté : `APP_SECRET`
|
||||
|
||||
Mettez à jour votre fichier `.env` pour utiliser la nouvelle variable `APP_SECRET` à la place des secrets de tokens individuels (vous pouvez utiliser le même secret qu'avant ou générer une nouvelle chaîne aléatoire).
|
||||
|
||||
**Compte connecté**
|
||||
|
||||
Si vous utilisez un compte connecté pour synchroniser vos emails et calendriers Google, vous devrez activer l'API des personnes sur votre console d'administration Google.
|
||||
|
||||
### v0.30.0 à v0.31.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.31.0
|
||||
|
||||
**Migration de schéma et de données :**
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.31
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.31` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
### v0.24.0 à v0.30.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.30.0
|
||||
|
||||
**Changement significatif**:
|
||||
Pour améliorer les performances, Twenty nécessite désormais la configuration de cache redis. Nous avons mis à jour notre [docker-compose.yml](https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml) pour refléter cela.
|
||||
Assurez-vous de mettre à jour votre configuration et vos variables d'environnement en conséquence :
|
||||
|
||||
```
|
||||
REDIS_HOST={your-redis-host}
|
||||
REDIS_PORT={your-redis-port}
|
||||
CACHE_STORAGE_TYPE=redis
|
||||
```
|
||||
|
||||
**Migration de schéma et de données :**
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.30
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.30` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
### v0.23.0 à v0.24.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.24.0
|
||||
|
||||
Exécutez les commandes suivantes :
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.24
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la structure de la base de données (schémas core et metadata)
|
||||
La commande `yarn command:prod upgrade-0.24` s'occupe de la migration des données de tous les espaces de travail.
|
||||
|
||||
### v0.22.0 à v0.23.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.23.0
|
||||
|
||||
Exécutez les commandes suivantes :
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod upgrade-0.23
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la base de données.
|
||||
La commande `yarn command:prod upgrade-0.23` s'occupe de la migration des données, y compris le transfert des activités vers les tâches/notes.
|
||||
|
||||
### v0.21.0 à v0.22.0
|
||||
|
||||
Mettez à jour votre instance Twenty pour utiliser l'image v0.22.0
|
||||
|
||||
Exécutez les commandes suivantes :
|
||||
|
||||
```
|
||||
yarn database:migrate:prod
|
||||
yarn command:prod workspace:sync-metadata -f
|
||||
yarn command:prod upgrade-0.22
|
||||
```
|
||||
|
||||
La commande `yarn database:migrate:prod` appliquera les migrations à la base de données.
|
||||
La commande `yarn command:prod workspace:sync-metadata -f` synchronisera la définition des objets standard avec les tables de métadonnées et appliquera les migrations nécessaires aux espaces de travail existants.
|
||||
La commande `yarn command:prod upgrade-0.22` appliquera des transformations de données spécifiques pour s’adapter aux nouvelles `defaultRequestInstrumentationOptions` d’objet.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user