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: Melhores Práticas
icon: star
---
Este documento descreve as melhores práticas que você deve seguir ao trabalhar no frontend.
## Gerenciamento de Estado
React e Jotai lidam com o gerenciamento de estado na base de código.
### Use átomos do Jotai para armazenar o estado
É uma boa prática criar tantos átomos quanto necessário para armazenar seu estado.
<Warning>
É melhor usar átomos extras do que tentar ser muito conciso com a perfuração de 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>
);
}
```
### Não use `useRef` para armazenar estado
Evite usar `useRef` para armazenar estado.
Se quiser armazenar o estado, você deve usar `useState` ou átomos do Jotai com `useAtomState`.
Veja [como gerenciar re-renderizações](#managing-re-renders) se você sentir que precisa de `useRef` para evitar que algumas re-renderizações aconteçam.
## Gerenciando re-renderizações
As re-renderizações podem ser difíceis de gerenciar no React.
Aqui estão algumas regras a seguir para evitar re-renderizações desnecessárias.
Lembre-se de que você pode **sempre** evitar re-renderizações entendendo sua causa.
### Trabalhe no nível raiz
Evitar re-renderizações em novos recursos agora é fácil eliminando-as no nível raiz.
O componente sidecar `PageChangeEffect` contém apenas um `useEffect` que contém toda a lógica para executar em uma mudança de página.
Assim você sabe que há apenas um lugar que pode disparar uma re-renderização.
### Sempre pense duas vezes antes de adicionar `useEffect` em sua base de código
As renderizações são frequentemente causadas por `useEffect` desnecessários.
Você deve pensar se precisa de `useEffect`, ou se você pode mover a lógica em uma função de manipulador de eventos.
Você vai achar geralmente fácil mover a lógica na função `handleClick` ou `handleChange`.
Você também pode encontrá-las em bibliotecas como Apollo: `onCompleted`, `onError`, etc.
### Use um componente irmão para extrair `useEffect` ou lógica de busca de dados
Se você acha que precisa adicionar um `useEffect` no seu componente raiz, considere extraí-lo em um componente sidecar.
Você pode aplicar o mesmo para lógica de busca de dados, com hooks do 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 />
</>
);
```
### Use famílias de átomos e seletores de família
Famílias de átomos e seletores são uma ótima maneira de evitar re-renderizações.
Eles são úteis quando você precisa armazenar uma lista de itens.
### Você não deve usar `React.memo(MyComponent)`
Evite usar `React.memo()` porque não resolve a causa da re-renderização, mas em vez disso quebra a cadeia de re-renderização, o que pode levar a um comportamento inesperado e tornar o código muito difícil de refatorar.
### Limite o uso de `useCallback` ou `useMemo`
Eles muitas vezes não são necessários e tornarão o código mais difícil de ler e manter por um ganho de desempenho que é imperceptível.
## Console.logs
As instruções `console.log` são valiosas durante o desenvolvimento, oferecendo insights em tempo real sobre valores de variáveis e fluxo de código. Mas, deixá-los no código de produção pode levar a vários problemas:
1. **Desempenho**: Log excessivo pode afetar o desempenho de execução, especialmente em aplicativos do lado do cliente.
2. **Segurança**: Registrar dados sensíveis pode expor informações críticas para qualquer pessoa que inspecionar o console do navegador.
3. **Limpeza**: Encher o console com logs pode obscurecer avisos ou erros importantes que desenvolvedores ou ferramentas precisam ver.
4. **Profissionalismo**: Usuários finais ou clientes verificando o console e vendo uma infinidade de instruções de log podem questionar a qualidade e o acabamento do código.
Certifique-se de remover todos os `console.logs` antes de enviar o código para produção.
## Nomeação
### Nomeação de Variáveis
Os nomes das variáveis devem descrever precisamente o propósito ou função da variável.
#### O problema com nomes genéricos
Nomes genéricos na programação não são ideais porque carecem de especificidade, levando a ambiguidade e reduzindo a legibilidade do código. Tais nomes não conseguem transmitir o propósito da variável ou função, tornando desafiador para os desenvolvedores entender a intenção do código sem uma investigação mais profunda. Isso pode resultar em aumento do tempo de depuração, maior susceptibilidade a erros e dificuldades na manutenção e colaboração. Enquanto isso, nomeação descritiva torna o código auto-explicativo e mais fácil de navegar, melhorando a qualidade do código e a produtividade do desenvolvedor.
```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('');
```
#### Algumas palavras a evitar em nomes de variáveis
* dummy
### Manipuladores de Eventos
Os nomes dos manipuladores de eventos devem começar com `handle`, enquanto `on` é um prefixo usado para nomear eventos em props de componentes.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## Props Opcionais
Evite passar o valor padrão para um prop opcional.
**EXEMPLO**
Observe o componente `EmailField` definido abaixo:
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
<TextInput value={value} disabled={disabled} fullWidth />
);
```
**Uso**
```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 como props
Tente tanto quanto possível passar componentes não instanciados como props, para que os filhos possam decidir por si mesmos quais props precisam passar.
O exemplo mais comum é de componentes de ícone:
```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>
)
};
```
Para que o React entenda que o componente é um componente, você precisa usar PascalCase, para depois instanciá-lo com `<MyIcon>`
## Perfuração de Props: Mantenha-a Mínima
Perfuração de props, no contexto do React, refere-se à prática de passar variáveis de estado e seus setters por muitas camadas de componentes, mesmo que componentes intermediários não os usem. Embora às vezes seja necessário, perfuração excessiva de props pode levar a:
1. **Legibilidade Reduzida**: Rastreamento de onde um prop se origina ou onde é utilizado pode se tornar confuso em uma estrutura de componentes profundamente aninhada.
2. **Desafios de Manutenção**: Mudanças na estrutura de props de um componente podem exigir ajustes em vários componentes, mesmo que eles não usem diretamente o prop.
3. **Redução da Reutilização de Componentes**: Um componente que recebe muitos props apenas para passá-los adiante torna-se menos de propósito geral e mais difícil de reutilizar em diferentes contextos.
Se você sentir que está usando perfuração excessiva de props, veja [melhores práticas de gerenciamento de estado](#state-management).
## Importações
Ao importar, opte pelos apelidos designados em vez de especificar caminhos completos ou relativos.
**Os Pseudónimos**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**Uso**
```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';
```
## Validação de Esquema
[Zod](https://github.com/colinhacks/zod) é o validador de esquema para objetos não tipados:
```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>;
```
## Alterações Incompatíveis
Sempre execute testes manuais completos antes de prosseguir para garantir que as modificações não tenham causado interrupções em outros lugares, dado que os testes ainda não foram extensivamente integrados.

View File

@@ -0,0 +1,110 @@
---
title: Arquitetura de Pastas
icon: folder-tree
info: Um olhar detalhado sobre nossa arquitetura de pastas
---
Neste guia, você explorará os detalhes da estrutura de diretórios do projeto e como isso contribui para a organização e manutenção do Twenty.
Seguindo esta convenção de arquitetura de pastas, é mais fácil encontrar os arquivos relacionados a funcionalidades específicas e garantir que a aplicação seja escalável e manutenível.
```
front
└───modules
│ └───module1
│ │ └───submodule1
│ └───module2
│ └───ui
│ │ └───display
│ │ └───inputs
│ │ │ └───buttons
│ │ └───...
└───pages
└───...
```
## Páginas
Inclui os componentes de nível superior definidos pelas rotas da aplicação. Elas importam componentes de nível mais baixo da pasta `modules` (mais detalhes abaixo).
## Módulos
Cada módulo representa uma funcionalidade ou um grupo de funcionalidades, compreendendo seus componentes específicos, estados e lógica operacional.
Todos devem seguir a estrutura abaixo. Você pode aninhar módulos dentro de módulos (referidos como submódulos) e as mesmas regras se aplicarão.
```
module1
└───components
│ └───component1
│ └───component2
└───constants
└───contexts
└───graphql
│ └───fragments
│ └───queries
│ └───mutations
└───hooks
│ └───internal
└───states
│ └───selectors
└───types
└───utils
```
### Contextos
Um contexto é uma maneira de passar dados através da árvore de componentes sem ter que passar propriedades manualmente em cada nível.
Veja [React Context](https://react.dev/reference/react#context-hooks) para mais detalhes.
### GraphQL
Inclui fragmentos, consultas e mutações.
Veja [GraphQL](https://graphql.org/learn/) para mais detalhes.
* Fragmentos
Um fragmento é uma parte reutilizável de uma consulta, que você pode usar em diferentes lugares. Usando fragmentos, é mais fácil evitar duplicar código.
Veja [GraphQL Fragments](https://graphql.org/learn/queries/#fragments) para mais detalhes.
* Consultas
Veja [GraphQL Queries](https://graphql.org/learn/queries/) para mais detalhes.
* Mutações
Veja [GraphQL Mutations](https://graphql.org/learn/queries/#mutations) para mais detalhes.
### Hooks
Veja [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) para mais detalhes.
### Estados
Contém a lógica de gerenciamento de estado. [Jotai](https://jotai.org) lida com isso.
* Seletores: Átomos derivados (usando `createAtomSelector`) calculam valores a partir de outros átomos e são automaticamente memoizados.
O gerenciamento de estado embutido do React ainda lida com o estado dentro de um componente.
### Utilitários
Deve conter apenas funções puras reutilizáveis. Caso contrário, crie hooks personalizados na pasta `hooks`.
## UI
Contém todos os componentes reutilizáveis de UI usados na aplicação.
Esta pasta pode conter subpastas, como `data`, `display`, `feedback` e `input` para tipos específicos de componentes. Cada componente deve ser autocontido e reutilizável, de modo que você possa usá-lo em diferentes partes da aplicação.
Ao separar os componentes de UI dos outros componentes na pasta `modules`, é mais fácil manter um design consistente e fazer alterações na UI sem afetar outras partes (lógica de negócios) do código.
## Interface e dependências
Você pode importar código de outros módulos, exceto da pasta `ui`. Isso manterá seu código fácil de testar.
### Interno
Cada parte (hooks, estados, ...) de um módulo pode ter uma pasta `internal`, que contém partes que são usadas apenas dentro do módulo.

View File

@@ -0,0 +1,91 @@
---
title: Comandos do Frontend
icon: terminal
---
## Comandos Úteis
### Iniciando o aplicativo
```bash
npx nx start twenty-front
```
### Regenerar esquema GraphQL baseado no esquema de API GraphQL
```bash
npx nx run twenty-front:graphql:generate --configuration=metadata
```
OU
```bash
npx nx run twenty-front:graphql:generate
```
### Lint
```bash
npx nx run twenty-front:lint # pass --fix to fix lint errors
```
## Traduções
```bash
npx nx run twenty-front:lingui:extract
npx nx run twenty-front:lingui:compile
```
### Teste
```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)
```
## Pilha de Tecnologias
O projeto possui uma pilha limpa e simples, com pouco código boilerplate.
**Aplicativo**
* [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/)
**Testes**
* [Jest](https://jestjs.io/)
* [Storybook](https://storybook.js.org/)
**Ferramentas**
* [Yarn](https://yarnpkg.com/)
* [Craco](https://craco.js.org/docs/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
## Arquitetura
### Roteamento
[React Router](https://reactrouter.com/) gerencia o roteamento.
Para evitar [re-renders]desnecessário (/developers/contribute/capabilities/frontend-development/best-practices-front#managing-re-renders) toda a lógica de roteamento está em um `useEffect` em `PageChangeEffect`.
### Gerenciamento de Estado
[Jotai](https://jotai.org/) gerencia o estado.
Veja [melhores práticas](/l/pt/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) para mais informações sobre gerenciamento de estado.
## Testes
[Jest](https://jestjs.io/) serve como ferramenta para testes unitários enquanto [Storybook](https://storybook.js.org/) para teste de componentes.
Jest é principalmente para testar funções utilitárias, e não os componentes em si.
Storybook é para testar o comportamento de componentes isolados, bem como exibir o sistema de design.

View File

@@ -0,0 +1,178 @@
---
title: Teclas de atalho
---
## Introdução
Quando você precisa ouvir uma tecla de atalho, normalmente usaria o listener de evento `onKeyDown`.
No entanto, em `twenty-front`, você pode ter conflitos entre as mesmas teclas de atalho que são usadas em diferentes componentes, montados ao mesmo tempo.
Por exemplo, se você tem uma página que escuta a tecla Enter, e um modal que escuta a tecla Enter, com um componente Select dentro desse modal que escuta a tecla Enter, você pode ter um conflito quando todos são montados ao mesmo tempo.
## O hook `useScopedHotkeys`
Para lidar com esse problema, temos um hook personalizado que permite ouvir as teclas de atalho sem qualquer conflito.
Você o coloca em um componente, e ele ouvirá as teclas de atalho somente quando o componente estiver montado E quando o **escopo da tecla de atalho** especificado estiver ativo.
## Como ouvir teclas de atalho na prática?
Há dois passos envolvidos na configuração da escuta de teclas de atalho :
1. Defina o [escopo da tecla de atalho](#what-is-a-hotkey-scope-) que ouvirá as teclas de atalho
2. Use o hook `useScopedHotkeys` para ouvir as teclas de atalho
Configurar escopos de teclas de atalho é necessário mesmo em páginas simples, porque outros elementos de UI como o menu à esquerda ou o menu de comando também podem ouvir teclas de atalho.
## Casos de uso para teclas de atalho
Em geral, você terá dois casos de uso que requerem teclas de atalho :
1. Em uma página ou um componente montado em uma página
2. Em um componente do tipo modal que assume o foco devido a uma ação do usuário
O segundo caso de uso pode ocorrer de forma recursiva : um dropdown em um modal, por exemplo.
### Ouvindo teclas de atalho em uma página
Exemplo :
```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>;
};
```
### Ouvindo teclas de atalho em um componente do tipo modal
Neste exemplo, usaremos um componente modal que ouve a tecla Escape para informar ao pai que feche.
Aqui, a interação do usuário está mudando o escopo.
```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>;
};
```
Então no componente 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>;
};
```
É importante usar este padrão quando você não tem certeza de que apenas usar um useEffect com montagem/desmontagem será suficiente para evitar conflitos.
Esses conflitos podem ser difíceis de depurar, e isso pode acontecer com mais frequência do que não com useEffects.
## O que é um escopo de tecla de atalho?
Um escopo de tecla de atalho é uma string que representa um contexto no qual as teclas de atalho estão ativas. Geralmente é codificado como um enum.
Quando você altera o escopo da tecla de atalho, as teclas associadas a esse escopo serão ativadas e as teclas associadas a outros escopos serão desativadas.
Você pode definir apenas um escopo por vez.
Como exemplo, os escopos de tecla de atalho para cada página são definidos no 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, o escopo atualmente selecionado é armazenado em um átomo Jotai que é compartilhado por toda a aplicação :
```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
key: 'currentHotkeyScopeState',
defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```
Mas esse átomo nunca deve ser manipulado manualmente! Veremos como usá-lo na próxima seção.
## Como funciona internamente?
Criamos um wrapper leve em cima de [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) que o torna mais eficiente e evita renderizações desnecessárias.
Também criamos um átomo Jotai para gerenciar o estado do escopo da tecla de atalho e torná-lo disponível em toda a aplicação.

View File

@@ -0,0 +1,8 @@
---
title: Storybook
description: Navegue pela biblioteca de componentes UI do Twenty
---
Veja toda a nossa biblioteca de componentes e documentação no Storybook.
[Abra o Storybook →](https://storybook.twenty.com)

View File

@@ -0,0 +1,291 @@
---
title: Guia de Estilo
icon: paintbrush
---
Este documento inclui as regras a seguir ao escrever código.
O objetivo aqui é ter uma base de código consistente, fácil de ler e fácil de manter.
Para isso, é melhor ser um pouco mais detalhado do que ser muito conciso.
Sempre tenha em mente que as pessoas leem código mais frequentemente do que o escrevem, especialmente em um projeto de código aberto, onde qualquer um pode contribuir.
Há muitas regras que não estão definidas aqui, mas que são verificadas automaticamente por linters.
## React
### Use componentes funcionais
Use sempre componentes funcionais TSX.
Não use `import` padrão com `const`, pois é mais difícil de ler e mais difícil de importar com autocomplete.
```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>;
};
```
### Propriedades
Crie o tipo das propriedades e chame-o de `(NomeDoComponente)Props` se não houver necessidade de exportá-lo.
Use destructuring de 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>;
```
#### Evite usar `React.FC` ou `React.FunctionComponent` para definir tipos de props
```tsx
/* ❌ - Bad, defines the component type annotations with `FC`
* - With `React.FC`, the component implicitly accepts a `children` prop
* even if it's not defined in the prop type. This might not always be
* desirable, especially if the component doesn't intend to render
* children.
*/
const EmailField: React.FC<{
value: string;
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
```
```tsx
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
* component's props
* - This method doesn't automatically include the children prop. If
* you want to include it, you have to specify it in OwnProps.
*/
type EmailFieldProps = {
value: string;
};
const EmailField = ({ value }: EmailFieldProps) => (
<TextInput value={value} disabled fullWidth />
);
```
#### Proibição de Prop Spreading de Variável Única em Elementos JSX
Evite usar o espalhamento de props de variável única em elementos JSX, como `{...props}`. Essa prática muitas vezes resulta em código menos legível e mais difícil de manter, pois não está claro quais props o componente está recebendo.
```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 }} />;
};
```
Justificativa:
* À primeira vista, é mais claro quais props o código passa, tornando mais fácil de entender e manter.
* Ajuda a evitar o acoplamento forte entre componentes via suas props.
* Ferramentas de linting tornam mais fácil identificar props mal digitadas ou não utilizadas quando você lista props explicitamente.
## JavaScript
### Use o operador de coalescência nula `??`
```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';
```
### Use encadeamento opcional `?.`
```tsx
// ❌ Bad
onClick && onClick();
// ✅ Good
onClick?.();
```
## TypeScript
### Use `type` em vez de `interface`
Sempre use `type` em vez de `interface`, porque elas quase sempre se sobrepõem e `type` é mais flexível.
```tsx
// ❌ Bad
interface MyInterface {
name: string;
}
// ✅ Good
type MyType = {
name: string;
};
```
### Use literais de string em vez de enums
[Literals de string](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) são o caminho a seguir para lidar com valores tipo enum no TypeScript. Eles são mais fáceis de estender com Pick e Omit, e oferecem uma melhor experiência de desenvolvedor, especialmente com autocomplete.
Você pode ver porque o TypeScript recomenda evitar enums [aqui](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 bibliotecas internas
Você deve usar enums que o GraphQL codegen gera.
É melhor usar um enum ao usar uma biblioteca interna, para que a biblioteca interna não precise expor um tipo literal de string que não está relacionado à API interna.
Exemplo:
```TSX
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
```
## Estilização
### Use StyledComponents
Estilize os componentes com [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
<div className="my-class">Hello World</div>
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
Prefixe componentes estilizados com "Styled" para diferenciá-los de componentes "reais".
```tsx
// ❌ Bad
const Title = styled.div`
color: red;
`;
```
```tsx
// ✅ Good
const StyledTitle = styled.div`
color: red;
`;
```
### Tematização
Utilizar o tema para a maioria da estilização dos componentes é a abordagem preferida.
#### Unidades de medida
Evite usar valores `px` ou `rem` diretamente dentro dos componentes estilizados. Os valores necessários geralmente já estão definidos no tema, por isso é recomendável fazer uso do tema para esses fins.
#### Cores
Evite introduzir novas cores; em vez disso, use a paleta existente do tema. Se houver uma situação em que a paleta não se alinhe, por favor, deixe um comentário para que a equipe possa corrigi-la.
```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};
`;
```
## Impor Importações Sem Tipo
Evite importações de tipo. Para impor esse padrão, uma regra do Oxlint verifica e relata quaisquer importações de tipos. Isso ajuda a manter a consistência e a legibilidade no código 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';
```
### Por Que Não Usar Importações de Tipo
* **Consistência**: Ao evitar importações de tipo e usar uma única abordagem para importações de tipo e valor, a base de código permanece consistente em seu estilo de importação de módulo.
* **Legibilidade**: Importações sem tipo melhoram a legibilidade do código, tornando claro quando você está importando valores ou tipos. Isso reduz a ambiguidade e facilita a compreensão do propósito dos símbolos importados.
* **Manutenção**: Melhora a manutenibilidade da base de código porque os desenvolvedores podem identificar e localizar importações somente de tipo ao revisar ou modificar o código.
### Regra do Oxlint
Uma regra do Oxlint, `typescript/consistent-type-imports`, aplica o padrão de não usar importações de tipos. Esta regra gerará erros ou avisos para qualquer violação de importação de tipo.
Observe que esta regra trata especificamente de casos isolados onde importações de tipos não intencionais ocorrem. O próprio TypeScript desencoraja essa prática, conforme mencionado nas [notas de lançamento do TypeScript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). Na maioria das situações, não é necessário usar importações somente de tipo.
Para garantir que seu código esteja em conformidade com essa regra, certifique-se de executar o Oxlint como parte do seu fluxo de trabalho de desenvolvimento.

View File

@@ -0,0 +1,61 @@
---
title: Trabalhar com Figma
info: Saiba como você pode colaborar com o Figma da Twenty
---
Figma é uma ferramenta de design de interface colaborativa que ajuda a superar a barreira de comunicação entre designers e desenvolvedores.
Este guia explica como você pode colaborar com o Figma.
## Acesso
1. **Acesse o link compartilhado:** Você pode acessar o arquivo do projeto no Figma [aqui](https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty).
2. **Entrar:** Se você ainda não estiver conectado, o Figma solicitará que o faça.
Recursos principais estão disponíveis apenas para usuários conectados, como o modo desenvolvedor e a capacidade de selecionar uma moldura dedicada.
<Warning>
Você não será capaz de colaborar efetivamente sem uma conta.
</Warning>
## Estrutura do Figma
Na barra lateral esquerda, você pode acessar as diferentes páginas do Figma da Twenty. É assim que elas estão organizadas:
* **Página de componentes:** Esta é a primeira página. O designer a utiliza para criar e organizar os elementos de design reutilizáveis utilizados em todo o arquivo de design. Por exemplo, botões, ícones, símbolos, ou qualquer outro componente reutilizável. Serve para manter a consistência em todo o design.
* **Página principal:** A segunda página é a página principal, que mostra a interface completa do usuário do projeto. Você pode pressionar ***Play*** para usar o protótipo completo do aplicativo.
* **Páginas de recursos:** As outras páginas são tipicamente dedicadas a recursos em andamento. Elas contêm o design de recursos ou módulos específicos do aplicativo ou website. Elas geralmente ainda estão em progresso.
## Dicas Úteis
Com acesso somente leitura, você não pode editar o design, mas pode acessar todos os recursos que serão úteis para converter os designs em código.
### Use o modo Dev
O Modo Dev do Figma aumenta a produtividade dos desenvolvedores ao oferecer navegação fácil no design, gerenciamento eficaz de ativos, ferramentas de comunicação eficientes, integrações de caixa de ferramentas, trechos de código rápidos, e informações importantes de camadas, unindo design e desenvolvimento. Você pode aprender mais sobre o Modo Dev [aqui](https://www.figma.com/dev-mode/).
Mude para o modo "Desenvolvedor" na parte direita da barra de ferramentas para ver especificações de design, copiar CSS, e acessar ativos.
### Use o Protótipo
Clique em qualquer elemento na tela e pressione o botão “Play” na borda superior direita da interface para acessar a visualização do protótipo. O modo Protótipo permite que você interaja com o design como se fosse o produto final. Demonstra o fluxo entre telas e como elementos da interface como botões, links, ou menus se comportam quando interagidos.
1. **Entendendo transições e animações:** No modo Protótipo, é possível visualizar quaisquer transições ou animações adicionadas por um designer entre telas ou elementos da UI, proporcionando instruções visuais claras aos desenvolvedores sobre o comportamento e estilo pretendidos.
2. **Esclarecimento de implementação:** Um protótipo também pode ajudar a reduzir ambiguidades. Os desenvolvedores podem interagir com ele para obter uma melhor compreensão da funcionalidade ou aparência de elementos específicos.
Para obter informações mais abrangentes e orientações sobre como aprender a plataforma Figma, você pode visitar a [Documentação Oficial do Figma](https://help.figma.com/hc/en-us).
### Medir distâncias
Selecione um elemento, mantenha a tecla `Option` (Mac) ou `Alt` (Windows) pressionada e passe o mouse sobre outro elemento para ver a distância entre eles.
### Extensão Figma para VSCode (Recomendado)
[Figma para VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension)
permite que você navegue e inspecione arquivos de design, colabore com designers, acompanhe alterações e acelere a implementação - tudo sem sair do seu editor de texto.
Faz parte das extensões recomendadas por nós.
## Colaboração
1. **Usando Comentários:** Você é bem-vindo para usar a ferramenta de comentários clicando no ícone de bolha na parte esquerda da barra de ferramentas.
2. **Chat do Cursor:** Uma característica interessante do Figma é o Chat do Cursor. Apenas pressione `;` no Mac e `/` no Windows para enviar uma mensagem se você vir outra pessoa usando Figma ao mesmo tempo que você.