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

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

View File

@@ -0,0 +1,22 @@
---
title: Beste Praktiken
---
Dieses Dokument beschreibt die Best Practices, die Sie bei der Arbeit am Backend befolgen sollten.
## Verfolgen Sie einen modularen Ansatz
Das Backend folgt einem modularen Ansatz, was ein grundlegendes Prinzip bei der Arbeit mit NestJS ist. Stellen Sie sicher, dass Sie Ihren Code in wiederverwendbare Module aufteilen, um eine saubere und organisierte Codebasis aufrechtzuerhalten.
Jedes Modul sollte ein bestimmtes Feature oder eine Funktionalität kapseln und einen klar definierten Umfang haben. Dieser modulare Ansatz ermöglicht eine klare Trennung der Verantwortlichkeiten und beseitigt unnötige Komplexitäten.
## Dienste zur Verwendung in Modulen bereitstellen
Erstellen Sie immer Dienste mit einer klaren Einzelverantwortung, was die Lesbarkeit und Wartbarkeit des Codes verbessert. Benennen Sie die Dienste beschreibend und konsistent.
Sie sollten auch Dienste bereitstellen, die Sie in anderen Modulen verwenden möchten. Das Bereitstellen von Diensten für andere Module ist durch das leistungsstarke Abhängigkeitsinjektionssystem von NestJS möglich und fördert die lose Kopplung zwischen den Komponenten.
## Vermeiden Sie die Verwendung des Typs `any`
Wenn Sie eine Variable als `any` deklarieren, führt der TypeScript-Typenprüfer keine Typprüfung durch, was es möglich macht, der Variablen Werte beliebigen Typs zuzuweisen. TypeScript verwendet Typinferenz, um den Typ einer Variable basierend auf dem Wert zu bestimmen. Durch die Deklaration als `any` kann TypeScript den Typ nicht mehr ableiten. Dies erschwert es, typbezogene Fehler während der Entwicklung zu erkennen, was zu Laufzeitfehlern führt und den Code weniger wartbar, weniger zuverlässig und schwerer verständlich für andere macht.
Deshalb sollte alles einen Typ haben. Wenn Sie also ein neues Objekt mit Vor- und Nachnamen erstellen, sollten Sie eine Schnittstelle oder einen Typ definieren, der Vor- und Nachnamen enthält und die Struktur des Objekts definiert, mit dem Sie arbeiten.

View File

@@ -0,0 +1,39 @@
---
title: Benutzerdefinierte Objekte
---
Objekte sind Strukturen, die es Ihnen ermöglichen, Daten (Aufzeichnungen, Attribute und Werte) zu speichern, die spezifisch für eine Organisation sind. Twenty bietet sowohl Standard- als auch benutzerdefinierte Objekte.
Standardobjekte sind systemeigene Objekte mit einer Reihe von Attributen, die allen Benutzern zur Verfügung stehen. Beispiele für Standardobjekte in Twenty sind Unternehmen und Person. Standardobjekte verfügen über Standardfelder, die ebenfalls allen Twenty-Nutzern zur Verfügung stehen, wie z.B. Unternehmen.displayName.
Benutzerdefinierte Objekte sind Objekte, die Sie erstellen können, um Informationen zu speichern, die einzigartig für Ihre Organisation sind. Sie sind nicht eingebaut; Mitglieder Ihres Arbeitsbereichs können benutzerdefinierte Objekte erstellen und anpassen, um Informationen zu speichern, für die Standardobjekte nicht geeignet sind.
## Schema auf hoher Ebene
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Schema auf hoher Ebene" />
</div>
<br />
## Wie es funktioniert
Benutzerdefinierte Objekte stammen aus Metadatentabellen, die Form, Namen und Typ der Objekte bestimmen. Alle diese Informationen sind in der Metadaten-Schema-Datenbank vorhanden, die aus Tabellen besteht:
* **Datenquelle**: Gibt an, wo die Daten vorhanden sind.
* **Objekt**: Beschreibt das Objekt und verlinkt zu einer Datenquelle.
* **Feld**: Umreißt die Felder eines Objekts und verbindet es mit dem Objekt.
Um ein benutzerdefiniertes Objekt hinzuzufügen, wird das Arbeitsbereichsmitglied die /metadata API abfragen. Dies aktualisiert die Metadaten entsprechend und berechnet ein GraphQL-Schema basierend auf den Metadaten, das in einem GQL-Cache für die spätere Verwendung gespeichert wird.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/add-custom-objects.jpeg" alt="Fragen Sie die /metadata API ab, um benutzerdefinierte Objekte hinzuzufügen" />
</div>
<br />
Um Daten abzurufen, wird der Prozess durchgeführt, indem Abfragen über den /graphql-Endpunkt erstellt und über den Abfrage-Resolver weitergeleitet werden.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Fragen Sie den /graphql-Endpunkt ab, um Daten abzurufen" />
</div>

View File

@@ -0,0 +1,46 @@
---
title: Feature-Flags
---
Feature-Flags werden verwendet, um experimentelle Funktionen auszublenden. Für Twenty werden sie auf der Arbeitsbereichsebene und nicht auf Benutzerebene festgelegt.
## Hinzufügen eines neuen Feature-Flags
Fügen Sie in `FeatureFlagKey.ts` das Feature-Flag hinzu:
```ts
type FeatureFlagKey =
| 'IS_FEATURENAME_ENABLED'
| ...;
```
Fügen Sie es auch dem Enum in `feature-flag.entity.ts` hinzu:
```ts
enum FeatureFlagKeys {
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
...
}
```
Um ein Funktions-Flag auf einem **Backend**-Feature anzuwenden, verwenden Sie:
```ts
@Gate({
featureFlag: 'IS_FEATURENAME_ENABLED',
})
```
Um ein Funktions-Flag auf einem **Frontend**-Feature anzuwenden, verwenden Sie:
```ts
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
```
## Konfigurieren Sie Funktions-Flags für die Bereitstellung
Ändern Sie den entsprechenden Eintrag in der Tabelle `core.featureFlag`:
| iD | schlüssel | workspaceId | wert |
| -------- | ------------------------ | ------------------ | ------ |
| Zufällig | `IS_FEATURENAME_ENABLED` | Arbeitsbereichs-ID | `wahr` |

View File

