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: Migliori Pratiche
---
Questo documento descrive le migliori pratiche da seguire quando si lavora sul backend.
## Segui un approccio modulare
Il backend segue un approccio modulare, che è un principio fondamentale quando si lavora con NestJS. Assicurati di suddividere il tuo codice in moduli riutilizzabili per mantenere una codebase pulita e organizzata.
Ogni modulo dovrebbe incapsulare una particolare caratteristica o funzionalità e avere un ambito ben definito. Questo approccio modulare consente una chiara separazione delle responsabilità e rimuove complessità non necessarie.
## Esporre servizi da usare nei moduli
Crea sempre servizi che abbiano una responsabilità chiara e singola, migliorando la leggibilità e la manutenzione del codice. Dai ai servizi nomi descrittivi e coerenti.
Dovresti anche esporre i servizi che vuoi utilizzare in altri moduli. Esporre servizi ad altri moduli è possibile tramite il potente sistema di iniezione delle dipendenze di NestJS e promuove un basso accoppiamento tra i componenti.
## Evitare di usare il tipo `any`
Quando dichiari una variabile come `any`, il sistema di controllo dei tipi di TypeScript non esegue alcun controllo dei tipi, rendendo possibile assegnare alla variabile valori di qualsiasi tipo. TypeScript utilizza l'inferenza dei tipi per determinare il tipo della variabile in base al valore. Dichiarandola come `any`, TypeScript non può più dedurre il tipo. Questo rende difficile individuare errori legati ai tipi durante lo sviluppo, portando a errori in fase di esecuzione e rendendo il codice meno manutenibile, meno affidabile e più difficile da comprendere per gli altri.
È per questo che tutto dovrebbe avere un tipo. Quindi, se crei un nuovo oggetto con un nome e un cognome, dovresti creare un'interfaccia o un tipo che contenga un nome e un cognome e che definisca la struttura dell'oggetto che stai manipolando.

View File

@@ -0,0 +1,39 @@
---
title: Oggetti personalizzati
---
Gli oggetti sono strutture che permettono di memorizzare dati (record, attributi e valori) specifici di un'organizzazione. Twenty offre sia oggetti standard che personalizzati.
Gli oggetti standard sono oggetti integrati con un set di attributi disponibili per tutti gli utenti. Esempi di oggetti standard in Twenty includono Company e Person. Gli oggetti standard hanno campi standard che sono inoltre disponibili per tutti gli utenti di Twenty, come Company.displayName.
Gli oggetti personalizzati sono oggetti che puoi creare per memorizzare informazioni uniche per la tua organizzazione. Non sono integrati; i membri del tuo workspace possono creare e personalizzare oggetti personalizzati per contenere informazioni non adatte per gli oggetti standard.
## Schema di alto livello
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Schema di alto livello" />
</div>
<br />
## Come funziona
Gli oggetti personalizzati derivano da tabelle di metadati che determinano la forma, il nome e il tipo degli oggetti. Tutte queste informazioni sono presenti nello schema di metadati, composto da tabelle:
* **DataSource**: Dettagli su dove sono presenti i dati.
* **Oggetto**: Descrive l'oggetto e si collega a una DataSource.
* **Campo**: Delinea i campi di un Oggetto e si collega all'Oggetto.
Per aggiungere un oggetto personalizzato, il membro del workspace interrogherà l'API /metadata. Questo aggiorna i metadati di conseguenza e calcola uno schema GraphQL basato sui metadati, memorizzandolo in una cache GQL per un uso successivo.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/add-custom-objects.jpeg" alt="Interroga l'API /metadata per aggiungere oggetti personalizzati" />
</div>
<br />
Per recuperare i dati, il processo consiste nel effettuare interrogazioni tramite l'endpoint /graphql e passarle tramite il Query Resolver.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Interroga l'endpoint /graphql per recuperare dati" />
</div>

View File

@@ -0,0 +1,46 @@
---
title: Flag delle funzionalità
---
I flag delle funzionalità vengono utilizzati per nascondere funzionalità sperimentali. Per Twenty, sono impostati a livello di workspace e non a livello utente.
## Aggiunta di un nuovo flag delle funzionalità
In `FeatureFlagKey.ts` aggiungi il flag di funzionalità:
```ts
type FeatureFlagKey =
| 'IS_FEATURENAME_ENABLED'
| ...;
```
Aggiungilo anche all'enum in `feature-flag.entity.ts`:
```ts
enum FeatureFlagKeys {
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
...
}
```
Per applicare un flag di funzionalità su una funzione **backend** usa:
```ts
@Gate({
featureFlag: 'IS_FEATURENAME_ENABLED',
})
```
Per applicare un flag di funzionalità su una funzione **frontend** usa:
```ts
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
```
## Configura i flag delle funzionalità per il deployment
Cambia il record corrispondente nella Tabella `core.featureFlag`:
| id | chiave | workspaceId | valore |
| ------- | ------------------------ | ----------- | ------ |
| Casuale | `IS_FEATURENAME_ENABLED` | IDWorkspace | `vero` |

View File

@@ -0,0 +1,125 @@
---
title: Architettura delle Cartelle
info: Uno sguardo dettagliato alla nostra architettura di cartelle del server
---
La struttura della directory del backend è la seguente:
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
```
## Capacità
Definisce le autorizzazioni e include gestori per ciascuna entità.
## Decoratori
Definisce decoratori personalizzati in NestJS per funzionalità aggiuntive.
Vedi [decoratori personalizzati](https://docs.nestjs.com/custom-decorators) per maggiori dettagli.
## Filtri
Include filtri di eccezione per gestire le eccezioni che potrebbero verificarsi nei punti finali GraphQL.
## Guards
Vedi [guards](https://docs.nestjs.com/guards) per ulteriori dettagli.
## Stato di salute
Include un'API REST pubblicamente disponibile (healthz) che restituisce un JSON per confermare se il database funziona correttamente.
## Metadati
Definisce oggetti personalizzati e rende disponibile un'API GraphQL (graphql/metadata).
## Workspace
Genera e serve schemi GraphQL personalizzati basati sui metadati.
### Struttura della directory dello spazio di lavoro
```
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 radice della directory di lavoro include il `workspace.factory.ts`, un file contenente la funzione `createGraphQLSchema`. Questa funzione genera uno schema specifico per il workspace utilizzando i metadati per adattare uno schema per i singoli lavori. Separando la costruzione dello schema e del risolutore, utilizziamo la funzione `makeExecutableSchema`, che combina questi elementi discreti.
Questa strategia non riguarda solo l'organizzazione, ma aiuta anche con l'ottimizzazione, come la memorizzazione nella cache delle definizioni di tipo generate per migliorare le prestazioni e la scalabilità.
### Generatore dello schema dello spazio di lavoro
Genera lo schema GraphQL e include:
#### Factory:
Costruttori specializzati per generare costrutti correlati a GraphQL.
* La fabbrica type.factory traduce i metadati del campo in tipi GraphQL utilizzando `TypeMapperService`.
* La fabbrica type-definition.factory crea oggetti di input o output GraphQL derivati da `objectMetadata`.
#### Tipi GraphQL
Include enumerazioni, input, oggetti e scalari e funge da base per la costruzione dello schema.
#### Interfacce e Definizioni di Oggetti
Contiene i progetti per le entità GraphQL e include tipi predefiniti e personalizzati come `MONEY` o `URL`.
#### Servizi
Contiene il servizio responsabile dell'associazione di FieldMetadataType al relativo scalare GraphQL o ai modificatori di query.
#### Archiviazione
Include la classe `TypeDefinitionsStorage` che contiene definizioni di tipo riutilizzabili, evitando la duplicazione di tipi GraphQL.
### Generatore di resolver dello spazio di lavoro
Crea funzioni di risolutore per interrogare e modificare lo schema GraphQL.
Ogni factory in questa directory è responsabile della produzione di un tipo di resolver distinto, come `FindManyResolverFactory`, progettato per un'applicazione adattabile su varie tabelle.
### Esecutore di query dello spazio di lavoro
Esegue le query generate sul database e analizza il risultato.

