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,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.