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

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

View File

@@ -0,0 +1,22 @@
---
title: Melhores Práticas
---
Este documento descreve as melhores práticas que você deve seguir ao trabalhar no backend.
## Siga uma abordagem modular
O backend segue uma abordagem modular, que é um princípio fundamental ao trabalhar com NestJS. Certifique-se de dividir seu código em módulos reutilizáveis para manter uma base de código limpa e organizada.
Cada módulo deve encapsular um recurso ou funcionalidade específico e ter um escopo bem definido. Esta abordagem modular permite uma clara separação de preocupações e remove complexidades desnecessárias.
## Expor serviços para uso em módulos
Sempre crie serviços que tenham uma responsabilidade clara e única, o que melhora a legibilidade e a manutenção do código. Nomeie os serviços de forma descritiva e consistente.
Você também deve expor serviços que deseja usar em outros módulos. Expor serviços para outros módulos é possível através do poderoso sistema de injeção de dependências do NestJS e promove o acoplamento frouxo entre os componentes.
## Evite usar o tipo `any`
Quando você declara uma variável como `any`, o verificador de tipos do TypeScript não realiza nenhuma verificação de tipo, tornando possível atribuir qualquer tipo de valores à variável. O TypeScript usa inferência de tipos para determinar o tipo da variável com base no valor. Ao declará-lo como `any`, o TypeScript não pode mais inferir o tipo. Isso torna difícil capturar erros relacionados a tipos durante o desenvolvimento, levando a erros em tempo de execução e tornando o código menos mantenível, menos confiável e mais difícil de entender para os outros.
Por isso, tudo deve ter um tipo. Assim, se você criar um novo objeto com um primeiro nome e um sobrenome, deve criar uma interface ou tipo que contenha um primeiro nome e um sobrenome que defina a forma do objeto que você está manipulando.

View File

@@ -0,0 +1,39 @@
---
title: Objetos personalizados
---
Os objetos são estruturas que permitem armazenar dados (registros, atributos e valores) específicos de uma organização. O Twenty vem com objetos padrão e personalizados.
Objetos padronizados são objetos embutidos com um conjunto de atributos disponíveis para todos os usuários. Exemplos de objetos padronizados na Twenty incluem Empresa e Pessoa. Os objetos padrão têm campos padrão que também estão disponíveis para todos os usuários do Twenty, como Company.displayName.
Objetos personalizados são objetos que você pode criar para armazenar informações exclusivas de sua organização. Eles não são embutidos; membros do seu espaço de trabalho podem criar e personalizar objetos personalizados para armazenar informações que os objetos padrão não são adequados.
## Esquema de alto nível
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Esquema de alto nível" />
</div>
<br />
## Como Funciona
Objetos personalizados vêm de tabelas de metadados que determinam a forma, nome e tipo dos objetos. Todas essas informações estão presentes no banco de dados do esquema de metadados, consistindo em tabelas:
* **DataSource**: Detalha onde os dados estão presentes.
* **Object**: Descreve o objeto e conecta-se a um DataSource.
* **Field**: Delimita os campos de um objeto e conecta-se ao objeto.
Para adicionar um objeto personalizado, o workspaceMember consultará a API /metadata. Isso atualiza os metadados de acordo e calcula um esquema GraphQL baseado nos metadados, armazenando-o em um cache GQL para uso posterior.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/add-custom-objects.jpeg" alt="Consultar a API /metadata para adicionar objetos personalizados" />
</div>
<br />
Para buscar dados, o processo envolve fazer consultas através do endpoint /graphql e passá-las pelo Query Resolver.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/server/custom-object-schema.png" alt="Consultar o endpoint /graphql para buscar dados" />
</div>

View File