View File

@@ -0,0 +1,41 @@
---
title: Coda di messaggi
---
Le code facilitano l'esecuzione di operazioni asincrone. Possono essere usate per eseguire attività in background come l'invio di un'email di benvenuto alla registrazione.
Ogni caso d'uso avrà la propria classe di coda estesa da `MessageQueueServiceBase`.
Al momento supportiamo solo `bull-mq`[bull-mq](https://bullmq.io/) come driver della coda.
## Passaggi per creare e utilizzare una nuova coda
1. Aggiungi un nome di coda per la tua nuova coda sotto l'enum `MESSAGE_QUEUES`.
2. Fornire l'implementazione della fabbrica della coda con il nome della coda come token di dipendenza.
3. Inserisci la coda che hai creato nel modulo/servizio richiesto con il nome della coda come token di dipendenza.
4. Aggiungi una classe worker con iniezione basata su token, proprio come il produttore.
### Esempio di utilizzo
```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: Comandi Backend
icon: terminal
---
## Comandi utili
Questi comandi devono essere eseguiti dalla cartella packages/twenty-server.
Da qualsiasi altra cartella puoi eseguire `npx nx {command} twenty-server` (oppure `npx nx run twenty-server:{command}`).
### Impostazione iniziale
```
npx nx database:reset twenty-server # setup the database with dev seeds
```
### Avvio del server
```
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
```
Nota: puoi eseguire `npx nx run twenty-server:test:integration:with-db-reset` nel caso in cui sia necessario resettare il database prima di eseguire i test di integrazione.
### Ripristino del database
Se vuoi resettare e seminare il database, puoi eseguire il seguente comando:
```bash
npx nx run twenty-server:database:reset
```
### Migrazioni
#### Per oggetti negli schemi Core/Metadata (TypeORM)
```bash
npx nx run twenty-server:database:migrate:generate
```
## Tech Stack
Twenty utilizza principalmente NestJS per il backend.
Prisma è stato il primo ORM che abbiamo usato. Ma per permettere agli utenti di creare campi personalizzati e oggetti personalizzati, un livello più basso aveva più senso poiché abbiamo bisogno di avere un controllo granulare. Il progetto ora utilizza TypeORM.
Ecco come appare ora lo stack tecnologico.
**Core**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
**Database**
* [Postgres](https://www.postgresql.org/)
**Integrazioni di terze parti**
* [Sentry](https://sentry.io/welcome/) per il tracciamento degli errori
**Testing**
* [Jest](https://jestjs.io/)
**Strumenti**
* [Yarn](https://yarnpkg.com/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
**Sviluppo**
* [AWS EKS](https://aws.amazon.com/eks/)

View File

@@ -0,0 +1,85 @@
---
title: App di Zapier
---
Sincronizza facilmente Twenty con oltre 3000 app utilizzando [Zapier](https://zapier.com/). Automatizza le attività, aumenta la produttività e potenzia le relazioni con i clienti!
## Informazioni su Zapier
Zapier è uno strumento che ti permette di automatizzare i flussi di lavoro collegando le app che il tuo team usa quotidianamente. Il concetto fondamentale di Zapier sono i flussi di lavoro automatici, chiamati Zaps, che includono trigger e azioni.
Puoi saperne di più su come funziona Zapier [qui](https://zapier.com/how-it-works).
## Impostazione
### Passo 1: Installa i pacchetti di Zapier
```bash
cd packages/twenty-zapier
yarn
```
### Passaggio 2: Accedi con la CLI
Usa le tue credenziali Zapier per accedere usando il CLI:
```bash
zapier login
```
### Passaggio 3: Configura le variabili d'ambiente
Dalla cartella `packages/twenty-zapier`, esegui:
```bash
cp .env.example .env
```
Esegui l'applicazione localmente, vai su [http://localhost:3000/settings/api-webhooks](http://localhost:3000/settings/api-webhooks) e genera una chiave API.
Sostituisci il valore **YOUR_API_KEY** nel file `.env` con la chiave API appena generata.
## Sviluppo
<Warning>
Assicurati di eseguire `yarn build` prima di qualsiasi comando `zapier`.
</Warning>
### Test
```bash
yarn test
```
### Lint
```bash
yarn format
```
### Osserva e compila mentre modifichi il codice
```bash
yarn watch
```
### Convalida la tua app Zapier
```bash
yarn validate
```
### Distribuisci la tua app Zapier
```bash
yarn deploy
```
### Elenca tutti i comandi del CLI di Zapier
```bash
zapier
```

View File

@@ -0,0 +1,79 @@
---
title: Bug, richieste e Pull Request
icon: bug
info: Segnala problemi, richiedi funzionalità e contribuisci al codice
---
## Segnalazione Bug
Per segnalare un bug, per favore [crea una issue su GitHub](https://github.com/twentyhq/twenty/issues/new).
Puoi anche chiedere aiuto su [Discord](https://discord.gg/cx5n4Jzs57).
## Richieste di Funzionalità
Se non sei sicuro che sia un bug e ti sembra più vicino a una richiesta di funzionalità, probabilmente dovresti [aprire invece una discussione](https://github.com/twentyhq/twenty/discussions/new).
## Invia una Pull Request
Contribuire al codice di Twenty inizia con una pull request (PR).
### Prima di Iniziare
1. Controlla le [issue esistenti](https://github.com/twentyhq/twenty/issues) per lavori correlati
2. Per le nuove funzionalità, apri prima una issue per discuterne
3. Consulta il nostro [Codice di condotta](https://github.com/twentyhq/twenty/blob/main/.github/CODE_OF_CONDUCT.md)
### Esegui il fork e clona
1. Esegui il fork del repository su GitHub
2. Clona il tuo fork:
```bash
git clone https://github.com/YOUR_USERNAME/twenty.git
cd twenty
```
3. Aggiungi il remote upstream:
```bash
git remote add upstream https://github.com/twentyhq/twenty.git
```
### Crea un branch
```bash
git checkout -b feature/your-feature-name
```
Usa nomi di branch descrittivi:
* `feature/add-export-button`
* `fix/login-redirect-issue`
* `docs/update-api-guide`
### Apporta le tue modifiche
1. Scrivi codice pulito e ben documentato
2. Segui lo stile di codice esistente
3. Aggiungi test per la nuova funzionalità
4. Aggiorna la documentazione se necessario
### Invia la tua PR
1. Esegui il push del tuo branch:
```bash
git push origin feature/your-feature-name
```
2. Apri una PR su GitHub
3. Compila il modello della PR
4. Collega le issue correlate
### Checklist della PR
* [ ] Il codice segue le linee guida di stile del progetto
* [ ] I test passano in locale
* [ ] La documentazione è aggiornata
* [ ] La descrizione della PR spiega le modifiche

View File

@@ -0,0 +1,329 @@
---
title: Migliori Pratiche
icon: star
---
Questo documento descrive le migliori pratiche da seguire quando si lavora sul frontend.
## Gestione dello Stato
React e Jotai gestiscono lo stato nella base di codice.
### Usa gli atomi di Jotai per memorizzare lo stato
È buona pratica creare tanti atomi quanti servono per memorizzare il tuo stato.
<Warning>
È meglio usare atomi extra piuttosto che cercare di essere troppo concisi con l'iniezione di props.
</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>
);
}
```
### Non utilizzare `useRef` per memorizzare lo stato
Evita di usare `useRef` per memorizzare lo stato.
Se vuoi memorizzare lo stato, dovresti usare `useState` o gli atomi di Jotai con `useAtomState`.
Consulta [come gestire i re-render](#managing-re-renders) se senti che hai bisogno di `useRef` per evitare alcuni re-render.
## Gestione dei Re-Render
I re-render possono essere difficili da gestire in React.
Ecco alcune regole da seguire per evitare re-render non necessari.
Tieni presente che puoi **sempre** evitare i re-render comprendendo la loro causa.
### Lavora a livello radice
Evitare i re-render in nuove funzionalità è ora più semplice eliminandoli a livello radice.
Il componente sidecar `PageChangeEffect` contiene un solo `useEffect` che detiene tutta la logica da eseguire su un cambio di pagina.
In questo modo sai che c'è solo un luogo che può attivare un re-render.
### Pensa sempre due volte prima di aggiungere `useEffect` nel tuo codice
I re-render sono spesso causati da `useEffect` non necessari.
Dovresti pensare se hai bisogno di `useEffect`, o se puoi spostare la logica in una funzione gestore di eventi.
Troverai generalmente facile spostare la logica in una funzione `handleClick` o `handleChange`.
Puoi trovarli anche in librerie come Apollo: `onCompleted`, `onError`, ecc.
### Usa un componente simile per estrarre la logica `useEffect` o di recupero dati
Se senti di dover aggiungere un `useEffect` nel tuo componente radice, dovresti considerare di estrarlo in un componente sidecar.
Puoi applicare lo stesso per la logica di recupero dati, con i hook di Apollo.
```tsx
// ❌ Bad, will cause re-renders even if data is not changing,
// because useEffect needs to be re-evaluated
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <div>{data}</div>;
};
export const App = () => (
<PageComponent />
);
```
```tsx
// ✅ Good, will not cause re-renders if data is not changing,
// because useEffect is re-evaluated in another sibling component
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
return <div>{data}</div>;
};
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <></>;
};
export const App = () => (
<>
<PageData />
<PageComponent />
</>
);
```
### Usa gli stati e i selettori della famiglia di atomi
Gli stati e i selettori della famiglia di atomi sono un ottimo modo per evitare re-render.
Sono utili quando hai bisogno di memorizzare una lista di elementi.
### Non dovresti usare `React.memo(MyComponent)`
Evita di usare `React.memo()` perché non risolve la causa del re-render, ma interrompe invece la catena di re-render, il che può portare a comportamenti inaspettati e rendere il codice molto difficile da rifattorizzare.
### Limita l'uso di `useCallback` o `useMemo`
Spesso non sono necessari e renderanno il codice più difficile da leggere e mantenere per un guadagno di prestazioni che è impercettibile.
## Console.log
Le dichiarazioni `console.log` sono preziose durante lo sviluppo, offrendo informazioni in tempo reale sui valori delle variabili e sul flusso del codice. Tuttavia, lasciarli nel codice di produzione può causare diversi problemi:
1. **Prestazioni**: Un logging eccessivo può influire sulle prestazioni di runtime, soprattutto nelle applicazioni lato client.
2. **Sicurezza**: Registrare dati sensibili può esporre informazioni critiche a chiunque ispezioni la console del browser.
3. **Pulizia**: Riempire la console di log può oscurare avvertimenti o errori importanti che sviluppatori o strumenti devono vedere.
4. **Professionalità**: Gli utenti finali o i clienti che controllano la console e vedono una miriade di dichiarazioni di log potrebbero mettere in dubbio la qualità e la raffinatezza del codice.
Assicurati di rimuovere tutti i `console.logs` prima di distribuire il codice in produzione.
## Denominazione
### Denominazione delle Variabili
I nomi delle variabili dovrebbero descrivere precisamente lo scopo o la funzione della variabile.
#### Il problema con i nomi generici
I nomi generici nella programmazione non sono ideali perché mancano di specificità, portando all'ambiguità e riducendo la leggibilità del codice. Tali nomi non riescono a trasmettere lo scopo della variabile o della funzione, rendendo difficile per gli sviluppatori comprendere l'intento del codice senza un'indagine più approfondita. Questo può risultare in tempi di debug più lunghi, maggiore suscettibilità agli errori e difficoltà nella manutenzione e nella collaborazione. Nel frattempo, una denominazione descrittiva rende il codice autoesplicativo e più facile da navigare, migliorando la qualità del codice e la produttività dello sviluppatore.
```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('');
```
#### Alcune parole da evitare nei nomi delle variabili
* fittizio
### Gestori di Eventi
I nomi dei gestori degli eventi dovrebbero iniziare con `handle`, mentre `on` è un prefisso usato per nominare gli eventi nelle props dei componenti.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## Props Opzionali
Evita di passare il valore predefinito per una prop opzionale.
**ESEMPIO**
Guarda il componente `EmailField` definito di seguito:
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
<TextInput value={value} disabled={disabled} fullWidth />
);
```
**Utilizzo**
```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" />;
```
## Componente come props
Cercate, per quanto possibile, di passare componenti non istanziati come props, così i figli possono decidere autonomamente quali props devono passare.
L'esempio più comune per questo sono i componenti icona:
```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>
)
};
```
Per far sì che React capisca che il componente è un componente, è necessario usare PascalCase, per poi istanziarlo con `<MyIcon>`.
## Prop Drilling: Mantienilo Minimal
Il prop drilling, nel contesto di React, si riferisce alla pratica di passare variabili di stato e i loro setter attraverso molti livelli di componenti, anche se i componenti intermedi non li usano. Anche se a volte è necessario, un eccessivo prop drilling può portare a:
1. **Diminuzione della leggibilità**: Tracciare da dove proviene un prop o dove viene utilizzato può diventare complicato in una struttura di componenti profondamente nidificata.
2. **Sfide di manutenzione**: Cambiamenti nella struttura dei props di un componente potrebbero richiedere aggiustamenti in diversi componenti, anche se non utilizzano direttamente il prop.
3. **Ridotta riutilizzabilità del componente**: Un componente che riceve molti props solo per passarli diventa meno generico e più difficile da riutilizzare in contesti diversi.
Se ritieni di utilizzare eccessivo prop drilling, vedi [migliori pratiche di gestione dello stato](#state-management).
## Importa
Quando importi, opta per gli alias designati anziché specificare percorsi completi o relativi.
**Gli alias**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**Utilizzo**
```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';
```
## Validazione dello Schema
[Zod](https://github.com/colinhacks/zod) è il validatore di schema per oggetti non tipizzati:
```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>;
```
## Modifiche Incompatibili
Esegui sempre test manuali approfonditi prima di procedere per garantire che le modifiche non abbiano causato interruzioni altrove, dato che i test non sono ancora stati ampiamente integrati.

View File

@@ -0,0 +1,110 @@
---
title: Architettura delle Cartelle
icon: folder-tree
info: Uno sguardo dettagliato all'architettura delle nostre cartelle
---
In questa guida, esplorerai i dettagli della struttura delle directory del progetto e come contribuisce all'organizzazione e alla manutenibilità di Twenty.
Seguendo questa convenzione di architettura delle cartelle, è più facile trovare i file relativi a funzionalità specifiche e garantire che l'applicazione sia scalabile e manutenibile.
```
front
└───modules
│ └───module1
│ │ └───submodule1
│ └───module2
│ └───ui
│ │ └───display
│ │ └───inputs
│ │ │ └───buttons
│ │ └───...
└───pages
└───...
```
## Pagine
Include i componenti di alto livello definiti dalle rotte dell'applicazione. Importano componenti di livello inferiore dalla cartella modules (vedi dettagli sotto).
## Moduli
Ogni modulo rappresenta una funzionalità o un gruppo di funzionalità, comprendente i suoi componenti specifici, stati e logica operativa.
Dovrebbero tutti seguire la struttura sottostante. Puoi nidificare moduli all'interno di moduli (indicati come sottomoduli) e le stesse regole si applicano.
```
module1
└───components
│ └───component1
│ └───component2
└───constants
└───contexts
└───graphql
│ └───fragments
│ └───queries
│ └───mutations
└───hooks
│ └───internal
└───states
│ └───selectors
└───types
└───utils
```
### Contesti
Un contesto è un modo per passare dati attraverso l'albero dei componenti senza dover trasmettere i props manualmente a ogni livello.
Vedi [React Context](https://react.dev/reference/react#context-hooks) per ulteriori dettagli.
### GraphQL
Include frammenti, query e mutazioni.
Vedi [GraphQL](https://graphql.org/learn/) per ulteriori dettagli.
* Frammenti
Un frammento è un pezzo riutilizzabile di una query, che puoi usare in posti diversi. Usando i frammenti, è più facile evitare la duplicazione di codice.
Vedi [GraphQL Fragments](https://graphql.org/learn/queries/#fragments) per ulteriori dettagli.
* Query
Vedi [GraphQL Queries](https://graphql.org/learn/queries/) per ulteriori dettagli.
* Mutazioni
Vedi [GraphQL Mutations](https://graphql.org/learn/queries/#mutations) per ulteriori dettagli.
### Hook
Vedi [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) per ulteriori dettagli.
### Stati
Contiene la logica di gestione degli stati. [Jotai](https://jotai.org) se ne occupa.
* Selettori: Gli atomi derivati (utilizzando `createAtomSelector`) calcolano valori da altri atomi e vengono memoizzati automaticamente.
La gestione degli stati integrata di React si occupa ancora dello stato all'interno di un componente.
### Utilità
Dovrebbe contenere solo funzioni pure riutilizzabili. In caso contrario, crea hook personalizzati nella cartella `hooks`.
## UI
Contiene tutti i componenti UI riutilizzabili usati nell'applicazione.
Questa cartella può contenere sottocartelle, come `data`, `display`, `feedback` e `input` per tipi specifici di componenti. Ogni componente dovrebbe essere autonomo e riutilizzabile, così da poterlo utilizzare in diverse parti dell'applicazione.
Separando i componenti UI dagli altri componenti nella cartella `modules`, è più facile mantenere un design coerente e apportare modifiche alla UI senza influenzare altre parti (logica di business) del codice.
## Interfaccia e dipendenze
Puoi importare codice di altri moduli da qualsiasi modulo, tranne la cartella `ui`. Questo manterrà il suo codice facile da testare.
### Interni
Ogni parte (hook, stati, ...) di un modulo può avere una cartella `internal`, che contiene le parti utilizzate solo all'interno del modulo.

View File

@@ -0,0 +1,91 @@
---
title: Comandi frontend
icon: terminal
---
## Comandi utili
### Avviare l'app
```bash
npx nx start twenty-front
```
### Rigenerare lo schema graphql sulla base dello schema API graphql
```bash
npx nx run twenty-front:graphql:generate --configuration=metadata
```
OPPURE
```bash
npx nx run twenty-front:graphql:generate
```
### Lint
```bash
npx nx run twenty-front:lint # pass --fix to fix lint errors
```
## Traduzioni
```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)
```
## Tech Stack
Il progetto ha una struttura chiara e semplice, con un codice boilerplate minimo.
**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/)
**Testing**
* [Jest](https://jestjs.io/)
* [Storybook](https://storybook.js.org/)
**Strumenti**
* [Yarn](https://yarnpkg.com/)
* [Craco](https://craco.js.org/docs/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
## Architettura
### Routing
[React Router](https://reactrouter.com/) gestisce il routing.
Per evitare [re-render](/l/it/developers/contribute/capabilities/frontend-development/best-practices-front#managing-re-renders) inutili, tutta la logica del routing è in un `useEffect` in `PageChangeEffect`.
### Gestione dello stato
[Jotai](https://jotai.org/) gestisce lo stato.
Vedi [best practices](/l/it/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) per ulteriori informazioni sulla gestione dello stato.
## Testing
[Jest](https://jestjs.io/) serve come strumento per i test unitari mentre [Storybook](https://storybook.js.org/) è per i test dei componenti.
Jest è principalmente per testare le funzioni utility, e non i componenti stessi.
Storybook è per testare il comportamento di componenti isolati, oltre che per mostrare il sistema di design.

View File

@@ -0,0 +1,178 @@
---
title: Tasti di scelta rapida
---
## Introduzione
Quando hai bisogno di ascoltare un tasto di scelta rapida, normalmente useresti l'evento `onKeyDown`.
In `twenty-front`, tuttavia, potresti avere conflitti tra gli stessi tasti di scelta rapida che sono utilizzati in componenti diversi, montati nello stesso momento.
Ad esempio, se hai una pagina che ascolta il tasto Invio, e una modale che ascolta lo stesso tasto, con un componente Select all'interno della modale che ascolta il tasto Invio, potresti avere un conflitto quando tutti sono montati contemporaneamente.
## L'hook `useScopedHotkeys`
Per gestire questo problema, abbiamo un hook personalizzato che rende possibile ascoltare i tasti di scelta rapida senza alcun conflitto.
Lo posizioni in un componente, e ascolterà i tasti solo quando il componente è montato E quando l'**ambito del tasto di scelta rapida** specificato è attivo.
## Come ascoltare i tasti di scelta rapida nella pratica?
Sono coinvolti due passaggi nell'impostazione dell'ascolto dei tasti di scelta rapida:
1. Imposta l'[ambito del tasto di scelta rapida](#what-is-a-hotkey-scope-) che ascolterà i tasti di scelta rapida
2. Usa l'hook `useScopedHotkeys` per ascoltare i tasti di scelta rapida
Configurare gli ambiti dei tasti di scelta rapida è necessario anche in pagine semplici, perché altri elementi dell'interfaccia utente come il menu a sinistra o il menu dei comandi potrebbero anch'essi ascoltare i tasti di scelta rapida.
## Casi d'uso per i tasti di scelta rapida
In generale, avrai due casi d'uso che richiedono tasti di scelta rapida:
1. In una pagina o un componente montato in una pagina
2. In un componente di tipo modale che prende il focus a causa di un'azione dell'utente
Il secondo caso d'uso può verificarsi in modo ricorsivo: ad esempio, un menu a tendina in una modale.
### Ascoltare i tasti di scelta rapida in una pagina
Esempio:
```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>;
};
```
### Ascoltare i tasti di scelta rapida in un componente di tipo modale
Per questo esempio utilizzeremo un componente modale che ascolta il tasto Esc per segnalare al suo genitore di chiuderlo.
Qui l'interazione dell'utente sta cambiando l'ambito.
```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>;
};
```
Poi nel componente modale:
```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>;
};
```
È importante utilizzare questo schema quando non sei sicuro che l'utilizzo di useEffect con mount/unmount sarà sufficiente per evitare conflitti.
Quei conflitti possono essere difficili da debug, e potrebbe accadere più spesso che no con useEffects.
## Che cos'è un ambito del tasto di scelta rapida?
Un ambito del tasto di scelta rapida è una stringa che rappresenta un contesto in cui i tasti di scelta rapida sono attivi. È generalmente codificato come un enum.
Quando cambi l'ambito del tasto di scelta rapida, i tasti di scelta rapida che ascoltano questo ambito saranno abilitati e i tasti che ascoltano altri ambiti saranno disabilitati.
Puoi impostare solo un ambito alla volta.
Ad esempio, gli ambiti dei tasti di scelta rapida per ogni pagina sono definiti nell'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',
}
```
Internamente, l'ambito selezionato attualmente viene memorizzato in un atomo Jotai condiviso in tutta l'applicazione:
```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
key: 'currentHotkeyScopeState',
defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```
Ma questo atomo non dovrebbe mai essere gestito manualmente! Vedremo come usarlo nella sezione successiva.
## Come funziona internamente?
Abbiamo creato un sottile wrapper su [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) che lo rende più performante ed evita rendering non necessari.
Creiamo anche un atomo Jotai per gestire lo stato dell'ambito del tasto di scelta rapida e renderlo disponibile ovunque nell'applicazione.