@@ -0,0 +1,125 @@
---
title: Ordnerarchitektur
info: Ein detaillierter Einblick in unsere Server-Ordnerarchitektur
---
Die Server-Verzeichnisstruktur ist wie folgt:
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
```
## Fähigkeit
Definiert Berechtigungen und enthält Handler für jedes Element.
## Dekoratoren
Definiert benutzerdefinierte Dekoratoren in NestJS für zusätzliche Funktionen.
Weitere Details finden Sie unter [benutzerdefinierte Dekoratoren](https://docs.nestjs.com/custom-decorators).
## Filter
Enthält Ausnahmefilter, um mögliche Ausnahmen bei GraphQL-Endpunkten zu behandeln.
## Guards
Weitere Details finden Sie unter [Guards](https://docs.nestjs.com/guards).
## Health
Enthält eine öffentlich zugängliche REST-API (healthz), die ein JSON zurückgibt, um zu bestätigen, dass die Datenbank wie erwartet funktioniert.
## Metadaten
Definiert benutzerdefinierte Objekte und stellt eine GraphQL-API (graphql/metadaten) bereit.
## Arbeitsbereich
Erzeugt und stellt ein benutzerdefiniertes GraphQL-Schema basierend auf den Metadaten bereit.
### Verzeichnisstruktur des Workspaces
```
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
```
Der Stamm des Arbeitsbereichsverzeichnisses umfasst die Datei `arbeitsbereich.fabrik.ts`, die die Funktion `createGraphQLSchema` enthält. Diese Funktion generiert arbeitsbereichsspezifische Schemata, indem sie die Metadaten nutzt, um ein Schema für einzelne Arbeitsbereiche zu gestalten. Durch die Trennung von Schema- und Resolver-Erstellung verwenden wir die Funktion `makeExecutableSchema`, die diese diskreten Elemente kombiniert.
Diese Strategie dient nicht nur der Organisation, sondern hilft auch bei der Optimierung, wie z.B. dem Caching generierter Typdefinitionen zur Leistungs- und Skalierbarkeitssteigerung.
### Workspace-Schema-Builder
Generiert das GraphQL-Schema und umfasst:
#### Fabriken:
Spezialisierte Konstruktoren, um GraphQL-bezogene Konstrukte zu generieren.
* Die type.factory übersetzt Feldmetadaten in GraphQL-Typen unter Verwendung des `TypeMapperService`.
* Die type-definition.factory erstellt GraphQL-Eingabe- oder Ausgabeobjekte, die aus `objektMetadaten` abgeleitet sind.
#### GraphQL-Typen
Umfasst Aufzählungen, Eingaben, Objekte und Skalare und dient als Bausteine für die Schemakonstruktion.
#### Schnittstellen und Objektdefinitionen
Enthält die Blaupausen für GraphQL-Entitäten und umfasst sowohl vordefinierte als auch benutzerdefinierte Typen wie `MONEY` oder `URL`.
#### Dienste
Enthält den Service, der dafür verantwortlich ist, FieldMetadataType dem passenden GraphQL-Scalar oder den entsprechenden Abfrage-Modifikatoren zuzuordnen.
#### Speicher
Enthält die Klasse `TypeDefinitionsStorage`, die wiederverwendbare Typdefinitionen enthält und die Duplizierung von GraphQL-Typen verhindert.
### Workspace-Resolver-Builder
Erstellt Resolverfunktionen für die Abfrage und Änderung des GraphQL-Schemas.
Jede Fabrik in diesem Verzeichnis ist für die Erstellung eines bestimmten Resolvertyps verantwortlich, wie die `FindManyResolverFactory`, die für eine vielseitige Anwendung auf verschiedenen Tabellen entwickelt wurde.
### Arbeitsbereich Anfrage Ausführer
Führt die generierten Anfragen auf der Datenbank aus und analysiert das Ergebnis.

View File

@@ -0,0 +1,41 @@
---
title: Nachrichtenwarteschlange
---
Warteschlangen erleichtern die Durchführung asynchroner Vorgänge. Sie können für die Ausführung von Hintergrundaufgaben verwendet werden, wie z.B. das Versenden einer Willkommens-E-Mail bei der Registrierung.
Jeder Anwendungsfall hat seine eigene Warteschlangenklasse, die von `MessageQueueServiceBase` erweitert wird.
Derzeit unterstützen wir nur `bull-mq`[bull-mq](https://bullmq.io/) als Warteschlangentreiber.
## Schritte zur Erstellung und Nutzung einer neuen Warteschlange
1. Fügen Sie einen Warteschlangennamen für Ihre neue Warteschlange unter dem Enum `MESSAGE_QUEUES` hinzu.
2. Stellen Sie die Factory-Implementierung der Warteschlange mit dem Warteschlangennamen als Abhängigkeits-Token bereit.
3. Injizieren Sie die von Ihnen erstellte Warteschlange im erforderlichen Modul/Service mit dem Warteschlangennamen als Abhängigkeits-Token.
4. Fügen Sie eine Worker-Klasse mit tokenbasierter Injektion hinzu, genau wie der Producer.
### Beispielverwendung
```ts
class Resolver {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {}
async onSomeAction() {
//business logic
await this.queue.add(someData);
}
}
//async worker
class CustomWorker {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {
this.initWorker();
}
async initWorker() {
await this.queue.work(async ({ id, data }) => {
//worker logic
});
}
}
```

View File

@@ -0,0 +1,87 @@
---
title: Backend Befehle
icon: terminal
---
## Nützliche Befehle
Diese Befehle sollten aus dem Verzeichnis packages/twenty-server ausgeführt werden.
Von jedem anderen Verzeichnis aus können Sie `npx nx {command} twenty-server` (oder `npx nx run twenty-server:{command}`) verwenden.
### Erstmalige Einrichtung
```
npx nx database:reset twenty-server # setup the database with dev seeds
```
### Server starten
```
npx nx run twenty-server:start
```
### Lint
```
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
```
Hinweis: Sie können `npx nx run twenty-server:test:integration:with-db-reset` ausführen, falls Sie die Datenbank zurücksetzen müssen, bevor Sie die Integrationstests durchführen.
### Datenbank zurücksetzen
Wenn Sie die Datenbank zurücksetzen und seeden möchten, können Sie den folgenden Befehl ausführen:
```bash
npx nx run twenty-server:database:reset
```
### Migrationen
#### Für Objekte in Core/Metadata-Schemas (TypeORM)
```bash
npx nx run twenty-server:database:migrate:generate
```
## Technologie-Stack
Twenty verwendet in erster Linie NestJS für das Backend.
Prisma war das erste ORM, das wir verwendeten. Um Benutzern die Möglichkeit zu geben, benutzerdefinierte Felder und Objekte zu erstellen, war eine niedrigere Ebene sinnvoller, da wir eine feinkörnige Kontrolle benötigen. Das Projekt verwendet jetzt TypeORM.
So sieht der Tech-Stack jetzt aus.
**Kern**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
**Datenbank**
* [Postgres](https://www.postgresql.org/)
**Externe Integrationen**
* [Sentry](https://sentry.io/welcome/) zum Verfolgen von Fehlern
**Tests**
* [Jest](https://jestjs.io/)
**Tools**
* [Yarn](https://yarnpkg.com/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
**Entwicklung**
* [AWS EKS](https://aws.amazon.com/eks/)

View File

@@ -0,0 +1,85 @@
---
title: Zapier App
---
Synchronisieren Sie mühelos Twenty mit über 3000 Apps mit [Zapier](https://zapier.com/). Automatisieren Sie Aufgaben, steigern Sie die Produktivität und stärken Sie Ihre Kundenbeziehungen!
## Über Zapier
Zapier ist ein Tool, das es Ihnen ermöglicht, Workflows zu automatisieren, indem Sie die Apps verbinden, die Ihr Team täglich nutzt. Das grundlegende Konzept von Zapier sind automatische Workflows, genannt Zaps, die Trigger und Aktionen umfassen.
Erfahren Sie mehr darüber, wie Zapier funktioniert [hier](https://zapier.com/how-it-works).
## Einrichtung
### Schritt 1: Installieren Sie die Zapier-Pakete
```bash
cd packages/twenty-zapier
yarn
```
### Schritt 2: Anmelden mit dem CLI
Verwenden Sie Ihre Zapier-Anmeldedaten, um sich über das CLI anzumelden:
```bash
zapier login
```
### Schritt 3: Einrichten von Umgebungsvariablen
Führen Sie im Ordner `packages/twenty-zapier` aus:
```bash
cp .env.example .env
```
Führen Sie die Anwendung lokal aus, gehen Sie zu [http://localhost:3000/settings/api-webhooks](http://localhost:3000/settings/api-webhooks), und generieren Sie einen API-Schlüssel.
Ersetzen Sie den Wert **YOUR_API_KEY** in der Datei `.env` durch den gerade generierten API-Schlüssel.
## Entwicklung
<Warning>
Stellen Sie sicher, dass Sie `yarn build` vor jedem `zapier`-Befehl ausführen.
</Warning>
### Test
```bash
yarn test
```
### Lint
```bash
yarn format
```
### Beobachten und kompilieren Sie, während Sie den Code bearbeiten
```bash
yarn watch
```
### Validieren Sie Ihre Zapier-App
```bash
yarn validate
```
### Stellen Sie Ihre Zapier-App bereit
```bash
yarn deploy
```
### Listet alle Zapier CLI-Befehle auf
```bash
zapier
```

View File

@@ -0,0 +1,79 @@
---
title: Fehlermeldungen, Anfragen & Pull Requests
icon: bug
info: Probleme melden, Funktionen vorschlagen und Code beisteuern
---
## Fehler melden
Um einen Fehler zu melden, [erstellen Sie bitte ein Issue auf GitHub](https://github.com/twentyhq/twenty/issues/new).
Sie können auch um Hilfe auf [Discord](https://discord.gg/cx5n4Jzs57) bitten.
## Feature-Anfragen
Wenn Sie sich nicht sicher sind, ob es sich um einen Fehler handelt, und glauben, dass es eher eine Funktionsanforderung ist, sollten Sie wahrscheinlich [stattdessen eine Diskussion eröffnen](https://github.com/twentyhq/twenty/discussions/new).
## Einen Pull Request einreichen
Codebeiträge zu Twenty beginnen mit einem Pull Request (PR).
### Bevor Sie beginnen
1. Prüfen Sie [bestehende Issues](https://github.com/twentyhq/twenty/issues) auf verwandte Arbeiten
2. Für neue Features zunächst ein Issue eröffnen, um dies zu besprechen
3. Lesen Sie unseren [Verhaltenskodex](https://github.com/twentyhq/twenty/blob/main/.github/CODE_OF_CONDUCT.md)
### Forken und klonen
1. Das Repository auf GitHub forken
2. Ihren Fork klonen:
```bash
git clone https://github.com/YOUR_USERNAME/twenty.git
cd twenty
```
3. Upstream-Remote hinzufügen:
```bash
git remote add upstream https://github.com/twentyhq/twenty.git
```
### Einen Branch erstellen
```bash
git checkout -b feature/your-feature-name
```
Verwenden Sie aussagekräftige Branch-Namen:
* `feature/add-export-button`
* `fix/login-redirect-issue`
* `docs/update-api-guide`
### Nehmen Sie Ihre Änderungen vor
1. Schreiben Sie sauberen, gut dokumentierten Code
2. Befolgen Sie den bestehenden Code-Stil
3. Fügen Sie Tests für neue Funktionalität hinzu
4. Aktualisieren Sie bei Bedarf die Dokumentation
### Reichen Sie Ihren PR ein
1. Pushen Sie Ihren Branch:
```bash
git push origin feature/your-feature-name
```
2. Öffnen Sie einen PR auf GitHub
3. Füllen Sie die PR-Vorlage aus
4. Verknüpfen Sie zugehörige Issues
### PR-Checkliste
* [ ] Der Code entspricht den Stilrichtlinien des Projekts
* [ ] Die Tests bestehen lokal
* [ ] Die Dokumentation ist aktualisiert
* [ ] Die PR-Beschreibung erläutert die Änderungen

View File

@@ -0,0 +1,329 @@
---
title: Beste Praktiken
icon: star
---
Dieses Dokument beschreibt die besten Praktiken, die Sie beim Arbeiten am Frontend beachten sollten.
## Zustandsverwaltung
React und Jotai übernehmen die Zustandsverwaltung im Code.
### Verwenden Sie Jotai-Atome, um den Zustand zu speichern
Es ist eine gute Praxis, so viele Atome zu erstellen, wie Sie benötigen, um Ihren Zustand zu speichern.
<Warning>
Es ist besser, zusätzliche Atome zu verwenden, als zu versuchen, mit Prop Drilling allzu knapp zu arbeiten.
</Warning>
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
export const myAtomState = createAtomState<string>({
key: 'myAtomState',
defaultValue: 'default value',
});
export const MyComponent = () => {
const [myAtom, setMyAtom] = useAtomState(myAtomState);
return (
<div>
<input
value={myAtom}
onChange={(e) => setMyAtom(e.target.value)}
/>
</div>
);
}
```
### Verwenden Sie `useRef` nicht, um den Zustand zu speichern.
Vermeiden Sie die Verwendung von `useRef`, um den Zustand zu speichern.
Wenn Sie den Zustand speichern möchten, sollten Sie `useState` oder Jotai-Atome mit `useAtomState` verwenden.
Sehen Sie sich [an, wie Re-Renderings verwaltet werden können](#managing-re-renders), falls Sie das Gefühl haben, dass Sie `useRef` benötigen, um einige Re-Renderings zu verhindern.
## Re-Renderings verwalten
Re-Renderings können in React schwer zu verwalten sein.
Hier sind einige Regeln, die befolgt werden sollten, um unnötige Re-Renderings zu vermeiden.
Beachten Sie, dass Sie **immer** Re-Renderings vermeiden können, indem Sie deren Ursache verstehen.
### Arbeiten Sie auf der Root-Ebene
Das Vermeiden von Re-Renderings in neuen Funktionen wird jetzt erleichtert, indem sie auf Root-Ebene eliminiert werden.
Die `PageChangeEffect`-Sidecar-Komponente enthält nur einen `useEffect`-Hook, der die gesamte Logik für den Seitenwechsel enthält.
Auf diese Weise wissen Sie, dass es nur einen Ort gibt, der ein Re-Rendering auslösen kann.
### Denken Sie immer zweimal nach, bevor Sie `useEffect` in Ihre Codebasis aufnehmen.
Re-Renderings werden oft durch unnötige `useEffect`-Verwendungen verursacht.
Sie sollten überlegen, ob Sie `useEffect` benötigen oder ob Sie die Logik in eine Event-Handler-Funktion verschieben können.
Es ist in der Regel einfach, die Logik in eine `handleClick` oder `handleChange`-Funktion zu verschieben.
Sie können sie auch in Bibliotheken wie Apollo finden: `onCompleted`, `onError`, usw.
### Verwenden Sie eine Geschwisterkomponente, um `useEffect`- oder Datenabruf-Logik auszulagern.
Wenn Sie das Gefühl haben, Ihrer Root-Komponente einen `useEffect` hinzufügen zu müssen, sollten Sie erwägen, ihn in eine Sidecar-Komponente auszulagern.
Dasselbe können Sie auch für die Datenabruflogik mit Apollo-Hooks anwenden.
```tsx
// ❌ Bad, will cause re-renders even if data is not changing,
// because useEffect needs to be re-evaluated
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <div>{data}</div>;
};
export const App = () => (
<PageComponent />
);
```
```tsx
// ✅ Good, will not cause re-renders if data is not changing,
// because useEffect is re-evaluated in another sibling component
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
return <div>{data}</div>;
};
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <></>;
};
export const App = () => (
<>
<PageData />
<PageComponent />
</>
);
```
### Verwenden Sie Atom-Familienzustände und Selektoren
Atom-Familienzustände und Selektoren sind eine großartige Möglichkeit, Re-Renders zu vermeiden.
Sie sind nützlich, wenn Sie eine Liste von Elementen speichern müssen.
### Sie sollten nicht `React.memo(MyComponent)` verwenden.
Vermeiden Sie die Verwendung von `React.memo()`, da es nicht die Ursache für das Re-Rendering löst, sondern stattdessen die Re-Render-Kette unterbricht, was zu unerwartetem Verhalten führen kann und es sehr schwierig macht, den Code zu refaktorisieren.
### Beschränken Sie die Nutzung von `useCallback` oder `useMemo`.
Oft sind sie nicht erforderlich und machen den Code schwerer zu lesen und zu warten für einen Leistungsgewinn, der kaum wahrnehmbar ist.
## Console.logs
`console.log`-Anweisungen bieten während der Entwicklung wertvolle Echtzeit-Einblicke in Variablenwerte und den Codefluss. Aber wenn sie im Produktivcode belassen werden, kann dies zu mehreren Problemen führen:
1. **Leistung**: Übermäßiges Logging kann die Laufzeitleistung beeinflussen, insbesondere bei clientseitigen Anwendungen.
2. **Sicherheit**: Das Protokollieren sensibler Daten kann kritische Informationen offenlegen für jeden, der die Konsole des Browsers inspiziert.
3. **Sauberkeit**: Das Ausfüllen der Konsole mit Logs kann wichtige Warnungen oder Fehler verdecken, die Entwickler oder Tools sehen müssen.
4. **Professionalität**: Endbenutzer oder Kunden, die die Konsole überprüfen und eine Vielzahl von Protokollanweisungen sehen, könnten die Qualität und den Feinschliff des Codes in Frage stellen.
Stellen Sie sicher, dass Sie alle `console.logs` entfernen, bevor Sie den Code in die Produktion übertragen.
## Namensgebung
### Variablenbenennung
Variablennamen sollten den Zweck oder die Funktion der Variable genau beschreiben.
#### Das Problem mit generischen Namen
Generische Namen in der Programmierung sind nicht ideal, weil ihnen die Spezifität fehlt, was zu Mehrdeutigkeit und verminderter Lesbarkeit des Codes führt. Solche Namen vermitteln nicht den Zweck der Variablen oder Funktion, was es Entwicklern erschwert, die Absicht des Codes ohne tiefere Untersuchung zu verstehen. Dies kann zu erhöhten Debugging-Zeiten, höherer Fehleranfälligkeit und Schwierigkeiten bei der Wartung und Zusammenarbeit führen. In der Zwischenzeit macht eine beschreibende Namensgebung den Code selbsterklärend und einfacher zu navigieren, was die Codequalität und die Produktivität der Entwickler verbessert.
```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('');
```
#### Einige Wörter, die in Variablennamen zu vermeiden sind
* Dummy
### Ereignis-Handler
Ereignis-Handler-Namen sollten mit `handle` beginnen, während `on` als Präfix dient, um Ereignisse in Komponenten-Props zu benennen.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## Optionale Props
Vermeiden Sie es, den Standardwert für ein optionales Prop zu übergeben.
**BEISPIEL**
Betrachten Sie die unten definierte `EmailField`-Komponente:
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
<TextInput value={value} disabled={disabled} fullWidth />
);
```
**Verwendung**
```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" />;
```
## Komponente als Props
Versuchen Sie nach Möglichkeit, nicht instanziierte Komponenten als Props zu übergeben, damit untergeordnete Komponenten selbst entscheiden können, welche Props sie weiterreichen müssen.
Das häufigste Beispiel dafür sind Icon-Komponenten:
```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>
)
};
```
Damit React versteht, dass die Komponente eine Komponente ist, müssen Sie PascalCase verwenden, damit sie später mit `<MyIcon>` instanziiert werden kann.
## Prop Drilling: Beschränken Sie es auf das Nötigste
Prop Drilling im React-Kontext bezieht sich auf die Praxis, Zustandvariablen und deren Setter durch viele Komponentenebenen zu leiten, auch wenn Zwischenkomponenten sie nicht verwenden. Obwohl es manchmal notwendig ist, kann übermäßiges Prop Drilling zu Folgendem führen:
1. **Verminderte Lesbarkeit**: Die Nachverfolgung, woher ein Prop stammt oder wo es verwendet wird, kann in einer tief verschachtelten Komponentenstruktur verworren werden.
2. **Wartungsherausforderungen**: Änderungen in der Prop-Struktur einer Komponente können Anpassungen in mehreren Komponenten erfordern, selbst wenn sie das Prop nicht direkt verwenden.
3. **Verringerte Wiederverwendbarkeit von Komponenten**: Eine Komponente, die viele Props nur zum Weiterreichen erhält, wird weniger universell und schwieriger in unterschiedlichen Kontexten wiederzuverwenden.
Wenn Sie das Gefühl haben, dass Sie übermäßig Prop Drilling einsetzen, sehen Sie sich die [Best Practices für das Zustandsmanagement](#state-management) an.
## Importe
Beim Importieren sollten Sie die vorgesehenen Aliase anstelle der vollständigen oder relativen Pfade verwenden.
**Die Aliase**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**Verwendung**
```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';
```
## Schemavalidierung
[Zod](https://github.com/colinhacks/zod) ist der Schema-Validator für ungetypte Objekte:
```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>;
```
## Breaking Changes
Führen Sie immer gründliche manuelle Tests durch, bevor Sie fortfahren, um sicherzustellen, dass keine Modifikationen anderswo Störungen verursacht haben, da Testen noch nicht umfassend integriert ist.

View File

@@ -0,0 +1,110 @@
---
title: Ordnerarchitektur
icon: folder-tree
info: Ein detaillierter Einblick in unsere Ordnerarchitektur
---
In diesem Leitfaden erkunden Sie die Details der Projektverzeichnisstruktur und wie sie zur Organisation und Wartbarkeit von Twenty beiträgt.
Indem Sie diesem Ordnerarchitekturkonzept folgen, können Sie Dateien, die sich auf spezifische Funktionen beziehen, leichter finden und sicherstellen, dass die Anwendung skalierbar und wartbar ist.
```
front
└───modules
│ └───module1
│ │ └───submodule1
│ └───module2
│ └───ui
│ │ └───display
│ │ └───inputs
│ │ │ └───buttons
│ │ └───...
└───pages
└───...
```
## Seiten
Enthält die Top-Level-Komponenten, die durch die Anwendungsrouten definiert werden. Sie importieren weitere Low-Level-Komponenten aus dem Ordner modules (weitere Details unten).
## Module
Jedes Modul repräsentiert eine Funktion oder eine Gruppe von Funktionen und umfasst deren spezifische Komponenten, Zustände und Betriebslogik.
Sie sollten alle der untenstehenden Struktur folgen. Sie können Module innerhalb von Modulen (als Submodule bezeichnet) verschachteln, und die gleichen Regeln gelten.
```
module1
└───components
│ └───component1
│ └───component2
└───constants
└───contexts
└───graphql
│ └───fragments
│ └───queries
│ └───mutations
└───hooks
│ └───internal
└───states
│ └───selectors
└───types
└───utils
```
### Kontexte
Ein Context ist eine Möglichkeit, Daten durch den Komponentenbaum weiterzugeben, ohne props auf jeder Ebene manuell weitergeben zu müssen.
Weitere Details finden Sie unter [React Context](https://react.dev/reference/react#context-hooks).
### GraphQL
Enthält Fragmente, Abfragen und Mutationen.
Weitere Details finden Sie unter [GraphQL](https://graphql.org/learn/).
* Fragmente
Ein Fragment ist ein wiederverwendbares Stück einer Abfrage, das an verschiedenen Stellen verwendet werden kann. Durch die Verwendung von Fragmenten lässt sich doppelter Code leichter vermeiden.
Weitere Details finden Sie unter [GraphQL Fragmente](https://graphql.org/learn/queries/#fragments).
* Abfragen
Weitere Details finden Sie unter [GraphQL Abfragen](https://graphql.org/learn/queries/).
* Mutationen
Weitere Details finden Sie unter [GraphQL Mutationen](https://graphql.org/learn/queries/#mutations).
### Hooks
Weitere Details finden Sie unter [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks).
### Zustände
Enthält die State-Management-Logik. [Jotai](https://jotai.org) übernimmt dies.
* Selektoren: Abgeleitete Atome (mit `createAtomSelector`) berechnen Werte aus anderen Atomen und werden automatisch memoisiert.
Die integrierte Zustandsverwaltung von React verwaltet weiterhin den Zustand innerhalb einer Komponente.
### Utils
Sollte nur wiederverwendbare reine Funktionen enthalten. Andernfalls erstellen Sie benutzerdefinierte Hooks im `hooks`-Ordner.
## UI
Enthält alle wiederverwendbaren UI-Komponenten, die in der Anwendung verwendet werden.
Dieser Ordner kann Unterordner wie `data`, `display`, `feedback` und `input` für bestimmte Komponententypen enthalten. Jede Komponente sollte in sich geschlossen und wiederverwendbar sein, sodass Sie sie in verschiedenen Teilen der Anwendung verwenden können.
Indem Sie die UI-Komponenten von den anderen Komponenten im `modules`-Ordner trennen, ist es einfacher, ein konsistentes Design beizubehalten und Änderungen an der UI vorzunehmen, ohne andere Bereiche (Geschäftslogik) der Codebasis zu beeinflussen.
## Schnittstelle und Abhängigkeiten
Sie können Code anderer Module aus jedem Modul importieren, mit Ausnahme des `ui`-Ordners. Dadurch bleibt der Code leicht testbar.
### Intern
Jeder Teil (Hooks, Zustände, ...) eines Moduls kann einen `internal`-Ordner haben, der Teile enthält, die nur innerhalb des Moduls verwendet werden.

View File

@@ -0,0 +1,91 @@
---
title: Frontend-Befehle
icon: terminal
---
## Nützliche Befehle
### Die App starten
```bash
npx nx start twenty-front
```
### GraphQL-Schema basierend auf API-GraphQL-Schema regenerieren
```bash
npx nx run twenty-front:graphql:generate --configuration=metadata
```
ODER
```bash
npx nx run twenty-front:graphql:generate
```
### Lint
```bash
npx nx run twenty-front:lint # pass --fix to fix lint errors
```
## Übersetzungen
```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)
```
## Technologie-Stack
Das Projekt hat einen sauberen und einfachen Stack mit minimalem Boilerplate-Code.
**App**
* [React](https://react.dev/)
* [Apollo](https://www.apollographql.com/docs/)
* [GraphQL Codegen](https://the-guild.dev/graphql/codegen)
* [Jotai](https://jotai.org/)
* [TypeScript](https://www.typescriptlang.org/)
**Tests**
* [Jest](https://jestjs.io/)
* [Storybook](https://storybook.js.org/)
**Tools**
* [Yarn](https://yarnpkg.com/)
* [Craco](https://craco.js.org/docs/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
## Architektur
### Routing
[React Router](https://reactrouter.com/) übernimmt das Routing.
Um unnötige [re-renders](/l/de/developers/contribute/capabilities/frontend-development/best-practices-front#managing-re-renders) zu vermeiden, befindet sich die gesamte Routing-Logik in einem `useEffect` in `PageChangeEffect`.
### Zustandsverwaltung
[Jotai](https://jotai.org/) übernimmt die Zustandsverwaltung.
Siehe [Best Practices](/l/de/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) für mehr Informationen zur Zustandsverwaltung.
## Tests
[Jest](https://jestjs.io/) dient als Werkzeug für Unittests, während [Storybook](https://storybook.js.org/) für Komponententests verwendet wird.
Jest ist hauptsächlich für das Testen von Dienstprogrammfunktionen und nicht für die Komponenten selbst gedacht.
Storybook dient zum Testen des Verhaltens isolierter Komponenten sowie zur Darstellung des Designsystems.

View File

@@ -0,0 +1,178 @@
---
title: Tastenkombinationen
---
## Einführung
Wenn Sie eine Tastenkombination abhören müssen, verwenden Sie normalerweise den `onKeyDown` Event-Listener.
In `twenty-front` jedoch könnten Sie Konflikte zwischen gleichen Tastenkombinationen haben, die in verschiedenen Komponenten, die gleichzeitig eingebunden sind, verwendet werden.
Zum Beispiel, wenn Sie eine Seite haben, die auf die Enter-Taste hört, und ein Modal das auf die Enter-Taste hört, und ein Select-Element innerhalb dieses Modals auf die Enter-Taste hört, könnte ein Konflikt entstehen, wenn alle gleichzeitig eingebunden sind.
## Der `useScopedHotkeys` Hook
Um dieses Problem zu lösen, verfügen wir über einen benutzerdefinierten Hook, der es ermöglicht, auf Tastenkombinationen zu hören, ohne Konflikte.
Sie platzieren ihn in einer Komponente, und er wird auf die Tastenkombinationen nur hören, wenn die Komponente eingebunden ist UND wenn der spezifizierte **Tastenkombinationsbereich** aktiv ist.
## Wie hört man in der Praxis auf Tastenkombinationen?
Es gibt zwei Schritte, um das Abhören von Tastenkombinationen einzurichten:
1. Setzen Sie den [Tastenkombinationsbereich](#what-is-a-hotkey-scope-), der auf Tastenkombinationen hören wird
2. Verwenden Sie den `useScopedHotkeys` Hook, um auf Tastenkombinationen zu hören
Das Einrichten von Tastenkombinationsbereichen ist auch bei einfachen Seiten erforderlich, da andere UI-Elemente wie das linke Menü oder das Befehlsmenü ebenfalls auf Tastenkombinationen hören könnten.
## Anwendungsfälle für Tastenkombinationen
Im Allgemeinen haben Sie zwei Anwendungsfälle, die Tastenkombinationen erfordern:
1. Auf einer Seite oder einer Komponente, die auf einer Seite eingebunden ist
2. In einer Modal-Komponente, die wegen einer Benutzeraktion den Fokus erhält
Der zweite Anwendungsfall kann rekursiv auftreten: ein Dropdown in einem Modal zum Beispiel.
### Auf Tastenkombinationen in einer Seite hören
Beispiel:
```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>;
};
```
### Auf Tastenkombinationen in einer Modal-Komponente hören
Für dieses Beispiel verwenden wir eine Modalkomponente, die auf die Escape-Taste hört, um ihrem übergeordneten Element mitzuteilen, dass sie geschlossen werden soll.
Hier ändert die Benutzerinteraktion den Bereich.
```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>;
};
```
Dann in der Modalkomponente:
```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>;
};
```
Es ist wichtig, dieses Muster zu verwenden, wenn du nicht sicher bist, dass die Verwendung eines useEffect mit mount/unmount ausreicht, um Konflikte zu vermeiden.
Diese Konflikte können schwer zu debuggen sein, und es kann öfter passieren, als nicht mit useEffects.
## Was ist ein Tastenkombinationsbereich?
Ein Tastenkombinationsbereich ist ein String, der einen Kontext darstellt, in dem die Tastenkombinationen aktiv sind. Er wird im Allgemeinen als Enum kodiert.
Wenn Sie den Tastenkombinationsbereich ändern, werden die Tastenkombinationen aktiviert, die auf diesen Bereich hören, und die Tastenkombinationen, die auf andere Bereiche hören, werden deaktiviert.
Sie können immer nur einen Bereich gleichzeitig setzen.
Als Beispiel sind die Tastenkombinationsbereiche für jede Seite im `PageHotkeyScope` Enum definiert:
```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',
}
```
Intern wird der aktuell ausgewählte Bereich in einem Jotai-Atom gespeichert, das in der gesamten Anwendung geteilt wird:
```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
key: 'currentHotkeyScopeState',
defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```
Aber dieses Atom sollte niemals manuell bearbeitet werden! Wir werden im nächsten Abschnitt sehen, wie man es verwendet.
## Wie funktioniert es intern?
Wir haben eine dünne Schicht über [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) erstellt, die es leistungsfähiger macht und unnötige Neu-Renderings vermeidet.
Wir erstellen außerdem ein Jotai-Atom, um den Zustand des Tastenkombinationsbereichs zu verwalten und in der gesamten Anwendung verfügbar zu machen.

View File

@@ -0,0 +1,8 @@
---
title: Storybook
description: Durchsuchen Sie die UI-Komponentenbibliothek von Twenty
---
Sehen Sie sich unsere vollständige Komponentenbibliothek und Dokumentation in Storybook an.
[Storybook öffnen →](https://storybook.twenty.com)

View File

@@ -0,0 +1,291 @@
---
title: Styleguide
icon: paintbrush
---
Dieses Dokument enthält die Regeln, die beim Schreiben von Code beachtet werden müssen.
Das Ziel ist es, eine konsistente Codebasis zu haben, die leicht lesbar und einfach zu warten ist.
Hierfür ist es besser, etwas ausführlicher zu sein als zu knapp.
Denken Sie daran, dass Code häufiger gelesen als geschrieben wird, insbesondere bei einem Open-Source-Projekt, zu dem jeder beitragen kann.
Es gibt viele Regeln, die hier nicht definiert sind, die aber automatisch durch Linters überprüft werden.
## React
### Verwenden Sie funktionale Komponenten
Verwenden Sie immer TSX-Funktionskomponenten.
Vermeiden Sie `import` mit `const`, da es schwieriger zu lesen und schwerer mit Code-Vervollständigung zu importieren ist.
```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>;
};
```
### Props
Erstellen Sie den Typ der Eigenschaften (props) und nennen Sie ihn `(ComponentName)Props`, wenn es nicht notwendig ist, ihn zu exportieren.
Verwenden Sie Destrukturierung der 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>;
```
#### Vermeiden Sie die Verwendung von `React.FC` oder `React.FunctionComponent`, um Prop-Typen zu definieren
```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 />
);
```
#### Kein einzelnes Variablen-Propspreading in JSX-Elementen
Vermeiden Sie das Propspreading einzelner Variablen in JSX-Elementen, wie `{...props}`. Diese Praxis führt oft zu weniger lesbarem und schwer wartbarem Code, da unklar ist, welche Props die Komponente erhält.
```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 }} />;
};
```
Rational
* Auf einen Blick ist klarer, welche Props der Code übergibt, was das Verständnis und die Wartung erleichtert.
* Es verhindert eine enge Kopplung von Komponenten über ihre Props.
* Linting-Tools erleichtern das Erkennen falsch geschriebener oder unbenutzter Props, wenn Sie Props explizit auflisten.
## JavaScript
### Verwenden Sie den Nullish-Coalescing-Operator `??`
```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';
```
### Verwenden Sie optionales Chaining `?.`
```tsx
// ❌ Bad
onClick && onClick();
// ✅ Good
onClick?.();
```
## TypeScript
### Verwenden Sie `type` anstelle von `interface`
Verwenden Sie immer `type` anstelle von `interface`, da sie fast immer überlappen und `type` flexibler ist.
```tsx
// ❌ Bad
interface MyInterface {
name: string;
}
// ✅ Good
type MyType = {
name: string;
};
```
### Verwenden Sie String-Literale anstelle von Enums
[String-Literale](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) sind die bevorzugte Methode, um enum-ähnliche Werte in TypeScript zu handhaben. Sie sind einfacher mit Pick und Omit zu erweitern und bieten eine bessere Entwicklererfahrung, vor allem mit Code-Vervollständigung.
Warum TypeScript empfiehlt, Enums zu vermeiden, sehen Sie [hier](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 und interne Bibliotheken
Sie sollten die von GraphQL Codegen generierten Enums verwenden.
Es ist auch besser, ein Enum zu verwenden, wenn eine interne Bibliothek verwendet wird, damit die interne Bibliothek keinen String-Literal-Typ freigeben muss, der nicht zur internen API gehört.
Beispiel:
```TSX
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
```
## Styling
### Verwenden Sie StyledComponents
Stylen Sie die Komponenten mit [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
<div className="my-class">Hello World</div>
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
Prefixen Sie stilisierte Komponenten mit "Styled", um sie von "echten" Komponenten zu unterscheiden.
```tsx
// ❌ Bad
const Title = styled.div`
color: red;
`;
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
### Themenbindung
Die Nutzung des Themas für den Großteil der Komponenten-Styling ist der bevorzugte Ansatz.
#### Einheiten von Messungen
Vermeiden Sie die Verwendung direkter `px`- oder `rem`-Werte innerhalb der gestylten Komponenten. Die erforderlichen Werte sind in der Regel bereits im Thema definiert, daher wird empfohlen, das Thema für diese Zwecke zu nutzen.
#### Farben
Vermeiden Sie es, neue Farben einzuführen; verwenden Sie stattdessen die vorhandene Palette aus dem Thema. Sollte es eine Situation geben, in der die Palette nicht übereinstimmt, hinterlassen Sie bitte einen Kommentar, damit das Team dies korrigieren kann.
```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};
`;
```
## Durchsetzung von No-Type Imports
Vermeiden Sie Typ-Importe. Um diesen Standard durchzusetzen, überprüft eine Oxlint-Regel alle Typ-Importe und meldet sie. Dies trägt zur Konsistenz und Lesbarkeit des TypeScript-Codes bei.
```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';
```
### Warum keine Typ-Importe?
* **Konsistenz**: Durch das Vermeiden von Typ-Importen und die Verwendung eines einzigen Ansatzes für sowohl Typ- als auch Wertimporte bleibt die Modulimportstruktur der Codebasis konsistent.
* **Lesbarkeit**: Keine Typ-Importe verbessern die Lesbarkeit des Codes, da klar wird, wann Werte oder Typen importiert werden. Dies reduziert die Zweideutigkeit und erleichtert das Verständnis des Zwecks der importierten Symbole.
* **Wartbarkeit**: Es verbessert die Wartbarkeit der Codebasis, da Entwickler Typ-Only-Imports beim Überprüfen oder Ändern von Code identifizieren und lokalisieren können.
### Oxlint-Regel
Eine Oxlint-Regel, `typescript/consistent-type-imports`, setzt den No-Type-Import-Standard durch. Diese Regel generiert Fehler oder Warnungen bei Verstößen gegen Typ-Importe.
Bitte beachten Sie, dass diese Regel speziell seltene Randfälle behandelt, in denen unbeabsichtigte Typ-Importe auftreten. TypeScript selbst lehnt diese Praxis ab, wie in den [TypeScript 3.8 Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) erwähnt. In den meisten Situationen sollten Typ-Only-Imports nicht benötigt werden.
Um sicherzustellen, dass Ihr Code mit dieser Regel übereinstimmt, achten Sie darauf, Oxlint als Teil Ihres Entwicklungsworkflows auszuführen.

View File

@@ -0,0 +1,60 @@
---
title: Mit Figma arbeiten
info: Erfahren Sie, wie Sie im Figma von Twenty zusammenarbeiten können.
---
Figma ist ein kollaboratives Interface-Design-Tool, das dazu beiträgt, die Kommunikationsbarriere zwischen Designern und Entwicklern zu überwinden.
Dieser Leitfaden erklärt, wie Sie mit Figma zusammenarbeiten können.
## Zugriff
1. **Auf den freigegebenen Link zugreifen:** Sie können auf die Figma-Datei des Projekts [hier](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty) zugreifen.
2. **Anmelden:** Wenn Sie nicht bereits angemeldet sind, werden Sie von Figma dazu aufgefordert.
Schlüsselfunktionen sind nur für angemeldete Benutzer verfügbar, wie der Entwicklermodus und die Möglichkeit, einen dedizierten Rahmen auszuwählen.
<Warning>
Ohne ein Konto können Sie nicht effektiv zusammenarbeiten.
</Warning>
## Figma-Struktur
In der linken Seitenleiste können Sie auf die verschiedenen Seiten der Figma-Datei von Twenty zugreifen. So sind sie organisiert:
* **Komponentenseite:** Dies ist die erste Seite. Der Designer verwendet sie zur Erstellung und Organisation der wiederverwendbaren Designelemente, die in der gesamten Designdatei verwendet werden. Zum Beispiel Schaltflächen, Icons, Symbole oder andere wiederverwendbare Komponenten. Sie dient dazu, die Konsistenz im gesamten Design beizubehalten.
* **Hauptseite:** Die zweite Seite ist die Hauptseite, die die vollständige Benutzeroberfläche des Projekts zeigt. Sie können ***Play*** drücken, um den vollständigen App-Prototypen zu verwenden.
* **Funktionsseiten:** Die anderen Seiten sind normalerweise den in Arbeit befindlichen Funktionen gewidmet. Sie enthalten das Design spezifischer Funktionen oder Module der Anwendung oder Website. Normalerweise sind sie noch in Arbeit.
## Nützliche Tipps
Mit Lesezugriff können Sie das Design nicht bearbeiten, aber Sie können alle Funktionen nutzen, die nützlich sind, um die Designs in Code zu konvertieren.
### Verwenden Sie den Dev-Modus
Der Dev-Modus von Figma verbessert die Produktivität von Entwicklern, indem er eine einfache Navigation im Design, effektive Ressourcenverwaltung, effiziente Kommunikationstools, Toolbox-Integrationen, schnelle Codeschnipsel und wichtige Ebeneninformationen bereitstellt, die die Lücke zwischen Design und Entwicklung schließen. Weitere Informationen über den Dev-Modus finden Sie [hier](https://www.figma.com/dev-mode/).
Wechseln Sie in der rechten Teil der Toolbar in den "Entwickler"-Modus, um Designspezifikationen anzuzeigen, CSS zu kopieren und auf Ressourcen zuzugreifen.
### Den Prototyp verwenden
Klicken Sie auf ein beliebiges Element auf der Leinwand und drücken Sie die Schaltfläche “Play” oben rechts in der Oberfläche, um die Prototyp-Ansicht zu öffnen. Der Prototypenmodus ermöglicht es Ihnen, mit dem Design zu interagieren, als wäre es das endgültige Produkt. Er zeigt den Fluss zwischen Bildschirmen und wie sich Interface-Elemente wie Schaltflächen, Links oder Menüs bei Interaktionen verhalten.
1. **Übergänge und Animationen verstehen:** Im Prototypenmodus können Sie Übergänge oder Animationen sehen, die ein Designer zwischen Bildschirmen oder UI-Elementen hinzugefügt hat, um Entwicklern klare visuelle Anweisungen zu beabsichtigtem Verhalten und Stil zu bieten.
2. **Implementierungsklarheit:** Ein Prototyp kann auch helfen, Unklarheiten zu reduzieren. Entwickler können damit interagieren, um ein besseres Verständnis der Funktionalität oder des Aussehens bestimmter Elemente zu erlangen.
Für umfassendere Details und Anleitungen zum Erlernen der Figma-Plattform können Sie die offizielle [Figma-Dokumentation](https://help.figma.com/hc/en-us) besuchen.
### Abstände messen
Wählen Sie ein Element aus, halten Sie die `Option`-Taste (Mac) oder `Alt`-Taste (Windows) gedrückt und fahren Sie dann mit der Maus über ein anderes Element, um den Abstand zwischen ihnen anzuzeigen.
### Figma-Erweiterung für VSCode (Empfohlen)
[Figma für VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension) ermöglicht es Ihnen, durch Design-Dateien zu navigieren und diese zu inspizieren, mit Designern zusammenzuarbeiten, Änderungen zu verfolgen und die Implementierung zu beschleunigen - alles, ohne Ihren Texteditor zu verlassen.
Es ist Teil unserer empfohlenen Erweiterungen.
## Zusammenarbeit
1. **Kommentare verwenden:** Sie sind eingeladen, die Kommentarfunktion zu nutzen, indem Sie auf das Blasensymbol im linken Teil der Toolbar klicken.
2. **Cursor-Chat:** Eine nette Funktion von Figma ist der Cursor-Chat. Drücken Sie einfach `;` auf Mac und `/` unter Windows, um eine Nachricht zu senden, wenn Sie sehen, dass jemand anderes Figma zur gleichen Zeit wie Sie verwendet.

View File

@@ -0,0 +1,318 @@
---
title: Lokale Einrichtung
icon: laptop-code
description: Der Leitfaden für Mitwirkende (oder neugierige Entwickler), die Twenty lokal ausführen möchten.
---
## Voraussetzungen
<Tabs>
<Tab title="Linux und macOS">
Bevor Sie Twenty installieren und verwenden können, stellen Sie sicher, dass Sie Folgendes auf Ihrem Computer installiert haben:
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Node v24.5.0](https://nodejs.org/en/download)
* [yarn v4](https://yarnpkg.com/getting-started/install)
* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
<Warning>
`npm` wird nicht funktionieren, Sie sollten stattdessen `yarn` verwenden. Yarn wird jetzt mit Node.js geliefert, so dass Sie es nicht separat installieren müssen.
Sie müssen nur `corepack enable` ausführen, um Yarn zu aktivieren, wenn Sie das noch nicht getan haben.
</Warning>
</Tab>
<Tab title="Windows (WSL)">
1. Installieren Sie WSL
Öffnen Sie PowerShell als Administrator und führen Sie aus:
```powershell
wsl --install
```
Sie sollten nun eine Aufforderung sehen, Ihren Computer neu zu starten. Wenn nicht, starten Sie ihn manuell neu.
Nach dem Neustart wird ein PowerShell-Fenster geöffnet und Ubuntu installiert. Dies kann einige Zeit in Anspruch nehmen.
Sie werden aufgefordert, einen Benutzernamen und ein Passwort für Ihre Ubuntu-Installation zu erstellen.
2. Git installieren und konfigurieren
```bash
sudo apt-get install git
git config --global user.name "Your Name"
git config --global user.email "youremail@domain.com"
```
3. nvm, node.js und yarn installieren
<Warning>
Verwenden Sie `nvm`, um die korrekte `node`-Version zu installieren. Die `.nvmrc` stellt sicher, dass alle Mitwirkenden die gleiche Version verwenden.
</Warning>
```bash
sudo apt-get install curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
```
Schließen und öffnen Sie Ihr Terminal erneut, um nvm zu verwenden. Führen Sie dann die folgenden Befehle aus.
```bash
nvm install # installs recommended node version
nvm use # use recommended node version
corepack enable
```
</Tab>
</Tabs>
---
## Schritt 1: Git klonen
Führen Sie in Ihrem Terminal den folgenden Befehl aus.
<Tabs>
<Tab title="SSH (Empfohlen)">
Wenn Sie SSH-Schlüssel noch nicht eingerichtet haben, können Sie [hier](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh) erfahren, wie das geht.
```bash
git clone git@github.com:twentyhq/twenty.git
```
</Tab>
<Tab title="HTTPS">
```bash
git clone https://github.com/twentyhq/twenty.git
```
</Tab>
</Tabs>
## Schritt 2: Positionieren Sie sich im Stammverzeichnis
```bash
cd twenty
```
Alle folgenden Befehle innerhalb des Projekts sind vom Stammverzeichnis aus auszuführen.
## Schritt 3: Einrichten einer PostgreSQL-Datenbank
<Tabs>
<Tab title="Linux">
**Option 1 (bevorzugt):** Um Ihre Datenbank lokal bereitzustellen:
Verwenden Sie den folgenden Link, um PostgreSQL auf Ihrem Linux-Rechner zu installieren: [PostgreSQL-Installation](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Hinweis: Möglicherweise müssen Sie `sudo -u postgres` dem Befehl vor `psql` hinzufügen, um Berechtigungsfehler zu vermeiden.
**Option 2:** Wenn Sie Docker installiert haben:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Mac OS">
**Option 1 (bevorzugt):** Um Ihre Datenbank lokal mit `brew` bereitzustellen:
```bash
brew install postgresql@16
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
brew services start postgresql@16
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Sie können überprüfen, ob der PostgreSQL-Server läuft, indem Sie folgendes ausführen:
```bash
brew services list
```
Das Installationsprogramm erstellt den Benutzer `postgres` möglicherweise nicht standardmäßig bei der Installation
über Homebrew auf macOS. Stattdessen wird eine PostgreSQL-Rolle erstellt, die Ihrem macOS
Benutzernamen (z. B. "john") entspricht.
Um zu überprüfen und, falls erforderlich, den Benutzer `postgres` zu erstellen, führen Sie folgende Schritte aus:
```bash
# Connect to PostgreSQL
psql postgres
or
psql -U $(whoami) -d postgres
```
Sobald Sie sich an der psql-Eingabeaufforderung (postgres=#) befinden, führen Sie aus:
```bash
# List existing PostgreSQL roles
\du
```
Sie werden eine Ausgabe ähnlich der folgenden sehen:
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
john | Superuser | {}
```
Wenn Sie keine `postgres`-Rolle sehen, fahren Sie mit dem nächsten Schritt fort.
Erstellen Sie die Rolle `postgres` manuell:
```bash
CREATE ROLE postgres WITH SUPERUSER LOGIN;
```
Dadurch wird eine Superuser-Rolle namens `postgres` mit Anmeldezugriff erstellt.
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
postgres | Superuser | {}
john | Superuser | {}
```
**Option 2:** Wenn Sie Docker installiert haben:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
Alle folgenden Schritte sind im WSL-Terminal auszuführen (innerhalb Ihrer virtuellen Maschine)
**Option 1:** Um Ihr PostgreSQL lokal bereitzustellen:
Verwenden Sie den folgenden Link, um PostgreSQL auf Ihrer Linux-VM zu installieren: [PostgreSQL-Installation](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Hinweis: Möglicherweise müssen Sie `sudo -u postgres` dem Befehl vor `psql` hinzufügen, um Berechtigungsfehler zu vermeiden.
**Option 2:** Wenn Sie Docker installiert haben:
Die Ausführung von Docker auf WSL fügt eine zusätzliche Komplexitätsschicht hinzu.
Verwenden Sie diese Option nur, wenn Sie mit den zusätzlichen Schritten vertraut sind, einschließlich der Aktivierung von [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
Sie können nun über `localhost:5432` auf die Datenbank zugreifen.
Wenn Sie die oben genannte Docker-Option verwendet haben, lauten die Standardanmeldedaten Benutzer `postgres` und Passwort `postgres`. Für native PostgreSQL-Installationen verwenden Sie die auf Ihrem Rechner konfigurierten Anmeldedaten und Rollen.
## Schritt 4: Einrichten einer Redis-Datenbank (Cache)
Twenty benötigt einen Redis-Cache, um die beste Leistung zu bieten.
<Tabs>
<Tab title="Linux">
**Option 1:** Bereitstellung von Redis lokal:
Verwenden Sie den folgenden Link, um Redis auf Ihrem Linux-Rechner zu installieren: [Redis Installation](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Option 2:** Wenn Sie Docker installiert haben:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Mac OS">
**Option 1 (bevorzugt):** Bereitstellung von Redis lokal mit `brew`:
```bash
brew install redis
```
Starten Sie Ihren Redis-Server:
```bash
brew services start redis
```
**Option 2:** Wenn Sie Docker installiert haben:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
**Option 1:** Um Redis lokal bereitzustellen:
Verwenden Sie den folgenden Link, um Redis auf Ihrer Linux-VM zu installieren: [Redis-Installation](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Option 2:** Wenn Sie Docker installiert haben:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
Wenn Sie eine Client-GUI benötigen, empfehlen wir [Redis Insight](https://redis.io/insight/) (kostenlose Version verfügbar).
## Schritt 5: Einrichten von Umgebungsvariablen
Verwenden Sie Umgebungsvariablen oder `.env`-Dateien, um Ihr Projekt zu konfigurieren. Weitere Informationen [hier](/l/de/developers/self-host/capabilities/setup).
Kopieren Sie die `.env.example`-Dateien in `/front` und `/server`:
```bash
cp ./packages/twenty-front/.env.example ./packages/twenty-front/.env
cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
```
<Info>
**Multi-Workspace-Modus:** Standardmäßig läuft Twenty im Einzel-Workspace-Modus, in dem nur ein Workspace erstellt werden kann. Um die Multi-Workspace-Unterstützung zu aktivieren (nützlich zum Testen von Subdomain-basierten Funktionen), setzen Sie `IS_MULTIWORKSPACE_ENABLED=true` in der `.env`-Datei Ihres Servers. Siehe [Multi-Workspace-Modus](/l/de/developers/self-host/capabilities/setup#multi-workspace-mode) für Details.
</Info>
## Schritt 6: Abhängigkeiten installieren
Um den Twenty-Server zu bauen und einige Daten in Ihre Datenbank zu seeden, führen Sie den folgenden Befehl aus:
```bash
yarn
```
Bitte beachten Sie, `npm` oder `pnpm` funktionieren nicht.
## Schritt 7: Das Projekt ausführen
<Tabs>
<Tab title="Linux">
Je nach Ihrer Linux-Distribution könnte der Redis-Server automatisch gestartet werden.
Wenn nicht, überprüfen Sie den [Redis Installationsleitfaden](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) für Ihre Distribution.
</Tab>
<Tab title="Mac OS">
Redis sollte bereits laufen. Falls nicht, führen Sie aus:
```bash
brew services start redis
```
</Tab>
<Tab title="Windows (WSL)">
Je nach Ihrer Linux-Distribution könnte der Redis-Server automatisch gestartet werden.
Wenn nicht, überprüfen Sie den [Redis Installationsleitfaden](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) für Ihre Distribution.
</Tab>
</Tabs>
Richten Sie Ihre Datenbank mit folgendem Befehl ein:
```bash
npx nx database:reset twenty-server
```
Starten Sie den Server, den Worker und die Frontend-Dienste:
```bash
npx nx start twenty-server
npx nx worker twenty-server
npx nx start twenty-front
```
Alternativ können Sie alle Dienste auf einmal starten:
```bash
npx nx start
```
## Schritt 8: Verwenden Sie Twenty
**Frontend**
Das Frontend von Twenty läuft unter [http://localhost:3001](http://localhost:3001).
Sie können sich mit dem Standard-Demokonto anmelden: `tim@apple.dev` (Passwort: `tim@apple.dev`)
**Backend**
* Der Server von Twenty wird unter [http://localhost:3000](http://localhost:3000) verfügbar sein.
* Die GraphQL-API kann unter [http://localhost:3000/graphql](http://localhost:3000/graphql) erreicht werden.
* Die REST-API ist erreichbar unter [http://localhost:3000/rest](http://localhost:3000/rest)
## Fehlerbehebung
Sollten Sie auf ein Problem stoßen, schauen Sie sich die [Fehlerbehebung](/l/de/developers/self-host/capabilities/troubleshooting) für Lösungen an.

View File

@@ -0,0 +1,77 @@
---
title: Befehle
icon: terminal
description: Nützliche Befehle für die Entwicklung von Twenty.
---
Befehle können vom Repository-Stammverzeichnis mit `npx nx` ausgeführt werden. Verwende `npx nx run {project}:{command}` für eine explizite Zielangabe.
## Die App starten
```bash
npx nx start twenty-front # Frontend dev server (http://localhost:3001)
npx nx start twenty-server # Backend server (http://localhost:3000)
npx nx run twenty-server:worker # Background worker
```
## Datenbank
```bash
npx nx database:reset twenty-server # Reset and seed database
npx nx run twenty-server:database:migrate:prod # Run migrations
npx nx run twenty-server:database:migrate:generate --name <name> --type <fast|slow> # Generate a migration
```
## Linting
```bash
npx nx lint:diff-with-main twenty-front # Lint changed files (fastest)
npx nx lint:diff-with-main twenty-server
npx nx lint twenty-front --configuration=fix # Auto-fix
```
## Typprüfung
```bash
npx nx typecheck twenty-front
npx nx typecheck twenty-server
```
## Tests
```bash
# Frontend
npx nx test twenty-front # Jest unit tests
npx nx storybook:build twenty-front # Build Storybook
npx nx storybook:test twenty-front # Storybook tests
# Backend
npx nx run twenty-server:test:unit # Unit tests
npx nx run twenty-server:test:integration # Integration tests
npx nx run twenty-server:test:integration:with-db-reset # Integration with DB reset
# Single file (fastest)
npx jest path/to/test.test.ts --config=packages/{project}/jest.config.mjs
```
## GraphQL
```bash
npx nx run twenty-front:graphql:generate # Regenerate types
npx nx run twenty-front:graphql:generate --configuration=metadata # Metadata schema
```
## Übersetzungen
```bash
npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Build
```bash
npx nx build twenty-shared # Must be built first
npx nx build twenty-front
npx nx build twenty-server
```

View File

@@ -0,0 +1,31 @@
---
title: Mitwirken
description: Tragen Sie zur Open-Source-Entwicklung von Twenty bei.
---
<Frame>
<img src="/images/user-guide/github/github-header.png" alt="KI" />
</Frame>
## Überblick
Twenty ist Open Source und freut sich über Beiträge aus der Community. Ob Sie Fehler beheben, Funktionen hinzufügen oder die Dokumentation verbessern, Ihre Beiträge tragen dazu bei, Twenty für alle zu verbessern.
## Möglichkeiten zum Mitwirken
* **Fehler melden**: Helfen Sie, Probleme zu identifizieren und zu dokumentieren
* **Funktionen einreichen**: Schlagen Sie neue Funktionalität vor und implementieren Sie sie
* **Dokumentation verbessern**: Machen Sie unsere Dokumentation klarer und hilfreicher
* **Frontend-Entwicklung**: Arbeiten Sie an der React-basierten UI
* **Backend-Entwicklung**: Tragen Sie zum NestJS-Server bei
## Erste Schritte
<CardGroup cols={2}>
<Card title="Fehlermeldungen und Anfragen" icon="bug" href="/l/de/developers/contribute/capabilities/bug-and-requests">
Melden Sie Probleme oder schlagen Sie neue Funktionen vor
</Card>
<Card title="Frontend-Entwicklung" icon="browser" href="/l/de/developers/contribute/capabilities/frontend-development/folder-architecture-front">
Tragen Sie zur UI bei
</Card>
</CardGroup>

View File

@@ -0,0 +1,176 @@
---
title: Styleguide
icon: paintbrush
description: Code-Konventionen und bewährte Verfahren für Beiträge zu Twenty.
---
## React
### Ausschließlich funktionale Komponenten
Verwenden Sie immer TSX-Funktionskomponenten mit benannten Exporten.
```tsx
// ❌ Bad
const MyComponent = () => {
return <div>Hello World</div>;
};
export default MyComponent;
// ✅ Good
export function MyComponent() {
return <div>Hello World</div>;
};
```
### Props
Erstellen Sie einen Typ namens `{ComponentName}Props`. Verwenden Sie Destrukturierung. Verwenden Sie `React.FC` nicht.
```tsx
type MyComponentProps = {
name: string;
};
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
### Kein Prop-Spreading mit einer einzelnen Variablen
```tsx
// ❌ Bad
const MyComponent = (props: MyComponentProps) => <Other {...props} />;
// ✅ Good
const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1, prop2 }} />;
```
## Zustandsverwaltung
### Jotai-Atome für globalen Zustand
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
export const myAtomState = createAtomState<string>({
key: 'myAtomState',
defaultValue: 'default value',
});
```
* Bevorzugen Sie Atome gegenüber Prop-Drilling
* Verwenden Sie `useRef` nicht für den Zustand — verwenden Sie `useState` oder Atome
* Verwenden Sie Atomfamilien und Selektoren für Listen
### Vermeiden Sie unnötige Re-Renders
* Lagern Sie `useEffect` und das Daten-Fetching in gleichrangige Sidecar-Komponenten aus
* Bevorzugen Sie Event-Handler (`handleClick`, `handleChange`) gegenüber `useEffect`
* Verwenden Sie `React.memo()` nicht — beheben Sie stattdessen die Grundursache
* Beschränken Sie die Nutzung von `useCallback`/`useMemo`
```tsx
// ❌ Bad — useEffect in the same component causes re-renders
export const Page = () => {
const [data, setData] = useAtomState(dataState);
const [dep] = useAtomState(depState);
useEffect(() => { setData(dep); }, [dep]);
return <div>{data}</div>;
};
// ✅ Good — extract into sibling
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [dep] = useAtomState(depState);
useEffect(() => { setData(dep); }, [dep]);
return <></>;
};
export const Page = () => {
const [data] = useAtomState(dataState);
return <div>{data}</div>;
};
```
## TypeScript
* **`type` statt `interface`** — flexibler, leichter zu kombinieren
* **String-Literale statt Enums** — außer für GraphQL-Codegen-Enums und interne Bibliotheks-APIs
* **Kein `any`** — striktes TypeScript wird durchgesetzt
* **Keine Type-Imports** — verwenden Sie reguläre Imports (erzwungen durch Oxlint `typescript/consistent-type-imports`)
* **Verwenden Sie [Zod](https://github.com/colinhacks/zod)** für die Laufzeitvalidierung ungetypter Objekte
## JavaScript
```tsx
// Use nullish-coalescing (??) instead of ||
const value = process.env.MY_VALUE ?? 'default';
// Use optional chaining
onClick?.();
```
## Namensgebung
* **Variablen**: camelCase, aussagekräftig (`email` statt `value`, `fieldMetadata` statt `fm`)
* **Konstanten**: SCREAMING_SNAKE_CASE
* **Typen/Klassen**: PascalCase
* **Dateien/Verzeichnisse**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Event-Handler**: `handleClick` (nicht `onClick` für die Handler-Funktion)
* **Komponenten-Props**: mit dem Komponentennamen präfixieren (`ButtonProps`)
* **Styled Components**: mit `Styled` präfixieren (`StyledTitle`)
## Styling
Verwenden Sie [Linaria](https://github.com/callstack/linaria) Styled Components. Verwenden Sie Theme-Werte — vermeiden Sie hartkodierte `px`, `rem` oder Farben.
```tsx
// ❌ Bad
const StyledButton = styled.button`
color: #333333;
font-size: 1rem;
margin-left: 4px;
`;
// ✅ Good
const StyledButton = styled.button`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
```
## Importe
Verwenden Sie Aliase statt relativer Pfade:
```tsx
// ❌ Bad
import { Foo } from '../../../../../testing/decorators/Foo';
// ✅ Good
import { Foo } from '~/testing/decorators/Foo';
import { Bar } from '@/modules/bar/components/Bar';
```
## Ordnerstruktur
```
front
└── modules/ # Feature modules
│ └── module1/
│ ├── components/
│ ├── constants/
│ ├── contexts/
│ ├── graphql/ (fragments, queries, mutations)
│ ├── hooks/
│ ├── states/ (atoms, selectors)
│ ├── types/
│ └── utils/
└── pages/ # Route-level components
└── ui/ # Reusable UI components (display, input, feedback, ...)
```
* Module können aus anderen Modulen importieren, aber `ui/` sollte abhängigkeitsfrei bleiben
* Verwenden Sie `internal/`-Unterordner für modulinternen Code
* Komponenten unter 300 Zeilen, Services unter 500 Zeilen