@@ -0,0 +1,46 @@
---
title: Flags de recurso
---
As flags de recurso são usadas para ocultar recursos experimentais. Para a Twenty, eles são definidos no nível do espaço de trabalho e não no nível do usuário.
## Adicionando uma nova flag de recurso
Em `FeatureFlagKey.ts` adicione a flag de recurso:
```ts
type FeatureFlagKey =
| 'IS_FEATURENAME_ENABLED'
| ...;
```
Também adicione-o ao enum em `feature-flag.entity.ts`:
```ts
enum FeatureFlagKeys {
IsFeatureNameEnabled = 'IS_FEATURENAME_ENABLED',
...
}
```
Para aplicar uma flag de recurso em um recurso de **back-end**, use:
```ts
@Gate({
featureFlag: 'IS_FEATURENAME_ENABLED',
})
```
Para aplicar uma flag de recurso em um recurso de **front-end**, use:
```ts
const isFeatureNameEnabled = useIsFeatureEnabled('IS_FEATURENAME_ENABLED');
```
## Configurar flags de recurso para a implantação
Altere o registro correspondente na Tabela `core.featureFlag`:
| iD | chave | workspaceId | valor |
| --------- | ------------------------ | ----------- | ------------ |
| Aleatório | `IS_FEATURENAME_ENABLED` | WorkspaceID | `verdadeiro` |

View File

