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: Cele mai bune practici
icon: star
---
Acest document prezintă cele mai bune practici pe care ar trebui să le urmați atunci când lucrați la frontend.
## Managementul stării
React și Jotai se ocupă de managementul stării în cod.
### Folosiți atomi Jotai pentru a stoca starea
Este o bună practică să creezi atâția atomi câți ai nevoie pentru a-ți stoca starea.
<Warning>
Este mai bine să folosești atomi suplimentari decât să încerci să fii prea concis recurgând la prop drilling.
</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>
);
}
```
### Nu folosiți `useRef` pentru a stoca starea
Evitați utilizarea `useRef` pentru a stoca starea.
Dacă doriți să stocați starea, ar trebui să folosiți `useState` sau atomii Jotai cu `useAtomState`.
Vezi [cum să gestionezi re-randările](#managing-re-renders) dacă ți se pare că ai nevoie de `useRef` pentru a preveni apariția unor re-randări.
## Gestionarea re-randărilor
Re-render-urile pot fi greu de gestionat în React.
Aici sunt câteva reguli de urmat pentru a evita re-render-urile inutile.
Ține minte că poți **întotdeauna** evita re-randările înțelegând cauza lor.
### Lucrați la nivelul rădăcinii
Evitarea re-randărilor în funcționalități noi este acum ușoară prin eliminarea lor de la rădăcină.
Componenta secundară `PageChangeEffect` conține doar un `useEffect` ce deține toată logica de execuție la o schimbare de pagină.
În acest fel știți că există doar un loc care poate declanșa un re-render.
### Gândiți-vă de două ori înainte de a adăuga `useEffect` în baza de cod
Re-render-urile sunt adesea cauzate de `useEffect` inutile.
Ar trebui să vă gândiți dacă aveți nevoie de `useEffect` sau dacă puteți muta logica într-o funcție de handler de eveniment.
Vă va fi, în general, ușor să mutați logica într-o funcție `handleClick` sau `handleChange`.
De asemenea, le puteți găsi în biblioteci precum Apollo: `onCompleted`, `onError`, etc.
### Folosiți o componentă adiacentă pentru a extrage `useEffect` sau logica de interogare de date
Dacă simțiți nevoia să adăugați un `useEffect` în componenta rădăcină, ar trebui să luați în considerare extragerea sa într-o componentă secundară.
Puteți aplica același principiu pentru logica de interogare de date, folosind hooks cu 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 />
</>
);
```
### Folosiți stări de familie de atomi și selectoare
Stările de familie de atomi și selectoarele sunt o metodă excelentă de a evita re-randările.
Sunt utile când trebuie să stocați o listă de elemente.
### Nu ar trebui să folosiți `React.memo(MyComponent)`
Evitați utilizarea `React.memo()` deoarece nu rezolvă cauza re-render-ului, ci întrerupe lanțul re-render, ceea ce poate duce la comportamente neașteptate și face codul foarte greu de refactorizat.
### Limitați utilizarea `useCallback` sau `useMemo`
Acestea sunt adesea inutile și vor face codul mai greu de citit și menținut pentru un câștig în performanță care nu este sesizabil.
## Console.logs
Instrucțiunile `console.log` sunt valoroase în timpul dezvoltării, oferind informații în timp real despre valorile variabilelor și fluxul de cod. Totuși, lăsându-le în codul de producție pot duce la mai multe probleme:
1. **Performanță**: Logările excesive pot afecta performanța de execuție, în special în aplicațiile pe partea de client.
2. **Siguranță**: Logarea datelor sensibile poate expune informații critice oricui inspectează consola browser-ului.
3. **Claritate**: Umplerea consolei cu logări poate ascunde avertismente sau erori importante pe care dezvoltatorii sau uneltele le trebuie să le vadă.
4. **Profesionalism**: Utilizatorii finali sau clienții care verifică consola și văd o multitudine de instrucțiuni de log s-ar putea să pună la îndoială calitatea și finisajul codului.
Asigurați-vă că eliminați toate `console.log`-urile înainte de a trimite codul în producție.
## Denumiri
### Denumire de variabilă
Denumirile variabilelor trebuie să descrie cu precizie scopul sau funcția variabilei.
#### Problema cu denumirile generice
Denumirile generice în programare nu sunt ideale deoarece nu au specificitate, ducând la ambiguitate și la reducerea lizibilității codului. Astfel de denumiri nu reușesc să transmită scopul variabilei sau funcției, ceea ce îngreunează înțelegerea intenției codului de către dezvoltatori fără o investigație mai profundă. Acest lucru poate duce la creșterea timpului de depanare, la o mai mare susceptibilitate la erori și dificultăți în întreținere și colaborare. Între timp, denumirile descriptive fac codul auto-explicativ și mai ușor de navigat, îmbunătățind calitatea codului și productivitatea dezvoltatorilor.
```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('');
```
#### Câteva cuvinte de evitat în denumirile de variabile
* dummy
### Handlere de evenimente
Numele handlerelor de evenimente ar trebui să înceapă cu `handle`, în timp ce `on` este un prefix folosit pentru a denumi evenimentele în prop-urile componentelor.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## Props opționale
Evitați să transmiteți valoarea implicită pentru un props opțional.
**EXEMPLU**
Țineți cont de componenta `EmailField` definită mai jos:
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
<TextInput value={value} disabled={disabled} fullWidth />
);
```
**Utilizare**
```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" />;
```
## Componentă ca props
Încercați, pe cât posibil, să transmiteți componente neinstanțiate ca props, astfel încât copiii să poată decide singuri de ce props au nevoie să transmită.
Cel mai frecvent exemplu pentru aceasta sunt componentele de pictograme:
```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>
)
};
```
Pentru ca React să înțeleagă că componenta este o componentă, trebuie să folosești PascalCase, pentru a o instanția ulterior cu `<MyIcon>`
## Prop Drilling: Păstrați-l Minimal
Prop drilling, în contextul React, se referă la practica de a transmite variabile de stare și setatorii acestora prin multe straturi de componente, chiar dacă componentele intermediare nu le folosesc. Deși uneori necesar, prop drilling excesiv poate duce la:
1. **Citibilitate Scăzută**: Urmărirea de unde provine un prop sau unde este utilizat poate deveni complexă într-o structură de componente foarte profundă.
2. **Provocări de Mentenanță**: Schimbările în structura props a unei componente pot necesita ajustări în mai multe componente, chiar dacă acestea nu utilizează direct prop-ul.
3. **Reutilizabilitate Redusă a Componentelor**: O componentă ce primește multe props doar pentru a le transmite mai departe devine mai puțin de uz general și mai greu de reutilizat în contexte diferite.
Dacă simțiți că folosiți prea mult prop drilling, consultați [cele mai bune practici de gestionare a stării](#state-management).
## Importuri
Când importați, optați pentru pseudonimele desemnate în loc să specificați căi complete sau relative.
**Pseudonimele**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**Utilizare**
```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';
```
## Validarea Schemelor
[Zod](https://github.com/colinhacks/zod) este validatorul de scheme pentru obiectele netipizate:
```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>;
```
## Schimbări Majore
Efectuați întotdeauna teste manuale temeinice înainte de a continua pentru a garanta că modificările nu au cauzat întreruperi în altă parte, având în vedere că testele nu au fost încă integrate extensiv.

View File

@@ -0,0 +1,110 @@
---
title: Arhitectura Folderului
icon: folder-tree
info: O privire detaliată asupra arhitecturii folderului nostru
---
În acest ghid, veți explora detaliile structurii directoarelor proiectului și modul în care aceasta contribuie la organizarea și mentenabilitatea Twenty.
Respectând convenția de arhitectură a folderului, este mai ușor să găsești fișierele legate de funcționalități specifice și să te asiguri că aplicația este scalabilă și ușor de întreținut.
```
front
└───modules
│ └───module1
│ │ └───submodule1
│ └───module2
│ └───ui
│ │ └───display
│ │ └───inputs
│ │ │ └───buttons
│ │ └───...
└───pages
└───...
```
## Pagini
Include componentele de nivel superior definite de rutele aplicației. Importă componente de nivel mai scăzut din folderul de module (mai multe detalii mai jos).
## Module
Fiecare modul reprezintă o funcționalitate sau un grup de funcționalități, cuprinzând componentele, stările și logica operațională specifice.
Toate ar trebui să urmeze structura de mai jos. Poți introduce module în cadrul altor module (denumite submodule), iar aceleași reguli se vor aplica.
```
module1
└───components
│ └───component1
│ └───component2
└───constants
└───contexts
└───graphql
│ └───fragments
│ └───queries
│ └───mutations
└───hooks
│ └───internal
└───states
│ └───selectors
└───types
└───utils
```
### Contexturi
Un context este o modalitate de a transmite date prin arborele de componente fără a fi necesar să transmiți manual proprietățile la fiecare nivel.
Vezi [React Context](https://react.dev/reference/react#context-hooks) pentru mai multe detalii.
### GraphQL
Include fragmente, interogări și mutații.
Vezi [GraphQL](https://graphql.org/learn/) pentru mai multe detalii.
* Fragmente
Un fragment este o bucată reutilizabilă dintr-o interogare, pe care o poți folosi în diferite locuri. Folosind fragmente este mai ușor să eviți duplicarea codului.
Vezi [GraphQL Fragments](https://graphql.org/learn/queries/#fragments) pentru mai multe detalii.
* Interogări
Vezi [GraphQL Queries](https://graphql.org/learn/queries/) pentru mai multe detalii.
* Mutații
Vezi [GraphQL Mutations](https://graphql.org/learn/queries/#mutations) pentru mai multe detalii.
### Hook-uri
Vezi [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) pentru mai multe detalii.
### Stări
Conține logica de gestionare a stării. [Jotai](https://jotai.org) gestionează aceasta.
* Selectori: Atomi derivați (folosind `createAtomSelector`) calculează valori din alți atomi și sunt memoizați automat.
Managementul de stare încorporat în React gestionează încă starea în cadrul unei componente.
### Unelte
Ar trebui să conțină doar funcții pure, reutilizabile. În caz contrar, creează hook-uri personalizate în folderul `hooks`.
## UI
Conține toate componentele UI reutilizabile folosite în aplicație.
Acest folder poate conține subfoldere, precum `data`, `afișaj`, `feedback` și `intrare` pentru tipuri specifice de componente. Fiecare componentă ar trebui să fie autonomă și reutilizabilă, astfel încât să o poți folosi în diferite părți ale aplicației.
Prin separarea componentelor UI de celelalte componente din folderul `modules`, este mai ușor să menții un design consistent și să faci modificări la interfața grafică fără a afecta alte părți (logica de afaceri) ale codului.
## Interfață și dependențe
Poți importa codul altor module din orice modul, cu excepția folderului `ui`. Acest lucru va menține codul ușor de testat.
### Intern
Fiecare parte (hook-uri, stări, ...) dintr-un modul poate avea un folder `intern`, care conține părți care sunt doar utilizate în cadrul modulului.

View File

@@ -0,0 +1,91 @@
---
title: Comenzi Frontend
icon: terminal
---
## Comenzi utile
### Pornirea aplicației
```bash
npx nx start twenty-front
```
### Regenerează schema graphql pe baza schemei graphql a API-ului
```bash
npx nx run twenty-front:graphql:generate --configuration=metadata
```
SAU
```bash
npx nx run twenty-front:graphql:generate
```
### Lint
```bash
npx nx run twenty-front:lint # pass --fix to fix lint errors
```
## Traduceri
```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)
```
## Tehnologii Utilizate
Proiectul are un stack curat și simplu, cu cod boilerplate minim.
**Aplicație**
* [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/)
**Testare**
* [Jest](https://jestjs.io/)
* [Storybook](https://storybook.js.org/)
**Instrumente**
* [Yarn](https://yarnpkg.com/)
* [Craco](https://craco.js.org/docs/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
## Arhitectură
### Rutare
[React Router](https://reactrouter.com/) se ocupă de rutare.
Pentru a evita [re-redări](/l/ro/developers/contribute/capabilities/frontend-development/best-practices-front#managing-re-renders) inutile, toată logica de rutare se află în `useEffect` în `PageChangeEffect`.
### Managementul stării
[Jotai](https://jotai.org/) se ocupă de managementul stării.
Consultați [cele mai bune practici](/l/ro/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) pentru mai multe informații despre managementul stării.
## Testare
[Jest](https://jestjs.io/) servește ca instrument pentru testarea unităților, în timp ce [Storybook](https://storybook.js.org/) este pentru testarea componentelor.
Jest este utilizat în principal pentru testarea funcțiilor utilitare, nu a componentelor în sine.
Storybook este pentru testarea comportamentului componentelor izolate, precum și pentru afișarea sistemului de design.

View File

@@ -0,0 +1,178 @@
---
title: Comenzi rapide
---
## Introducere
Atunci când aveți nevoie să ascultați o comandă rapidă, în mod normal ați folosi evenimentul `onKeyDown`.
În `twenty-front` totuși, s-ar putea să aveți conflicte între aceleași comenzi rapide folosite în componente diferite, montate simultan.
De exemplu, dacă aveți o pagină care ascultă tasta Enter, și un modal care ascultă aceeași tastă, cu o componentă Select în interiorul acelui modal care ascultă și ea tasta Enter, s-ar putea să apară un conflict când toate sunt montate simultan.
## Hook-ul `useScopedHotkeys`
Pentru a gestiona această problemă, avem un hook personalizat care face posibilă ascultarea comenzilor rapide fără niciun conflict.
Îl așezați într-o componentă și va asculta comenzile rapide doar când componenta este montată ȘI când **domeniul comenzii rapide** specificat este activ.
## Cum detectezi scurtăturile de la tastatură în practică?
Sunt două etape implicate în configurarea ascultării comenzilor rapide:
1. Setați [domeniul comenzii rapide](#what-is-a-hotkey-scope-) care va asculta comenzi rapide
2. Folosiți `useScopedHotkeys` pentru a asculta comenzile rapide
Setarea domeniilor comenzilor rapide este necesară chiar și în paginile simple, deoarece alte elemente ale UI, cum ar fi meniul din stânga sau meniul de comandă, s-ar putea să asculte și ele comenzi rapide.
## Cazuri de utilizare pentru comenzi rapide
În general, veți avea două cazuri de utilizare care necesită comenzi rapide:
1. Într-o pagină sau componentă montată într-o pagină
2. Într-o componentă de tip modal care preia focusul datorită unei acțiuni a utilizatorului
Al doilea caz de utilizare poate apărea recursiv: un dropdown într-un modal, de exemplu.
### Ascultarea comenzilor rapide într-o pagină
Exemplu:
```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>;
};
```
### Ascultarea comenzilor rapide într-o componentă de tip modal
În acest exemplu vom folosi o componentă modal care ascultă tasta Escape pentru a-i spune părintelui să o închidă.
Aici interacțiunea utilizatorului modifică domeniul.
```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>;
};
```
Apoi în componenta modal:
```tsx
const MyDropdownComponent = ({ onClose }: { onClose: () => void }) => {
// 2. Use the useScopedHotkeys hook to listen for Escape.
// Note that escape is a common hotkey that could be used by many other components
// So it's important to use a hotkey scope to avoid conflicts
useScopedHotkeys(
Key.Escape,
() => {
onClose()
},
ExampleHotkeyScopes.ExampleModal,
);
return <div>My modal component</div>;
};
```
Este important să folosiți acest tipar când nu sunteți sigur că doar utilizând un useEffect la montare/demontare va fi suficient pentru a evita conflictele.
Aceste conflicte pot fi greu de depanat și s-ar putea să apară mai des decât credeți cu useEffects.
## Ce este un domeniu al comenzilor rapide?
Un domeniu al comenzilor rapide este un șir de caractere care reprezintă un context în care comenzile rapide sunt active. Este în general codificat sub forma unui enum.
Când schimbați domeniul comenzii rapide, comenzile rapide care ascultă acest domeniu vor fi activate, iar cele care ascultă alte domenii vor fi dezactivate.
Puteți seta doar un singur domeniu la un moment dat.
Ca exemplu, domeniile comenzilor rapide pentru fiecare pagină sunt definite în enumul `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',
}
```
Intern, domeniul selectat în prezent este stocat într-un atom Jotai care este partajat în toată aplicația:
```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
key: 'currentHotkeyScopeState',
defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```
Însă acest atom nu ar trebui niciodată să fie gestionat manual! Vom vedea cum să o folosim în secțiunea următoare.
## Cum funcționează intern?
Am făcut un wrapper subțire peste [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) care îl face mai performant și evită re-rendările inutile.
De asemenea, creăm un atom Jotai pentru a gestiona starea domeniului comenzilor rapide și pentru a o face disponibilă oriunde în aplicație.

View File

@@ -0,0 +1,8 @@
---
title: Storybook
description: Răsfoiește biblioteca de componente UI Twenty
---
Vizualizați biblioteca noastră completă de componente și documentația în Storybook.
[Deschideți Storybook →](https://storybook.twenty.com)

View File

@@ -0,0 +1,291 @@
---
title: Ghid de stil
icon: paintbrush
---
Acest document include regulile ce trebuie urmate la scrierea codului.
Scopul aici este de a avea o bază de cod coerentă, ușor de citit și ușor de întreținut.
Pentru aceasta, este mai bine să fie mai detaliat decât prea concis.
Ține mereu minte că oamenii citesc codul mai des decât îl scriu, mai ales într-un proiect open-source, unde oricine poate contribui.
Există multe reguli care nu sunt definite aici, dar care sunt verificate automat de linters.
## React
### Folosește componente funcționale
Utilizează întotdeauna componente funcționale TSX.
Nu folosi `import` implicit cu `const`, deoarece este mai greu de citit și mai greu de importat cu completarea de cod.
```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>;
};
```
### Proprietăți
Creează tipul proprietăților și numește-l `(NumeComponentă)Props` dacă nu este necesar să o exporți.
Folosește destructurarea 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>;
```
#### Ferește-te de utilizarea `React.FC` sau `React.FunctionComponent` pentru a defini tipurile props
```tsx
/* ❌ - Bad, defines the component type annotations with `FC`
* - With `React.FC`, the component implicitly accepts a `children` prop
* even if it's not defined in the prop type. This might not always be
* desirable, especially if the component doesn't intend to render
* children.
*/
const EmailField: React.FC<{
value: string;
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
```
```tsx
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
* component's props
* - This method doesn't automatically include the children prop. If
* you want to include it, you have to specify it in OwnProps.
*/
type EmailFieldProps = {
value: string;
};
const EmailField = ({ value }: EmailFieldProps) => (
<TextInput value={value} disabled fullWidth />
);
```
#### Fără Împrăștierea de Proprietăți cu o singură variabilă în Elemente JSX
Evită utilizarea împrăștierii de proprietăți cu o singură variabilă în elemente JSX, cum ar fi `{...props}`. Această practică adesea duce la cod mai puțin lizibil și mai greu de întreținut deoarece nu este clar ce proprietăți primește componenta.
```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 }} />;
};
```
Raționament:
* La o privire rapidă, este mai evident ce proprietăți sunt transmise de cod, făcându-l mai ușor de înțeles și întreținut.
* Ajută la prevenirea unei cuplări strânse între componente prin proprietățile lor.
* Instrumentele de linting fac mai ușor să identifici proprietăți sprellate greșit sau neutilizate atunci când enumeri explicit proprietățile.
## JavaScript
### Utilizează operatorul de coalescență nulă `??`
```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';
```
### Folosește accesarea opțională `?.`
```tsx
// ❌ Bad
onClick && onClick();
// ✅ Good
onClick?.();
```
## TypeScript
### Utilizați `type` în loc de `interface`
Utilizați întotdeauna `type` în loc de `interface`, deoarece aproape întotdeauna se suprapun, iar `type` este mai flexibil.
```tsx
// ❌ Bad
interface MyInterface {
name: string;
}
// ✅ Good
type MyType = {
name: string;
};
```
### Folosește litere de șir în loc de enum
[Literele de șir](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) sunt metoda standard pentru manipularea valorilor similare cu enum în TypeScript. Sunt mai ușor de extins cu Pick și Omit, oferind o experiență mai bună dezvoltatorilor, mai ales cu completarea codului.
Poți vedea de ce TypeScript recomandă evitarea enumurilor [aici](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 și biblioteci interne
Ar trebui să folosești enumurile generate de GraphQL codegen.
Este de asemenea mai bine să folosești un enum atunci când utilizezi o bibliotecă internă, astfel încât biblioteca internă să nu trebuiască să expose un tip de literă de șir care nu este legată de API-ul intern.
Exemplu:
```TSX
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
```
## Stilizare
### Folosește ComponentaStilizată
Stilizează componentele cu [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
<div className="my-class">Hello World</div>
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
Prefixează componentele stilizate cu „Styled” pentru a le diferenția de componentele „reale”.
```tsx
// ❌ Bad
const Title = styled.div`
color: red;
`;
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
### Tematica
Utilizarea temei pentru majoritatea stilizării componentelor este abordarea preferată.
#### Unități de măsură
Evită utilizarea directă a valorilor `px` sau `rem` în componentele stilizate. Valorile necesare sunt de regulă deja definite în temă, așa că este recomandat să utilizezi tema pentru aceste scopuri.
#### Culori
Abține-te să introduci culori noi; în schimb, folosește paleta existentă din temă. În situația în care paleta nu se potrivește, te rugăm să lași un comentariu pentru ca echipa să poată remedia acest aspect.
```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};
`;
```
## Impunerea Neprecizării Importurilor de Tip
Evită importurile de tip. Pentru a impune acest standard, o regulă Oxlint verifică și raportează orice importuri de tip. Acest lucru ajută la menținerea consistenței și lizibilității în codul 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';
```
### De ce Fără Importuri de Tip
* **Consistență**: Evitând importurile de tip și folosind o singură abordare atât pentru importurile de tip cât și de valoare, baza de cod rămâne consistentă în stilul său de import module.
* **Lizibilitate**: Importurile doar de tip îmbunătățesc lizibilitatea codului, făcând clar când importați valori sau tipuri. Aceasta reduce ambiguitatea și face mai ușor de înțeles scopul simbolurilor importate.
* **Întreținere**: Îmbunătățește întreținerea bazei de cod, deoarece dezvoltatorii pot identifica și localiza importurile doar de tip când revizuiesc sau modifică codul.
### Regula Oxlint
O regulă Oxlint, `typescript/consistent-type-imports`, impune standardul fără importuri de tip. Această regulă va genera erori sau avertismente pentru orice încălcare a importurilor de tip.
Rețineți că această regulă abordează în mod specific cazurile-limită rare în care apar importuri de tip neintenționate. TypeScript descurajează el însuși această practică, așa cum este menționat în notele de lansare [TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). În majoritatea situațiilor, nu ar trebui să aveți nevoie de importuri doar de tip.
Pentru a vă asigura că codul respectă această regulă, rulați Oxlint ca parte a fluxului de lucru de dezvoltare.

View File

@@ -0,0 +1,61 @@
---
title: Lucrează cu Figma
info: Află cum poți colabora cu Figma de la Twenty.
---
Figma este un instrument de design de interfață colaborativ care ajută la eliminarea barierelor de comunicare între designeri și dezvoltatori.
Acest ghid explică cum poți colabora cu Figma.
## Acces
1. **Accesează linkul partajat:** Poți accesa fișierul Figma al proiectului [aici](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty).
2. **Autentificare:** Dacă nu ești deja autentificat, Figma te va solicita să faci acest lucru.
Funcțiile principale sunt disponibile doar pentru utilizatorii autentificați, cum ar fi modul dezvoltator și capacitatea de a selecta un cadru dedicat.
<Warning>
Nu vei putea colabora eficient fără un cont.
</Warning>
## Structura Figma
În bara laterală din stânga, poți accesa diferitele pagini din Figma de la Twenty. Acestea sunt organizate astfel:
* **Pagina de componente:** Aceasta este prima pagină. Designerul o folosește pentru a crea și organiza elementele de design reutilizabile folosite în întregul fișier de design. De exemplu, butoane, pictograme, simboluri sau oricare alte componente reutilizabile. Aceasta servește la menținerea consistenței în cadrul designului.
* **Pagina principală:** A doua pagină este pagina principală, care afișează interfața completă a proiectului. Poți apăsa ***Play*** pentru a utiliza prototipul complet al aplicației.
* **Pagini de funcționalitate:** Celelalte pagini sunt de obicei dedicate funcționalităților în curs de dezvoltare. Acestea conțin designul caracteristicilor specifice sau modulelor aplicației sau site-ului web. De obicei, sunt încă în dezvoltare.
## Sfaturi utile
Cu acces doar pentru citire, nu poți edita designul, dar poți accesa toate funcțiile care vor fi utile pentru a converti designurile în cod.
### Folosește Modul Dev
Modul Dev al Figma îmbunătățește productivitatea dezvoltatorilor prin oferirea unei navigări facile în design, gestionarea eficientă a resurselor, instrumente de comunicare eficace, integrări de toolbox, fragmente rapide de cod și informații esențiale despre straturi, reducând decalajul între design și dezvoltare. Poți afla mai multe despre Modul Dev [aici](https://www.figma.com/dev-mode/).
Comută la modul "Developer" în partea dreaptă a barei de instrumente pentru a vedea specificațiile de design, a copia CSS și a accesa resursele.
### Folosește Prototipul
Fă clic pe orice element de pe pânză și apasă butonul “Play” din colțul de sus dreapta al interfeței pentru a accesa vizualizarea prototip. Modul Prototip îți permite să interacționezi cu designul ca și cum ar fi produsul final. Acesta demonstrează fluxul între ecrane și cum elementele de interfață, cum ar fi butoanele, link-urile sau meniurile, se comportă la interacțiune.
1. **Înțelegerea tranzițiilor și animațiilor:** În modul de prototip, poți vizualiza orice tranziții sau animații adăugate de designer între ecrane sau elementele UI, oferind indicații vizuale clare dezvoltatorilor despre comportamentul și stilul dorit.
2. **Clarificarea implementării:** Un prototip poate ajuta, de asemenea, la reducerea ambiguităților. Dezvoltatorii pot interacționa cu acesta pentru a înțelege mai bine funcționalitatea sau aspectul elementelor particulare.
Pentru mai multe detalii și îndrumări despre învățarea platformei Figma, poți vizita [Documentația oficială Figma](https://help.figma.com/hc/en-us).
### Măsoară distanțele
Selectează un element, ține apăsată tasta `Option` (Mac) sau `Alt` (Windows), apoi plasează cursorul peste un alt element pentru a vedea distanța dintre ele.
### Extensia Figma pentru VSCode (Recomandat)
[Figma pentru VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension)
îți permite să navighezi și să inspectezi fișierele de design, să colaborezi cu designeri, să urmărești schimbările și să grăbești implementarea - totul fără a părăsi editorul de text.
Face parte din extensiile noastre recomandate.
## Colaborare
1. **Utilizarea comentariilor:** Ești binevenit să folosești funcția de comentarii făcând clic pe pictograma de bule din partea stângă a barei de instrumente.
2. **Chat cu Cursor:** O caracteristică plăcută a Figma este Chatul cu Cursor. Apasă `;` pe Mac și `/` pe Windows pentru a trimite un mesaj dacă vezi pe altcineva folosind Figma în același timp cu tine.