View File

@@ -0,0 +1,8 @@
---
title: Storybook
description: Sfoglia la libreria dei componenti UI di Twenty
---
Visualizza la nostra libreria completa di componenti e documentazione in Storybook.
[Apri Storybook →](https://storybook.twenty.com)

View File

@@ -0,0 +1,291 @@
---
title: Guida di stile
icon: paintbrush
---
Questo documento include le regole da seguire quando si scrive codice.
L'obiettivo qui è avere una base di codice coerente, facile da leggere e da mantenere.
Per questo, è meglio essere un po' più dettagliati che troppo concisi.
Tieni sempre a mente che le persone leggono il codice più spesso di quanto non lo scrivano, soprattutto in un progetto open source, dove chiunque può contribuire.
Ci sono molte regole che non sono definite qui, ma che vengono controllate automaticamente dai linters.
## React
### Usa componenti funzionali
Usa sempre componenti funzionali TSX.
Non utilizzare l'`import` di default con `const`, perché è più difficile da leggere e importare con il completamento del codice.
```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
Crea il tipo di proprietà e chiamalo `(ComponentName)Props` se non c'è bisogno di esportarle.
Usa la destrutturazione delle 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>;
```
#### Evita di usare `React.FC` o `React.FunctionComponent` per definire i tipi di proprietà
```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 />
);
```
#### Nessuna espansione singola delle variabili delle props negli elementi JSX
Evita di usare l'espansione singola delle variabili delle props negli elementi JSX, come `{...props}`. Questa pratica spesso porta a un codice meno leggibile e più difficile da mantenere perché non è chiaro quali props stia ricevendo il componente.
```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 }} />;
};
```
Ragionamento:
* A colpo d'occhio, è più chiaro quali props il codice passa, rendendo più semplice comprenderlo e mantenerlo.
* Aiuta a prevenire un accoppiamento stretto tra i componenti attraverso le loro props.
* Gli strumenti di linting rendono più facile identificare le props mal scritte o non utilizzate quando si elencano esplicitamente.
## JavaScript
### Usa l'operatore di coalescenza dei valori null `??`
```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';
```
### Usa il collegamento delle opzioni `?.`
```tsx
// ❌ Bad
onClick && onClick();
// ✅ Good
onClick?.();
```
## TypeScript
### Usa `type` invece di `interface`
Usa sempre `type` invece di `interface`, perché quasi sempre si sovrappongono, e `type` è più flessibile.
```tsx
// ❌ Bad
interface MyInterface {
name: string;
}
// ✅ Good
type MyType = {
name: string;
};
```
### Usa letterali di stringa invece di enum
[I letterali di stringa](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) sono la modalità preferita per gestire valori simili agli enum in TypeScript. Sono più facili da estendere con Pick e Omit e offrono una migliore esperienza per lo sviluppatore, in particolare con il completamento del codice.
Puoi vedere perché TypeScript consiglia di evitare gli enum [qui](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 e librerie interne
Dovresti usare gli enum che il codegen GraphQL genera.
È anche meglio usare un enum quando si utilizza una libreria interna, così la libreria interna non deve esporre un tipo di letterale di stringa che non è correlato all'API interna.
Esempio:
```TSX
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
```
## Stile
### Usa StyledComponents
Stilizza i componenti con [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
<div className="my-class">Hello World</div>
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
Prefissi i componenti stilizzati con "Styled" per differenziarli dai componenti "reali".
```tsx
// ❌ Bad
const Title = styled.div`
color: red;
`;
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
### Tematizzazione
Utilizzare il tema per la maggior parte della stilizzazione dei componenti è l'approccio preferito.
#### Unità di misura
Evita di usare `px` o valori `rem` direttamente nei componenti stilizzati. I valori necessari sono generalmente già definiti nel tema, quindi è consigliato sfruttare il tema per questi scopi.
#### Colori
Evita di introdurre nuovi colori; usa invece la palette esistente nel tema. Nel caso in cui la palette non sia adeguata, procedi lasciando un commento affinché il team possa risolvere.
```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};
`;
```
## Applicare importazioni senza tipo
Evita le importazioni di tipo. Per far rispettare questo standard, una regola di Oxlint verifica e segnala qualsiasi import di tipo. Questo aiuta a mantenere la coerenza e la leggibilità del codice 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';
```
### Perché evitare importazioni di tipo
* **Coerenza**: Evitando le importazioni di tipo e usando un unico approccio sia per le importazioni di tipo che di valore, il codice rimane coerente nel suo stile di importazione dei moduli.
* **Leggibilità**: Le importazioni senza tipo migliorano la leggibilità del codice rendendo chiaro quando si stanno importando valori o tipi. Questo riduce l'ambiguità e rende più semplice comprendere lo scopo dei simboli importati.
* **Manutenibilità**: Migliora la manutenibilità della codebase perché gli sviluppatori possono identificare e individuare gli import solo di tipo durante la revisione o la modifica del codice.
### Regola Oxlint
Una regola di Oxlint, `typescript/consistent-type-imports`, impone lo standard che vieta gli import di tipo. Questa regola genererà errori o avvisi per qualsiasi violazione relativa agli import di tipo.
Tieni presente che questa regola affronta specificamente rari casi limite in cui si verificano import di tipo involontari. TypeScript stesso scoraggia questa pratica, come menzionato nelle [note di rilascio di TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). Nella maggior parte delle situazioni, non si dovrebbe aver bisogno di usare importazioni di solo tipo.
Per assicurarti che il tuo codice sia conforme a questa regola, assicurati di eseguire Oxlint come parte del tuo flusso di lavoro di sviluppo.

View File

@@ -0,0 +1,60 @@
---
title: Lavora con Figma
info: Scopri come puoi collaborare al file Figma di Twenty.
---
Figma è uno strumento di progettazione dell'interfaccia collaborativa che aiuta a colmare il divario comunicativo tra designer e sviluppatori.
Questa guida spiega come puoi collaborare con Figma.
## Accesso
1. **Accedi al link condiviso:** Puoi accedere al file Figma del progetto [qui](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty).
2. **Accedi:** Se non hai ancora effettuato l'accesso, Figma ti inviterà a farlo.
Le funzionalità chiave sono disponibili solo per gli utenti connessi, come la modalità sviluppatore e la possibilità di selezionare un telaio dedicato.
<Warning>
Non sarai in grado di collaborare efficacemente senza un account.
</Warning>
## Struttura di Figma
Nella barra laterale sinistra, puoi accedere alle diverse pagine del file Figma di Twenty. Ecco come sono organizzate:
* **Pagina dei componenti:** Questa è la prima pagina. Il designer la usa per creare e organizzare gli elementi di design riutilizzabili impiegati in tutto il file di design. Ad esempio, pulsanti, icone, simboli o qualsiasi altro componente riutilizzabile. Serve a mantenere la coerenza nel design.
* **Pagina principale:** La seconda pagina è la pagina principale, che mostra l'interfaccia utente completa del progetto. Puoi premere ***Play*** per utilizzare il prototipo completo dell'app.
* **Pagine delle caratteristiche:** Le altre pagine sono generalmente dedicate alle caratteristiche in corso di sviluppo. Contengono il design di caratteristiche specifiche o moduli dell'applicazione o del sito web. Sono generalmente ancora in fase di sviluppo.
## Suggerimenti utili
Con l'accesso in sola lettura, non puoi modificare il design, ma puoi accedere a tutte le caratteristiche che saranno utili per convertire i design in codice.
### Usa la modalità Dev
La modalità Dev di Figma migliora la produttività degli sviluppatori offrendo facile navigazione nel design, gestione efficace delle risorse, strumenti di comunicazione efficienti, integrazioni toolbox, rapidi frammenti di codice e informazioni chiave sui layer, colmando il divario tra design e sviluppo. Puoi saperne di più sulla modalità Dev [qui](https://www.figma.com/dev-mode/).
Cambia alla modalità "Sviluppatore" nella parte destra della barra degli strumenti per vedere le specifiche del design, copiare il CSS e accedere alle risorse.
### Usa il Prototipo
Fai clic su qualsiasi elemento del canvas e premi il pulsante "Play" nell'angolo in alto a destra dell'interfaccia per accedere alla visuale del prototipo. La modalità Prototipo ti permette di interagire con il design come se fosse il prodotto finale. Dimostra il flusso tra gli schermi e il comportamento degli elementi dell'interfaccia come pulsanti, link o menu quando interagiti.
1. **Comprendere transizioni e animazioni:** Nella modalità Prototipo, puoi vedere qualsiasi transizione o animazione aggiunta da un designer tra gli schermi o gli elementi dell'interfaccia utente, fornendo ai sviluppatori istruzioni visive chiare sul comportamento e lo stile previsto.
2. **Chiarimento dell'implementazione:** Un prototipo può anche aiutare a ridurre le ambiguità. Gli sviluppatori possono interagire con esso per ottenere una migliore comprensione della funzionalità o dell'aspetto di particolari elementi.
Per dettagli e consigli più completi sull'apprendimento della piattaforma Figma, puoi visitare la [Documentazione ufficiale di Figma](https://help.figma.com/hc/en-us).
### Misura le distanze
Seleziona un elemento, tieni premuto il tasto `Option` (Mac) o `Alt` (Windows), quindi passa con il mouse su un altro elemento per vedere la distanza tra di loro.
### Estensione Figma per VSCode (Consigliata)
[Figma per VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension) ti permette di navigare e ispezionare i file di design, collaborare con i designer, tracciare i cambiamenti e velocizzare l'implementazione - tutto senza lasciare il tuo editor di testo.
Fa parte delle nostre estensioni consigliate.
## Collaborazione
1. **Utilizzo dei commenti:** Puoi utilizzare la funzione commento cliccando sull'icona a forma di fumetto nella parte sinistra della barra degli strumenti.
2. **Chat del cursore:** Una caratteristica interessante di Figma è la chat del cursore. Premi `;` su Mac e `/` su Windows per inviare un messaggio se vedi qualcun altro utilizzare Figma nello stesso momento.

View File

@@ -0,0 +1,318 @@
---
title: Configurazione Locale
icon: laptop-code
description: La guida per i collaboratori (o sviluppatori curiosi) che vogliono eseguire Twenty localmente.
---
## Prerequisiti
<Tabs>
<Tab title="Linux e macOS">
Prima di poter installare e usare Twenty, assicurati di installare quanto segue sul tuo computer:
* [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` non funzionerà, dovresti usare `yarn` invece. Yarn è ora incluso con Node.js, quindi non è necessario installarlo separatamente.
Devi solo eseguire `corepack enable` per abilitare Yarn se non l'hai ancora fatto.
</Warning>
</Tab>
<Tab title="Windows (WSL)">
1. Installa WSL
Apri PowerShell come amministratore ed esegui:
```powershell
wsl --install
```
Dovresti ora vedere un prompt per riavviare il computer. In caso contrario, riavvialo manualmente.
Al riavvio, si aprirà una finestra di PowerShell e installerà Ubuntu. Questo potrebbe richiedere un po' di tempo.
Vedrai un prompt per creare un nome utente e una password per la tua installazione di Ubuntu.
2. Installa e configura Git
```bash
sudo apt-get install git
git config --global user.name "Your Name"
git config --global user.email "youremail@domain.com"
```
3. Installa nvm, node.js e yarn
<Warning>
Usa `nvm` per installare la versione corretta di `node`. Il file `.nvmrc` garantisce che tutti i collaboratori utilizzino la stessa versione.
</Warning>
```bash
sudo apt-get install curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
```
Chiudi e riapri il tuo terminale per usare nvm. Poi esegui i seguenti comandi.
```bash
nvm install # installs recommended node version
nvm use # use recommended node version
corepack enable
```
</Tab>
</Tabs>
---
## Passaggio 1: Clona con Git
Nel tuo terminale, esegui il seguente comando.
<Tabs>
<Tab title="SSH (Consigliato)">
Se non hai già configurato le chiavi SSH, puoi imparare come farlo [qui](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh).
```bash
git clone git@github.com:twentyhq/twenty.git
```
</Tab>
<Tab title="HTTPS">
```bash
git clone https://github.com/twentyhq/twenty.git
```
</Tab>
</Tabs>
## Passaggio 2: Posizionati alla radice
```bash
cd twenty
```
Dovresti eseguire tutti i comandi nei passaggi successivi dalla radice del progetto.
## Passaggio 3: Configura un database PostgreSQL
<Tabs>
<Tab title="Linux">
**Opzione 1 (preferita):** Per predisporre il database in locale:
Usa il seguente link per installare PostgreSQL sulla tua macchina Linux: [Installazione di PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Nota: Potrebbe essere necessario aggiungere `sudo -u postgres` al comando prima di `psql` per evitare errori di permessi.
**Opzione 2:** Se hai Docker installato:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Mac OS">
**Opzione 1 (preferita):** Per predisporre il database in locale con `brew`:
```bash
brew install postgresql@16
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
brew services start postgresql@16
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Puoi verificare se il server PostgreSQL è in esecuzione eseguendo:
```bash
brew services list
```
Il programma di installazione potrebbe non creare l'utente `postgres` per impostazione predefinita quando si installa
tramite Homebrew su macOS. Invece, crea un ruolo di PostgreSQL che corrisponde al tuo nome utente macOS
(es., "john").
Per controllare e creare l'utente `postgres` se necessario, segui questi passaggi:
```bash
# Connect to PostgreSQL
psql postgres
or
psql -U $(whoami) -d postgres
```
Una volta nel prompt di psql (postgres=#), esegui:
```bash
# List existing PostgreSQL roles
\du
```
Vedrai un output simile a:
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
john | Superuser | {}
```
Se non vedi un ruolo `postgres` elencato, procedi al passo successivo.
Crea manualmente il ruolo `postgres`:
```bash
CREATE ROLE postgres WITH SUPERUSER LOGIN;
```
Questo crea un ruolo superuser chiamato `postgres` con accesso di login.
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
postgres | Superuser | {}
john | Superuser | {}
```
**Opzione 2:** Se hai Docker installato:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
Tutti i passaggi seguenti devono essere eseguiti nel terminale WSL (all'interno della tua macchina virtuale)
**Opzione 1:** Per predisporre PostgreSQL in locale:
Usa il seguente link per installare PostgreSQL sulla tua macchina virtuale Linux: [Installazione di PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Nota: Potrebbe essere necessario aggiungere `sudo -u postgres` al comando prima di `psql` per evitare errori di permessi.
**Opzione 2:** Se hai Docker installato:
Eseguire Docker su WSL aggiunge un livello extra di complessità.
Usa questa opzione solo se ti senti a tuo agio con i passaggi extra coinvolti, incluso l'attivazione di [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
Ora puoi accedere al database all'indirizzo `localhost:5432`.
Se hai utilizzato l'opzione Docker sopra, le credenziali predefinite sono utente `postgres` e password `postgres`. Per le installazioni native di PostgreSQL, usa le credenziali e i ruoli configurati sulla tua macchina.
## Passaggio 4: Configura un database Redis (cache)
Twenty richiede una cache Redis per offrire le migliori prestazioni.
<Tabs>
<Tab title="Linux">
**Opzione 1:** Per predisporre Redis in locale:
Usa il seguente link per installare Redis sulla tua macchina Linux: [Installazione di Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Opzione 2:** Se hai Docker installato:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Mac OS">
**Opzione 1 (preferita):** Per predisporre Redis in locale con `brew`:
```bash
brew install redis
```
Avvia il tuo server Redis:
```bash
brew services start redis
```
**Opzione 2:** Se hai Docker installato:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
**Opzione 1:** Per predisporre Redis in locale:
Usa il seguente link per installare Redis sulla tua macchina virtuale Linux: [Installazione di Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Opzione 2:** Se hai Docker installato:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
Se hai bisogno di una GUI client, ti consigliamo [Redis Insight](https://redis.io/insight/) (versione gratuita disponibile).
## Passaggio 5: Configura le variabili d'ambiente
Usa variabili d'ambiente o file `.env` per configurare il tuo progetto. Maggiori informazioni [qui](/l/it/developers/self-host/capabilities/setup).
Copia i file `.env.example` in `/front` e `/server`:
```bash
cp ./packages/twenty-front/.env.example ./packages/twenty-front/.env
cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
```
<Info>
**Modalità multi-workspace:** Per impostazione predefinita, Twenty viene eseguito in modalità a singolo workspace in cui può essere creato un solo workspace. Per abilitare il supporto multi-workspace (utile per testare le funzionalità basate sui sottodomini), imposta `IS_MULTIWORKSPACE_ENABLED=true` nel file `.env` del server. Vedi [Modalità multi-workspace](/l/it/developers/self-host/capabilities/setup#multi-workspace-mode) per i dettagli.
</Info>
## Passaggio 6: Installazione delle dipendenze
Per costruire il server Twenty e popolare alcuni dati nel tuo database, esegui il seguente comando:
```bash
yarn
```
Nota che `npm` o `pnpm` non funzioneranno
## Passaggio 7: Esecuzione del progetto
<Tabs>
<Tab title="Linux">
A seconda della tua distribuzione Linux, il server Redis potrebbe essere avviato automaticamente.
In caso contrario, controlla la [guida all'installazione di Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) per la tua distribuzione.
</Tab>
<Tab title="Mac OS">
Redis dovrebbe essere già in esecuzione. In caso contrario, esegui:
```bash
brew services start redis
```
</Tab>
<Tab title="Windows (WSL)">
A seconda della tua distribuzione Linux, il server Redis potrebbe essere avviato automaticamente.
In caso contrario, controlla la [guida all'installazione di Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) per la tua distribuzione.
</Tab>
</Tabs>
Configura il tuo database con il seguente comando:
```bash
npx nx database:reset twenty-server
```
Avvia il server, il worker e i servizi del frontend:
```bash
npx nx start twenty-server
npx nx worker twenty-server
npx nx start twenty-front
```
In alternativa, puoi avviare tutti i servizi contemporaneamente:
```bash
npx nx start
```
## Passo 8: Utilizza Twenty
**Frontend**
Il frontend di Twenty sarà in esecuzione su [http://localhost:3001](http://localhost:3001).
Puoi accedere utilizzando l'account demo predefinito: `tim@apple.dev` (password: `tim@apple.dev`)
**Backend**
* Il server di Twenty sarà attivo e funzionante su [http://localhost:3000](http://localhost:3000)
* L'API GraphQL è accessibile su [http://localhost:3000/graphql](http://localhost:3000/graphql)
* L'API REST è raggiungibile su [http://localhost:3000/rest](http://localhost:3000/rest)
## Risoluzione dei problemi
Se riscontri problemi, controlla [Risoluzione dei problemi](/l/it/developers/self-host/capabilities/troubleshooting) per le soluzioni.

View File

@@ -0,0 +1,77 @@
---
title: Comandi
icon: terminal
description: Comandi utili per sviluppare Twenty.
---
I comandi possono essere eseguiti dalla radice del repository usando `npx nx`. Usa `npx nx run {project}:{command}` per specificare esplicitamente il target.
## Avviare l'app
```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
```
## Database
```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
```
## Controllo dei tipi
```bash
npx nx typecheck twenty-front
npx nx typecheck twenty-server
```
## Testing
```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
```
## Traduzioni
```bash
npx nx run twenty-front:lingui:extract # Extract strings
npx nx run twenty-front:lingui:compile # Compile translations
```
## Compilazione
```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: Contribuire
description: Contribuisci allo sviluppo open source di Twenty.
---
<Frame>
<img src="/images/user-guide/github/github-header.png" alt="AI" />
</Frame>
## Panoramica
Twenty è open source e accoglie i contributi della comunità. Che tu stia risolvendo bug, aggiungendo funzionalità o migliorando la documentazione, i tuoi contributi aiutano a rendere Twenty migliore per tutti.
## Come contribuire
* **Segnala bug**: Aiuta a identificare e documentare i problemi
* **Proponi funzionalità**: Proponi e implementa nuove funzionalità
* **Migliora la documentazione**: Rendi la nostra documentazione più chiara e utile
* **Sviluppo frontend**: Lavora sull'interfaccia utente basata su React
* **Sviluppo backend**: Contribuisci al server NestJS
## Per iniziare
<CardGroup cols={2}>
<Card title="Segnalazioni di bug e richieste" icon="bug" href="/l/it/developers/contribute/capabilities/bug-and-requests">
Segnala problemi o richiedi funzionalità
</Card>
<Card title="Sviluppo Frontend" icon="browser" href="/l/it/developers/contribute/capacities/frontend-development/folder-architecture-front">
Contribuisci all'interfaccia utente
</Card>
</CardGroup>

View File

@@ -0,0 +1,176 @@
---
title: Guida di stile
icon: paintbrush
description: Convenzioni di codice e buone pratiche per contribuire a Twenty.
---
## React
### Solo componenti funzionali
Usa sempre componenti funzionali TSX con export nominati.
```tsx
// ❌ Bad
const MyComponent = () => {
return <div>Hello World</div>;
};
export default MyComponent;
// ✅ Good
export function MyComponent() {
return <div>Hello World</div>;
};
```
### Props
Crea un tipo denominato `{ComponentName}Props`. Usa la destrutturazione. Non usare `React.FC`.
```tsx
type MyComponentProps = {
name: string;
};
export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;
```
### Niente spread di una singola variabile nelle props
```tsx
// ❌ Bad
const MyComponent = (props: MyComponentProps) => <Other {...props} />;
// ✅ Good
const MyComponent = ({ prop1, prop2 }: MyComponentProps) => <Other {...{ prop1, prop2 }} />;
```
## Gestione dello stato
### Atomi Jotai per lo stato globale
```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',
});
```
* Preferisci gli atomi al prop drilling
* Non usare `useRef` per lo stato — usa `useState` o gli atomi
* Usa famiglie di atomi e selettori per le liste
### Evita i re-render inutili
* Estrai `useEffect` e il recupero dei dati in componenti sidecar fratelli
* Preferisci i gestori di eventi (`handleClick`, `handleChange`) a `useEffect`
* Non usare `React.memo()` — risolvi invece la causa principale
* Limita l'uso di `useCallback` o `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` invece di `interface`** — più flessibile, più facile da comporre
* **Letterali di stringa invece degli enum** — tranne per gli enum generati da GraphQL codegen e le API interne della libreria
* **Niente `any`** — TypeScript rigoroso applicato
* **Niente import di tipo** — usa import normali (applicato da Oxlint `typescript/consistent-type-imports`)
* **Usa [Zod](https://github.com/colinhacks/zod)** per la validazione a runtime di oggetti non tipizzati
## JavaScript
```tsx
// Use nullish-coalescing (??) instead of ||
const value = process.env.MY_VALUE ?? 'default';
// Use optional chaining
onClick?.();
```
## Denominazione
* **Variabili**: camelCase, descrittive (`email` non `value`, `fieldMetadata` non `fm`)
* **Costanti**: SCREAMING_SNAKE_CASE
* **Tipi/Classi**: PascalCase
* **File/directory**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`)
* **Gestori di eventi**: `handleClick` (non `onClick` per la funzione gestore)
* **Props dei componenti**: metti come prefisso il nome del componente (`ButtonProps`)
* **Componenti styled**: metti come prefisso `Styled` (`StyledTitle`)
## Stile
Usa i componenti styled di [Linaria](https://github.com/callstack/linaria). Usa i valori del tema — evita `px`, `rem` o colori hardcoded.
```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)};
`;
```
## Importazioni
Usa gli alias invece dei percorsi relativi:
```tsx
// ❌ Bad
import { Foo } from '../../../../../testing/decorators/Foo';
// ✅ Good
import { Foo } from '~/testing/decorators/Foo';
import { Bar } from '@/modules/bar/components/Bar';
```
## Struttura delle cartelle
```
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, ...)
```
* I moduli possono importare da altri moduli, ma `ui/` dovrebbe restare privo di dipendenze
* Usa le sottocartelle `internal/` per il codice privato del modulo
* Componenti sotto le 300 righe, servizi sotto le 500 righe