@@ -0,0 +1,125 @@
---
title: Arquitetura de Pastas
info: Um olhar detalhado sobre a arquitetura de pastas do nosso servidor
---
A estrutura do diretório do backend é a seguinte:
```
server
└───ability
└───constants
└───core
└───database
└───decorators
└───filters
└───guards
└───health
└───integrations
└───metadata
└───workspace
└───utils
```
## Habilidade
Define permissões e inclui manipuladores para cada entidade.
## Decoradores
Define decoradores personalizados no NestJS para funcionalidade adicional.
Veja [decoradores personalizados](https://docs.nestjs.com/custom-decorators) para mais detalhes.
## Filtros
Inclui filtros de exceção para lidar com exceções que podem ocorrer nos endpoints do GraphQL.
## Guards
Veja [guardas](https://docs.nestjs.com/guards) para mais detalhes.
## Integridade
Inclui uma API REST publicamente disponível (healthz) que retorna um JSON para confirmar se o banco de dados está funcionando como esperado.
## Metadados
Define objetos personalizados e disponibiliza uma API GraphQL (graphql/metadata).
## Espaço de trabalho
Gera e serve um esquema GraphQL personalizado com base nos metadados.
### Estrutura do Diretório de Trabalho
```
workspace
└───workspace-schema-builder
└───factories
└───graphql-types
└───database
└───interfaces
└───object-definitions
└───services
└───storage
└───utils
└───workspace-resolver-builder
└───factories
└───interfaces
└───workspace-query-builder
└───factories
└───interfaces
└───workspace-query-runner
└───interfaces
└───utils
└───workspace-datasource
└───workspace-manager
└───workspace-migration-runner
└───utils
└───workspace.module.ts
└───workspace.factory.spec.ts
└───workspace.factory.ts
```
A raiz do diretório workspace inclui o `workspace.factory.ts`, um arquivo que contém a função `createGraphQLSchema`. Esta função gera um esquema específico para o espaço de trabalho usando os metadados para adaptar um esquema para espaços de trabalho individuais. Ao separar a construção do esquema e do resolvedor, usamos a função `makeExecutableSchema`, que combina esses elementos discretos.
Essa estratégia não se trata apenas de organização, mas também ajuda na otimização, como no armazenamento em cache das definições de tipo geradas para melhorar o desempenho e a escalabilidade.
### Construtor de Esquema do Workspace
Gera o esquema GraphQL, e inclui:
#### Fábricas:
Construtores especializados para gerar construções relacionadas ao GraphQL.
* O type.factory traduz os metadados dos campos em tipos GraphQL usando o `TypeMapperService`.
* A type-definition.factory cria objetos de entrada ou saída do GraphQL derivados de `objectMetadata`.
#### Tipos GraphQL
Inclui enumerações, entradas, objetos e escalares, e serve como blocos de construção para a construção do esquema.
#### Interfaces e Definições de Objetos
Contém os planos para entidades GraphQL, e inclui tipos predefinidos e personalizados como `MONEY` ou `URL`.
#### Serviços
Contém o serviço responsável por associar o FieldMetadataType com seu scalar ou modificadores de consulta GraphQL apropriados.
#### Armazenamento
Inclui a classe `TypeDefinitionsStorage` que contém definições de tipos reutilizáveis, evitando a duplicação de tipos GraphQL.
### Construtor de Resolvedor de Workspace
Cria funções resolvedoras para consultar e modificar o esquema GraphQL.
Cada fábrica neste diretório é responsável por produzir um tipo distinto de resolvedor, como a `FindManyResolverFactory`, projetada para aplicação adaptável em várias tabelas.
### Executor de Consultas do Workspace
Executa as consultas geradas no banco de dados e analisa o resultado.

View File

@@ -0,0 +1,41 @@
---
title: Fila de Mensagens
---
Filas facilitam operações assíncronas a serem realizadas. Elas podem ser usadas para realizar tarefas em segundo plano, como enviar um email de boas-vindas ao se registrar.
Cada caso de uso terá sua própria classe de fila estendida de `MessageQueueServiceBase`.
Atualmente, só damos suporte ao `bull-mq`[bull-mq](https://bullmq.io/) como o driver de fila.
## Passos para criar e usar uma nova fila
1. Adicione um nome de fila para sua nova fila no enum `MESSAGE_QUEUES`.
2. Forneça a implementação da fábrica da fila com o nome da fila como o token de dependência.
3. Injete a fila que você criou no módulo/serviço necessário com o nome da fila como o token de dependência.
4. Adicione uma classe de trabalhador com injeção baseada em token, assim como o produtor.
### Exemplo de uso
```ts
class Resolver {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {}
async onSomeAction() {
//business logic
await this.queue.add(someData);
}
}
//async worker
class CustomWorker {
constructor(@Inject(MESSAGE_QUEUES.custom) private queue: MessageQueueService) {
this.initWorker();
}
async initWorker() {
await this.queue.work(async ({ id, data }) => {
//worker logic
});
}
}
```

View File

@@ -0,0 +1,87 @@
---
title: Comandos de Backend
icon: terminal
---
## Comandos Úteis
Esses comandos devem ser executados a partir da pasta packages/twenty-server.
Em qualquer outra pasta você pode executar o `npx nx {command} twenty-server` (ou o `npx nx run twenty-server:{command}`).
### Configuração inicial
```
npx nx database:reset twenty-server # setup the database with dev seeds
```
### Iniciando o servidor
```
npx nx run twenty-server:start
```
### Lint
```
npx nx run twenty-server:lint # pass --fix to fix lint errors
```
### Teste
```
npx nx run twenty-server:test:unit # run unit tests
npx nx run twenty-server:test:integration # run integration tests
```
Nota: você pode executar `npx nx run twenty-server:test:integration:with-db-reset` caso precise redefinir o banco de dados antes de executar os testes de integração.
### Redefinindo o banco de dados
Se você deseja redefinir e semear a base de dados, pode executar o seguinte comando:
```bash
npx nx run twenty-server:database:reset
```
### Migrações
#### Para objetos nos esquemas Core/Metadata (TypeORM)
```bash
npx nx run twenty-server:database:migrate:generate
```
## Pilha de Tecnologias
O Twenty usa principalmente NestJS para o backend.
Prisma foi o primeiro ORM que usamos. Mas para permitir que os usuários criem campos e objetos personalizados, um nível mais baixo fazia mais sentido, pois precisamos ter controle detalhado. O projeto agora usa TypeORM.
Veja como a pilha de tecnologia se parece agora.
**Core**
* [NestJS](https://nestjs.com/)
* [TypeORM](https://typeorm.io/)
* [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server)
**Banco de Dados**
* [Postgres](https://www.postgresql.org/)
**Integrações de terceiros**
* [Sentry](https://sentry.io/welcome/) para rastreamento de bugs
**Testes**
* [Jest](https://jestjs.io/)
**Ferramentas**
* [Yarn](https://yarnpkg.com/)
* [Oxlint](https://oxc.rs/docs/guide/usage/linter.html)
**Desenvolvimento**
* [AWS EKS](https://aws.amazon.com/eks/)

View File

@@ -0,0 +1,85 @@
---
title: Aplicativo Zapier
---
Sincronize o Twenty com mais de 3000 aplicativos usando [Zapier](https://zapier.com/) sem esforço. Automatize tarefas, aumente a produtividade e potencialize seus relacionamentos com clientes!
## Sobre o Zapier
O Zapier é uma ferramenta que permite automações de fluxos de trabalho conectando os aplicativos que sua equipe usa todos os dias. O conceito fundamental do Zapier são automações de fluxos de trabalho, chamadas Zaps, que incluem disparadores e ações.
Você pode aprender mais sobre como o Zapier funciona [aqui](https://zapier.com/how-it-works).
## Configuração
### Etapa 1: Instalar pacotes do Zapier
```bash
cd packages/twenty-zapier
yarn
```
### Etapa 2: Faça login com o CLI
Use suas credenciais do Zapier para fazer login usando o CLI:
```bash
zapier login
```
### Etapa 3: Defina as variáveis de ambiente
Na pasta `packages/twenty-zapier`, execute:
```bash
cp .env.example .env
```
Execute o aplicativo localmente, vá para [http://localhost:3000/settings/api-webhooks](http://localhost:3000/settings/api-webhooks) e gere uma chave API.
Substitua o valor **YOUR_API_KEY** no arquivo `.env` pela chave API que você acabou de gerar.
## Desenvolvimento
<Warning>
Certifique-se de executar `yarn build` antes de qualquer comando `zapier`.
</Warning>
### Teste
```bash
yarn test
```
### Lint
```bash
yarn format
```
### Assista e compile enquanto edita o código
```bash
yarn watch
```
### Valide seu aplicativo Zapier
```bash
yarn validate
```
### Implante seu aplicativo Zapier
```bash
yarn deploy
```
### Liste todos os comandos CLI do Zapier
```bash
zapier
```

View File

@@ -0,0 +1,79 @@
---
title: Bugs, solicitações e Pull Requests
icon: bug
info: Reporte issues, solicite funcionalidades e contribua com código
---
## Relatar Erros
Para relatar um erro, por favor [crie um problema no GitHub](https://github.com/twentyhq/twenty/issues/new).
Você também pode pedir ajuda no [Discord](https://discord.gg/cx5n4Jzs57).
## Solicitações de Funcionalidades
Se você não tem certeza se é um erro e sente que está mais próximo de uma solicitação de funcionalidade, então você provavelmente deve [abrir uma discussão em vez disso](https://github.com/twentyhq/twenty/discussions/new).
## Envie um Pull Request
Contribuir com código para o Twenty começa com um pull request (PR).
### Antes de Começar
1. Confira as [issues existentes](https://github.com/twentyhq/twenty/issues) para trabalhos relacionados
2. Para novas funcionalidades, abra primeiro uma issue para discutir
3. Consulte nosso [Código de Conduta](https://github.com/twentyhq/twenty/blob/main/.github/CODE_OF_CONDUCT.md)
### Fork e clone
1. Crie um fork do repositório no GitHub
2. Clone seu fork:
```bash
git clone https://github.com/YOUR_USERNAME/twenty.git
cd twenty
```
3. Adicione o remoto upstream:
```bash
git remote add upstream https://github.com/twentyhq/twenty.git
```
### Crie uma branch
```bash
git checkout -b feature/your-feature-name
```
Use nomes de branch descritivos:
* `feature/add-export-button`
* `fix/login-redirect-issue`
* `docs/update-api-guide`
### Faça suas alterações
1. Escreva código limpo e bem documentado
2. Siga o estilo de código existente
3. Adicione testes para a nova funcionalidade
4. Atualize a documentação se necessário
### Envie seu PR
1. Faça push da sua branch:
```bash
git push origin feature/your-feature-name
```
2. Abra um PR no GitHub
3. Preencha o modelo do PR
4. Vincule as issues relacionadas
### Checklist de PR
* [ ] O código segue as diretrizes de estilo do projeto
* [ ] Os testes passam localmente
* [ ] A documentação está atualizada
* [ ] A descrição do PR explica as alterações

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

View File

@@ -0,0 +1,318 @@
---
title: Configuração Local
icon: laptop-code
description: O guia para contribuidores (ou desenvolvedores curiosos) que desejam executar o Twenty localmente.
---
## Pré-requisitos
<Tabs>
<Tab title="Linux e macOS">
Antes de instalar e usar o Twenty, certifique-se de instalar o seguinte em seu computador:
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Node v24.5.0](https://nodejs.org/en/download)
* [yarn v4](https://yarnpkg.com/getting-started/install)
* [nvm](https://github.com/nvm-sh/nvm/blob/master/README.md)
<Warning>
`npm` não funcionará, você deve usar `yarn` em vez disso. O Yarn agora vem com o Node.js, então você não precisa instalá-lo separadamente.
Você só precisa executar `corepack enable` para habilitar o Yarn, se ainda não o fez.
</Warning>
</Tab>
<Tab title="Windows (WSL)">
1. Instale o WSL
Abra o PowerShell como Administrador e execute:
```powershell
wsl --install
```
Você deve agora ver um aviso para reiniciar o computador. Caso contrário, reinicie-o manualmente.
Ao reiniciar, uma janela do PowerShell será aberta e instalará o Ubuntu. Isso pode levar algum tempo.
Você verá uma solicitação para criar um nome de usuário e senha para sua instalação do Ubuntu.
2. Instalar e configurar o git
```bash
sudo apt-get install git
git config --global user.name "Your Name"
git config --global user.email "youremail@domain.com"
```
3. Instale nvm, node.js e yarn
<Warning>
Use `nvm` para instalar a versão correta do `node`. O `.nvmrc` garante que todos os contribuidores usem a mesma versão.
</Warning>
```bash
sudo apt-get install curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
```
Feche e reabra o seu terminal para usar o nvm. Em seguida, execute os seguintes comandos.
```bash
nvm install # installs recommended node version
nvm use # use recommended node version
corepack enable
```
</Tab>
</Tabs>
---
## Passo 1: Clonar com Git
No seu terminal, execute o seguinte comando.
<Tabs>
<Tab title="SSH (Recomendado)">
Se ainda não configurou as chaves SSH, você pode aprender como fazê-lo [aqui](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh).
```bash
git clone git@github.com:twentyhq/twenty.git
```
</Tab>
<Tab title="HTTPS">
```bash
git clone https://github.com/twentyhq/twenty.git
```
</Tab>
</Tabs>
## Passo 2: Posicione-se na raiz
```bash
cd twenty
```
Você deve executar todos os comandos nas etapas seguintes a partir da raiz do projeto.
## Passo 3: Configurar um Banco de Dados PostgreSQL
<Tabs>
<Tab title="Linux">
**Opção 1 (preferencial):** Para provisionar seu banco de dados localmente:
Use o seguinte link para instalar o PostgreSQL na sua máquina Linux: [Instalação do PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Nota: Pode ser necessário adicionar `sudo -u postgres` ao comando antes de `psql` para evitar erros de permissão.
**Opção 2:** Se você tem o docker instalado:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Mac OS">
**Opção 1 (preferencial):** Para prover seu banco de dados localmente com `brew`:
```bash
brew install postgresql@16
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
brew services start postgresql@16
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Você pode verificar se o servidor PostgreSQL está em execução, executando:
```bash
brew services list
```
O instalador pode não criar o usuário `postgres` por padrão ao instalar
via Homebrew no macOS. Em vez disso, ele cria uma função PostgreSQL que corresponde ao seu nome de usuário do macOS
(por exemplo, "john").
Para verificar e criar o usuário `postgres`, se necessário, siga estas etapas:
```bash
# Connect to PostgreSQL
psql postgres
or
psql -U $(whoami) -d postgres
```
Uma vez no prompt do psql (postgres=#), execute:
```bash
# List existing PostgreSQL roles
\du
```
Você verá uma saída semelhante a:
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
john | Superuser | {}
```
Se você não vir um papel `postgres` listado, prossiga para a próxima etapa.
Crie o papel `postgres` manualmente:
```bash
CREATE ROLE postgres WITH SUPERUSER LOGIN;
```
Isso cria um papel de superusuário chamado `postgres` com acesso de login.
```bash
Role name | Attributes | Member of
-----------+-------------+-----------
postgres | Superuser | {}
john | Superuser | {}
```
**Opção 2:** Se você tem o docker instalado:
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
Todos os passos a seguir devem ser executados no terminal WSL (dentro da sua máquina virtual)
**Opção 1:** Para provisionar seu PostgreSQL localmente:
Use o seguinte link para instalar o PostgreSQL na sua máquina virtual Linux: [Instalação do PostgreSQL](https://www.postgresql.org/download/linux/)
```bash
psql postgres -c "CREATE DATABASE \"default\";" -c "CREATE DATABASE test;"
```
Nota: Pode ser necessário adicionar `sudo -u postgres` ao comando antes de `psql` para evitar erros de permissão.
**Opção 2:** Se você tem o docker instalado:
Executar o Docker no WSL adiciona uma camada extra de complexidade.
Use esta opção apenas se estiver confortável com as etapas adicionais envolvidas, incluindo a ativação do [Docker Desktop WSL2](https://docs.docker.com/desktop/wsl).
```bash
make -C packages/twenty-docker postgres-on-docker
```
</Tab>
</Tabs>
Agora você pode acessar o banco de dados em `localhost:5432`.
Se você usou a opção do Docker acima, as credenciais padrão são usuário `postgres` e senha `postgres`. Para instalações nativas do PostgreSQL, use as credenciais e os papéis configurados na sua máquina.
## Passo 4: Configurar um Banco de Dados Redis (cache)
O Twenty requer um cache Redis para oferecer o melhor desempenho.
<Tabs>
<Tab title="Linux">
**Opção 1:** Para prover seu Redis localmente:
Use o seguinte link para instalar o Redis na sua máquina Linux: [Instalação do Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Opção 2:** Se você tem o docker instalado:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Mac OS">
**Opção 1 (preferencial):** Para prover seu Redis localmente com `brew`:
```bash
brew install redis
```
Inicie o servidor Redis:
```bash
brew services start redis
```
**Opção 2:** Se você tem o docker instalado:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
<Tab title="Windows (WSL)">
**Opção 1:** Para prover seu Redis localmente:
Use o seguinte link para instalar o Redis na sua máquina virtual Linux: [Instalação do Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-linux/)
**Opção 2:** Se você tem o docker instalado:
```bash
make -C packages/twenty-docker redis-on-docker
```
</Tab>
</Tabs>
Se você precisar de uma GUI de cliente, recomendamos o [Redis Insight](https://redis.io/insight/) (versão gratuita disponível).
## Passo 5: Configurar variáveis de ambiente
Use variáveis de ambiente ou arquivos `.env` para configurar seu projeto. Mais informações [aqui](/l/pt/developers/self-host/capabilities/setup).
Copie os arquivos `.env.example` em `/front` e `/server`:
```bash
cp ./packages/twenty-front/.env.example ./packages/twenty-front/.env
cp ./packages/twenty-server/.env.example ./packages/twenty-server/.env
```
<Info>
**Modo Multiworkspace:** Por padrão, o Twenty é executado no modo de workspace único, em que apenas um workspace pode ser criado. Para ativar o suporte a multiworkspace (útil para testar recursos baseados em subdomínio), defina `IS_MULTIWORKSPACE_ENABLED=true` no arquivo `.env` do seu servidor. Veja [Modo Multiworkspace](/l/pt/developers/self-host/capabilities/setup#multi-workspace-mode) para obter detalhes.
</Info>
## Passo 6: Instalando dependências
Para compilar o servidor Twenty e popular seu banco de dados com alguns dados, execute o seguinte comando:
```bash
yarn
```
Note que `npm` ou `pnpm` não funcionarão
## Passo 7: Executando o projeto
<Tabs>
<Tab title="Linux">
Dependendo da sua distribuição Linux, o servidor Redis pode ser iniciado automaticamente.
Se não, verifique o [guia de instalação do Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) para sua distro.
</Tab>
<Tab title="Mac OS">
Redis já deve estar em execução. Se não, execute:
```bash
brew services start redis
```
</Tab>
<Tab title="Windows (WSL)">
Dependendo da sua distribuição Linux, o servidor Redis pode ser iniciado automaticamente.
Se não, verifique o [guia de instalação do Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/) para sua distro.
</Tab>
</Tabs>
Configure seu banco de dados com o seguinte comando:
```bash
npx nx database:reset twenty-server
```
Inicie o servidor, o worker e os serviços de frontend:
```bash
npx nx start twenty-server
npx nx worker twenty-server
npx nx start twenty-front
```
Alternativamente, você pode iniciar todos os serviços de uma vez:
```bash
npx nx start
```
## Passo 8: Use Twenty
**Frontend**
O frontend do Twenty estará em execução em [http://localhost:3001](http://localhost:3001).
Você pode fazer login usando a conta demo padrão: `tim@apple.dev` (senha: `tim@apple.dev`)
**Backend**
* O servidor do Twenty estará ativo em [http://localhost:3000](http://localhost:3000)
* A API GraphQL pode ser acessada em [http://localhost:3000/graphql](http://localhost:3000/graphql)
* A API REST pode ser acessada em [http://localhost:3000/rest](http://localhost:3000/rest)
## Resolução de Problemas
Se encontrar algum problema, verifique [Resolução de Problemas](/l/pt/developers/self-host/capabilities/troubleshooting) para soluções.