Stack
- React 19.1 + TypeScript 5.9
- Vite (não Webpack/Create React App)
- Node >= 22.3 (mínimo obrigatório)
Estilo e Componentes
Seção intitulada “Estilo e Componentes”- TailwindCSS 4 - todos os estilos devem usar Tailwind
- Radix UI - componentes base headless
- shadcn/ui - componentes pré-construídos
- Lucide React - ícones (não use outros pacotes de ícones)
- Framer Motion - animações
State e Data Fetching
Seção intitulada “State e Data Fetching”- Zustand - state management global
- SEMPRE use
devtoolsmiddleware - SEMPRE use
shallowao selecionar múltiplos valores - SEMPRE versione a store com
version
- SEMPRE use
- TanStack Query (@tanstack/react-query) - data fetching e cache
- React Hook Form + Zod - formulários e validação
Roteamento e Navegação
Seção intitulada “Roteamento e Navegação”- React Router 7 - roteamento
Validações
Seção intitulada “Validações”- Zod 4 - validação e parsing de dados
Utilidades
Seção intitulada “Utilidades”- class-variance-authority (cva) - variantes de componentes
- clsx + tailwind-merge - merge de classes CSS
- date-fns - manipulação de datas (não use moment.js ou dayjs)
Testing
Seção intitulada “Testing”- Vitest (não Jest)
- Testing Library (@testing-library/react)
- Playwright (Testes e2e)
- MSW - mock de API
Code Quality
Seção intitulada “Code Quality”- Biome - linting e formatting (NÃO use ESLint ou Prettier)
Estrutura do Monorepo
Seção intitulada “Estrutura do Monorepo”gaio-frontend/├── packages/│ ├── common/ # Código compartilhado, utils, types, hooks│ ├── components/ # Biblioteca de componentes UI base│ ├── console/ # Aplicação principal (console)│ └── site/ # Site institucional├── local.env # Variáveis de ambiente local (NÃO commitado)├── deploy.env # Config de deploy (NÃO commitado)└── package.json # Root package.json (workspaces)Workspaces npm
Seção intitulada “Workspaces npm”O projeto usa npm workspaces para gerenciar o monorepo. Cada package tem seu próprio package.json.
Importações entre pacotes
Seção intitulada “Importações entre pacotes”- Use
@gaio/commonpara importar do common (hooks, utils, types) - Use
@gaio/componentspara importar componentes base - Nunca use caminhos relativos entre pacotes (
../../packages/common) - Imports locais dentro do mesmo package podem usar caminhos relativos ou aliases
@/
Dependências entre packages
Seção intitulada “Dependências entre packages”console (aplicação principal) ├── depende de → @gaio/common ├── depende de → @gaio/components └── usa → radix-ui, react-query, zustand, etc.
components (biblioteca UI base) ├── depende de → @gaio/common └── usa → radix-ui, lucide-react, framer-motion
common (base compartilhada) └── hooks, utils, types, themeEstrutura do Console (Principal)
Seção intitulada “Estrutura do Console (Principal)”Organização de pastas em packages/console/src/
Seção intitulada “Organização de pastas em packages/console/src/”src/├── components/ # Componentes reutilizáveis por categoria│ ├── actions/ # Componentes de ações (delete, edit, settings)│ ├── animations/ # Componentes animados│ ├── buttons/ # Componentes de botões│ ├── cards/ # Componentes de cards│ ├── chart/ # Componentes de gráficos│ ├── contact/ # Componentes relacionados a contatos│ ├── custom-fields/ # Componentes de campos customizados│ ├── dates/ # Componentes relacionados a datas│ ├── dynamic-filter/ # Sistema de filtros dinâmicos│ ├── fields/ # Componentes de campos de formulário│ ├── header/ # Componentes do header│ ├── modal/ # Componentes de modais│ ├── screens/ # Componentes de layouts e sidebar│ ├── sentiment/ # Componentes de análise de sentimento│ ├── tags/ # Componentes de tags│ ├── ui/ # Componentes UI genéricos│ └── utils/ # Componentes utilitários├── features/ # Features complexas com domínio próprio│ ├── chat/ # Feature de chat completa│ │ ├── components/│ │ ├── message/│ │ ├── store/ # Store Zustand específica do chat│ │ ├── types/│ │ └── utils/│ └── contacts/ # Feature de contatos completa│ └── editor/├── screens/ # Telas/páginas da aplicação│ ├── admin/ # Telas administrativas│ ├── user/ # Telas de usuário│ └── error/ # Telas de erro├── hooks/ # Custom hooks globais├── services/ # Serviços de API├── utils/ # Utilitários gerais├── config/ # Configurações├── constants/ # Constantes└── router/ # Configuração de rotasNomenclatura de arquivos
Seção intitulada “Nomenclatura de arquivos”Baseado na estrutura real do projeto
-
Componentes em
src/components/:- Pasta:
kebab-case/ - Arquivo:
component.tsx - Exemplo:
src/components/alert/component.tsx - Função exportada:
PascalCase(ex:export function Alert())
- Pasta:
-
Componentes em
src/features/:- Pasta:
kebab-case/ - Arquivos específicos:
kebab-case.tsx - Exemplo:
src/features/chat/chat-header.tsx - Função exportada:
PascalCase(ex:export function ChatHeader())
- Pasta:
-
Outros arquivos:
- Hooks:
use-kebab-case.ts(ex:use-auth.ts) - Services:
kebab-case.ts(ex:user.tsemservices/) - Utils:
kebab-case.ts(ex:format-date.ts) - Types:
kebab-case.tsoukebab-case.types.ts - Stores:
kebab-case.ts(ex:chat.tsemfeatures/chat/store/)
- Hooks:
Padrões de Componentes React
Seção intitulada “Padrões de Componentes React”Template para Componente Base (src/components/)
Seção intitulada “Template para Componente Base (src/components/)”import { type ComponentProps } from "react";import { cva, type VariantProps } from "class-variance-authority";import { cn } from "@gaio/components";
const buttonVariants = cva("base-classes-here", { variants: { variant: { default: "variant-classes", outline: "variant-classes", }, size: { default: "size-classes", sm: "size-classes", }, }, defaultVariants: { variant: "default", size: "default", },});
export interface ButtonProps extends ComponentProps<"button">, VariantProps<typeof buttonVariants> { // Props específicas aqui}
export function Button({ className, variant, size, ...props }: ButtonProps) { return ( <button className={buttonVariants({ variant, size, className })} {...props} /> );}Template para Feature Component (src/features/)
Seção intitulada “Template para Feature Component (src/features/)”import { cn } from "@gaio/components";import type { ChatHeaderProps } from "./types/chat";
export function ChatHeader({ className, ...props }: ChatHeaderProps) { return ( <div className={cn("chat-header-classes", className)} {...props}> {/* Conteúdo */} </div> );}Regras de Componentes
Seção intitulada “Regras de Componentes”-
Estrutura de pastas:
- Componentes base:
src/components/[nome]/component.tsx - Features:
src/features/[feature]/[nome].tsx
- Componentes base:
-
Use TypeScript - tipagem completa obrigatória
-
Props interface - sempre exporte a interface de props
- SEMPRE estenda
ComponentProps<'elemento'>para herdar props nativas (className, style, etc.) - Combine com
VariantProps<typeof variants>quando usar CVA
- SEMPRE estenda
-
cn() - use
cn()do@gaio/componentspara merge de classes -
cva - SEMPRE use CVA para criar variações de componentes
- Nunca crie variações manualmente com objetos ou condicionais
- Exemplos de variações: size, variant, color, state
-
TailwindCSS only - nunca use CSS modules
Padrões de Hooks
Seção intitulada “Padrões de Hooks”Custom Hook Template
Seção intitulada “Custom Hook Template”import { useState, useEffect } from "react";
export function useCustomHook(param: string) { const [state, setState] = useState<Type>(initialValue);
useEffect(() => { // Effect logic }, [param]);
return { state, setState };}Hooks do @gaio/common
Seção intitulada “Hooks do @gaio/common”O package @gaio/common fornece hooks reutilizáveis:
import { useBeforeUnload, useBodyScrollLock, useDebounce, useBreakpoint, useIntersectionObserver, useScroll, // ...} from "@gaio/common";Data Fetching com React Query
Seção intitulada “Data Fetching com React Query”Estrutura de Service
Seção intitulada “Estrutura de Service”import { api } from "@/lib/api";import type { User } from "@/types/user";
export const userService = { getUser: async (id: string): Promise<User> => { const response = await api.get(`/users/${id}`); return response.data; },
updateUser: async (id: string, data: Partial<User>): Promise<User> => { const response = await api.patch(`/users/${id}`, data); return response.data; },};Hook com React Query
Seção intitulada “Hook com React Query”import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";import { userService } from "@/services/user";
export function useUser(id: string) { return useQuery({ queryKey: ["user", id], queryFn: () => userService.getUser(id), });}
export function useUpdateUser() { const queryClient = useQueryClient();
return useMutation({ mutationFn: userService.updateUser, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ["user", data.id] }); }, });}State Management com Zustand
Seção intitulada “State Management com Zustand”REGRAS OBRIGATÓRIAS
Seção intitulada “REGRAS OBRIGATÓRIAS”SEMPRE siga este padrão para stores Zustand:
- devtools middleware - para debug no Redux DevTools
- version - versionar a store (incrementar ao mudar estrutura)
- shallow - usar ao consumir múltiplos valores
Template de Store
Seção intitulada “Template de Store”import { create } from "zustand";import { devtools } from "zustand/middleware";import type { Chat } from "../types/chat";
interface ChatState { selectedChat: Chat | null; chats: Chat[]; setSelectedChat: (chat: Chat | null) => void; setChats: (chats: Chat[]) => void;}
export const useChatStore = create<ChatState>()( devtools( (set) => ({ selectedChat: null, chats: [], setSelectedChat: (selectedChat) => set({ selectedChat }), setChats: (chats) => set({ chats }), }), { name: "chat-store", // Nome para Redux DevTools version: 1, // OBRIGATÓRIO: versionar }, ),);Usando Store com Shallow
Seção intitulada “Usando Store com Shallow”import { useChatStore } from './store/chat';import { shallow } from 'zustand/shallow';
export function ChatList() { // ✅ CORRETO - múltiplos valores COM shallow const { chats, setSelectedChat } = useChatStore( (state) => ({ chats: state.chats, setSelectedChat: state.setSelectedChat }), shallow // ← OBRIGATÓRIO );
// ✅ CORRETO - valor único NÃO precisa shallow const selectedChat = useChatStore((state) => state.selectedChat);
// ❌ ERRADO - múltiplos valores SEM shallow const { chats, setSelectedChat } = useChatStore((state) => ({ chats: state.chats, setSelectedChat: state.setSelectedChat }));
return ( <div> {/* Renderizar chats */} </div> );}Quando incrementar version
Seção intitulada “Quando incrementar version”Incremente version ao mudar estrutura do state:
- ✅ Adicionar campos
- ✅ Remover campos
- ✅ Mudar tipos
- ✅ Alterar objetos aninhados
// Version 1interface ChatState { selectedChat: Chat | null; chats: Chat[];}
// Version 2 - adicionou campointerface ChatState { selectedChat: Chat | null; chats: Chat[]; unreadCount: number; // ← Novo, incrementar para version: 2}Formulários com React Hook Form + Zod
Seção intitulada “Formulários com React Hook Form + Zod”Template de Formulário
Seção intitulada “Template de Formulário”import { useForm } from 'react-hook-form';import { zodResolver } from '@hookform/resolvers/zod';import { z } from 'zod';
const formSchema = z.object({ email: z.string().email('E-mail inválido'), password: z.string().min(8, 'Senha deve ter no mínimo 8 caracteres'),});
type FormData = z.infer<typeof formSchema>;
export function LoginForm() { const { register, handleSubmit, formState: { errors }, } = useForm<FormData>({ resolver: zodResolver(formSchema), });
const onSubmit = (data: FormData) => { // Handle submission };
return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('email')} /> {errors.email && <span>{errors.email.message}</span>} {/* ... */} </form> );}Template de Teste
Seção intitulada “Template de Teste”import { render, screen } from '@testing-library/react';import { describe, it, expect } from 'vitest';import { Button } from './component';
describe('Button', () => { it('renders correctly', () => { render(<Button>Click me</Button>); expect(screen.getByText('Click me')).toBeInTheDocument(); });});Regras de Testes
Seção intitulada “Regras de Testes”- Use Vitest (não Jest)
- Arquivo:
[nome].test.tsxao lado do componente - Use Testing Library queries semânticas
TypeScript Avançado - Melhores Práticas
Seção intitulada “TypeScript Avançado - Melhores Práticas”Regras Fundamentais
Seção intitulada “Regras Fundamentais”- Sempre use tipagem explícita - evite
anya todo custo - Prefira tipos estreitos - use union types ao invés de genéricos demais
- Use type guards - valide tipos em runtime
- Imutabilidade - prefira
readonlyeconst - Composição sobre herança - use tipos compostos
Tipagem de Funções
Seção intitulada “Tipagem de Funções”Regras de Inferência
Seção intitulada “Regras de Inferência”- Funções exportadas/públicas: SEMPRE tipagem explícita de retorno
- Funções internas/privadas simples: Inferência OK se óbvio
- Funções genéricas/complexas: SEMPRE tipagem explícita
- Parâmetros: SEMPRE tipados (nunca inferir)
✅ Bom - Tipagem apropriada
Seção intitulada “✅ Bom - Tipagem apropriada”// ✅ Função exportada - tipo de retorno explícitoexport function calculateTotal(items: CartItem[], discount: number): number { return items.reduce((sum, item) => sum + item.price, 0) * (1 - discount);}
// ✅ Função interna simples - inferência OKfunction sumPrices(items: CartItem[]) { return items.reduce((sum, item) => sum + item.price, 0); // inferido como number}
// ✅ Arrow functions exportadas - tipo explícitoexport const formatUser = (user: User): FormattedUser => ({ id: user.id, fullName: `${user.firstName} ${user.lastName}`, email: user.email.toLowerCase(),});
// ✅ Arrow function interna - inferência OK se simplesconst normalizeEmail = (email: string) => email.toLowerCase().trim();
// ✅ Async exportada - tipo explícitoexport async function fetchUser(id: string): Promise<User> { const response = await api.get(`/users/${id}`); return response.data;}
// ✅ Callback/handler interno - inferência OKconst handleClick = (e: React.MouseEvent<HTMLButtonElement>) => { Logger.debug(e.currentTarget.value); // inferido como void};
// ✅ Função genérica - SEMPRE explícitafunction mapArray<T, U>(items: T[], mapper: (item: T) => U): U[] { return items.map(mapper);}❌ Ruim - Tipagem inadequada
Seção intitulada “❌ Ruim - Tipagem inadequada”// ❌ NUNCA use anyfunction processData(data: any) { return data.map((item: any) => item.value);}
// ❌ Parâmetros sem tipofunction calculate(a, b) { return a + b;}
// ❌ Função exportada sem tipo de retornoexport function getUser(id: string) { return api.get(`/users/${id}`); // Que tipo retorna?}
// ❌ Função complexa sem tipo de retornofunction complexOperation(data: Data[]) { if (data.length === 0) return null; if (data.length === 1) return data[0]; return data; // null | Data | Data[] - ambíguo!}Quando usar tipagem explícita de retorno
Seção intitulada “Quando usar tipagem explícita de retorno”SEMPRE tipagem explícita:
- ✅ Funções exportadas (API pública)
- ✅ Funções assíncronas
- ✅ Funções com múltiplos return types
- ✅ Funções genéricas
- ✅ Callbacks complexos
- ✅ Métodos de classe públicos
Inferência OK:
- ✅ Funções auxiliares internas simples
- ✅ Arrow functions em componentes (onClick, onChange)
- ✅ Transformações simples de dados
- ✅ Guards de tipo óbvios
Error Handling - Padrão Obrigatório
Seção intitulada “Error Handling - Padrão Obrigatório”Regras de Error Handling
Seção intitulada “Regras de Error Handling”- SEMPRE use try-catch em operações assíncronas
- SEMPRE tipagem de erro específica
- NUNCA deixe catch vazio
- SEMPRE logar erros apropriadamente
- Retorne tipos explícitos de sucesso/erro
✅ Pattern: Result Type (Recomendado)
Seção intitulada “✅ Pattern: Result Type (Recomendado)”// Type helper para resultado de operaçõestype Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E };
// Uso em serviçosasync function fetchUserSafe(id: string): Promise<Result<User>> { try { const response = await api.get(`/users/${id}`); return { success: true, data: response.data }; } catch (error) { if (error instanceof ApiError) { return { success: false, error: new Error(`Failed to fetch user: ${error.message}`), }; } return { success: false, error: new Error("Unknown error occurred"), }; }}
// Uso no componenteasync function loadUser(id: string) { const result = await fetchUserSafe(id);
if (result.success) { setUser(result.data); } else { showError(result.error.message); }}✅ Pattern: Try-Catch com Tipagem
Seção intitulada “✅ Pattern: Try-Catch com Tipagem”// Sempre tipagem específica de erroclass ApiError extends Error { constructor( message: string, public statusCode: number, public code?: string, ) { super(message); this.name = "ApiError"; }}
// Uso corretoasync function updateUser(id: string, data: UpdateUserData): Promise<User> { try { const response = await api.patch(`/users/${id}`, data); return response.data; } catch (error) { // Type guard para erro conhecido if (error instanceof ApiError) { if (error.statusCode === 404) { throw new Error(`User ${id} not found`); } if (error.statusCode === 403) { throw new Error("Permission denied"); } }
// Erro desconhecido if (error instanceof Error) { throw new Error(`Failed to update user: ${error.message}`); }
// Fallback throw new Error("An unexpected error occurred"); }}✅ Pattern: Custom Error Types
Seção intitulada “✅ Pattern: Custom Error Types”// Defina erros específicos do domínioclass ValidationError extends Error { constructor( public field: string, message: string, ) { super(message); this.name = "ValidationError"; }}
class NotFoundError extends Error { constructor( public resource: string, public id: string, ) { super(`${resource} with id ${id} not found`); this.name = "NotFoundError"; }}
// Uso em funçõesasync function deleteUser(id: string): Promise<void> { try { await api.delete(`/users/${id}`); } catch (error) { if (error instanceof ApiError && error.statusCode === 404) { throw new NotFoundError("User", id); } throw error; }}
// No componentetry { await deleteUser(userId); showSuccess("User deleted");} catch (error) { if (error instanceof NotFoundError) { showError(`User not found`); } else if (error instanceof ValidationError) { showError(`Invalid ${error.field}`); } else { showError("Failed to delete user"); }}❌ Ruim - Catch sem tipagem
Seção intitulada “❌ Ruim - Catch sem tipagem”// NUNCA faça issotry { await api.get("/users");} catch (error) { Logger.error(error); // error é any}
// NUNCA catch vaziotry { await api.post("/users", data);} catch (e) { // Silenciar erro é perigoso}
// NUNCA capture tudo sem especificartry { const result = await complexOperation();} catch { return null; // Perde informação do erro}Type Guards e Narrowing
Seção intitulada “Type Guards e Narrowing”✅ Type Guards Customizados
Seção intitulada “✅ Type Guards Customizados”// Type guard com isfunction isUser(value: unknown): value is User { return ( typeof value === "object" && value !== null && "id" in value && "email" in value && typeof value.id === "string" && typeof value.email === "string" );}
// Usofunction processResponse(data: unknown) { if (isUser(data)) { // TypeScript sabe que data é User aqui Logger.debug(data.email); }}
// Type guard para arraysfunction isUserArray(value: unknown): value is User[] { return Array.isArray(value) && value.every(isUser);}✅ Discriminated Unions
Seção intitulada “✅ Discriminated Unions”// Union com discriminadortype ApiResponse<T> = | { status: 'loading' } | { status: 'error'; error: Error } | { status: 'success'; data: T };
// Uso com exhaustive checkingfunction handleResponse<T>(response: ApiResponse<T>) { switch (response.status) { case 'loading': return <Spinner />; case 'error': return <ErrorMessage error={response.error} />; case 'success': return <DataDisplay data={response.data} />; default: // Exhaustive check - não compila se faltar um case const _exhaustive: never = response; return _exhaustive; }}Utility Types e Types Avançados
Seção intitulada “Utility Types e Types Avançados”✅ Utility Types Nativos
Seção intitulada “✅ Utility Types Nativos”// Partial - todos campos opcionaistype UpdateUser = Partial<User>;
// Required - todos campos obrigatóriostype CompleteUser = Required<User>;
// Pick - selecionar campostype UserPreview = Pick<User, "id" | "name" | "avatar">;
// Omit - excluir campostype UserWithoutPassword = Omit<User, "password">;
// Record - objeto com chaves tipadastype UserMap = Record<string, User>;
// Readonly - imutáveltype ImmutableUser = Readonly<User>;✅ Types Customizados Avançados
Seção intitulada “✅ Types Customizados Avançados”// Extrair tipo de retorno de funçãotype UserServiceReturn = ReturnType<typeof userService.getUser>;
// Extrair tipo de parâmetrostype UserServiceParams = Parameters<typeof userService.getUser>;
// Extrair tipo de Promisetype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;type UserData = UnwrapPromise<ReturnType<typeof fetchUser>>;
// Non-nullabletype NonNullableUser = NonNullable<User | null | undefined>;
// Array element typetype ArrayElement<T> = T extends (infer E)[] ? E : never;type UserFromArray = ArrayElement<User[]>;✅ Conditional Types
Seção intitulada “✅ Conditional Types”// Tipo condicionaltype IsString<T> = T extends string ? true : false;
// Extrair campos de um tipo específicotype StringKeys<T> = { [K in keyof T]: T[K] extends string ? K : never;}[keyof T];
type UserStringFields = StringKeys<User>; // 'name' | 'email' | ...
// Deep Readonlytype DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];};Imutabilidade e Readonly
Seção intitulada “Imutabilidade e Readonly”✅ Prefira Imutabilidade
Seção intitulada “✅ Prefira Imutabilidade”// Use readonly em interfacesinterface User { readonly id: string; readonly email: string; name: string; // Apenas campos que mudam ficam mutáveis}
// Use const assertionsconst CONFIG = { API_URL: "https://api.example.com", TIMEOUT: 5000,} as const;
// Agora CONFIG é deeply readonlytype Config = typeof CONFIG; // { readonly API_URL: "https://api.example.com", ... }
// Arrays imutáveisconst ROLES = ["admin", "user", "guest"] as const;type Role = (typeof ROLES)[number]; // 'admin' | 'user' | 'guest'
// Readonly em parâmetrosfunction processUsers(users: readonly User[]): void { // users.push(...) // Erro! Não pode modificar const newUsers = [...users, newUser]; // Ok! Cria novo array}Patterns de Tipagem em React
Seção intitulada “Patterns de Tipagem em React”✅ Props com Genéricos
Seção intitulada “✅ Props com Genéricos”// Componente genéricointerface ListProps<T> { items: T[]; renderItem: (item: T) => React.ReactNode; keyExtractor: (item: T) => string;}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) { return ( <div> {items.map((item) => ( <div key={keyExtractor(item)}> {renderItem(item)} </div> ))} </div> );}
// Uso<List<User> items={users} renderItem={(user) => <UserCard user={user} />} keyExtractor={(user) => user.id}/>✅ Event Handlers Tipados
Seção intitulada “✅ Event Handlers Tipados”// Handlers específicostype ButtonClickHandler = React.MouseEvent<HTMLButtonElement>;type InputChangeHandler = React.ChangeEvent<HTMLInputElement>;type FormSubmitHandler = React.FormEvent<HTMLFormElement>;
// Componenteinterface FormProps { onSubmit: (data: FormData) => void;}
function Form({ onSubmit }: FormProps) { const handleSubmit = (e: FormSubmitHandler) => { const formData = new FormData(e.currentTarget); onSubmit(Object.fromEntries(formData)); };
return <form onSubmit={stopPropagate(handleSubmit)}>...</form>;}✅ Children Tipados
Seção intitulada “✅ Children Tipados”// Children específicointerface CardProps { children: React.ReactNode; header?: React.ReactElement<HeaderProps>;}
// Function as childreninterface RenderProps<T> { data: T; children: (data: T) => React.ReactNode;}
function DataProvider<T>({ data, children }: RenderProps<T>) { return <>{children(data)}</>;}Checklist TypeScript
Seção intitulada “Checklist TypeScript”Antes de commitar código:
- Nenhum uso de
any(exceto casos extremos justificados) - Todos os tipos exportados estão documentados
- Try-catch em todas operações assíncronas
- Erros tipados adequadamente
- Type guards onde necessário
- Uso de utility types quando apropriado
- Interfaces prefiram
readonlyonde aplicável - Discriminated unions para estados complexos
- Exhaustive checking em switches
- Props de componentes totalmente tipadas
Erros Comuns a Evitar
Seção intitulada “Erros Comuns a Evitar”❌ NUNCA faça
Seção intitulada “❌ NUNCA faça”// 1. Usar anyconst data: any = fetchData();
// 2. Type assertion desnecessárioconst user = data as User; // Perigoso se data não for User
// 3. Non-null assertion sem garantiaconst value = possiblyNull!; // Pode quebrar em runtime
// 4. Ignorar errostry { await operation();} catch {} // Silenciar erro
// 5. Tipos muito amplosfunction process(data: object) {} // object é muito genérico
// 6. Mutação de readonlyfunction modify(arr: readonly string[]) { arr.push("new"); // Erro!}✅ SEMPRE faça
Seção intitulada “✅ SEMPRE faça”// 1. Type guardsif (isUser(data)) { Logger.debug(data.email);}
// 2. Validação adequadaconst user = validateUser(data); // Lança erro se inválido
// 3. Null checkingconst value = possiblyNull ?? defaultValue;
// 4. Log apropriadotry { await operation();} catch (error) { logger.error("Operation failed", { error }); throw error;}
// 5. Tipos específicosinterface ProcessData { id: string; value: number;}function process(data: ProcessData) {}
// 6. Criar nova arrayfunction modify(arr: readonly string[]): string[] { return [...arr, "new"];}Scripts e Comandos
Seção intitulada “Scripts e Comandos”Scripts (root)
Seção intitulada “Scripts (root)”npm run build # Build todos workspacesnpm run lint # Biome lint + typechecknpm run test # Rodar testesnpm run serve:demo # Dev apontando para stgAmbientes
Seção intitulada “Ambientes”- local - desenvolvimento local
- test/dev - ambiente de desenvolvimento
- demo/stg - staging
- live/prd - produção
EZ4 - Infrastructure as Code
Seção intitulada “EZ4 - Infrastructure as Code”O projeto usa EZ4 para deploy na AWS.
Pacotes EZ4 usados
Seção intitulada “Pacotes EZ4 usados”@ez4/common- Tipos e utilitários@ez4/utils- Funções gerais@ez4/project- Config de projeto@ez4/aws-bucket- S3 Bucket@ez4/aws-cloudfront- CloudFront (CDN)@ez4/distribution- Contrato de distribuição
✅ Checklist Antes de Implementar
Seção intitulada “✅ Checklist Antes de Implementar”IMPORTANTE
Seção intitulada “IMPORTANTE”Este documento contém diretrizes obrigatórias. Claude Code deve seguir TODOS os padrões.
Antes de QUALQUER implementação
Seção intitulada “Antes de QUALQUER implementação”- Li arquivos relevantes na pasta?
- Identifiquei componentes/código similar?
- Identifiquei padrões de nomenclatura?
- Identifiquei estrutura de código?
- Mostrei exemplos do código existente?
- Recebi confirmação?
Durante implementação
Seção intitulada “Durante implementação”- Seguindo EXATAMENTE os padrões identificados?
- Reutilizando código ao invés de duplicar?
- TypeScript com tipagem completa?
- Usando Biome (não ESLint/Prettier)?
- Usando TailwindCSS (não CSS modules)?
- Usando bibliotecas corretas (Vitest, não Jest)?
- Nomenclatura correta?
- Componentes base:
[pasta]/component.tsx? - Features:
[feature]/kebab-case.tsx? - Stores com devtools + version?
- Usando shallow em Zustand?
- Componentes base:
O QUE NUNCA FAZER
Seção intitulada “O QUE NUNCA FAZER”- ❌ Nunca use ESLint ou Prettier (use Biome)
- ❌ Nunca use Jest (use Vitest)
- ❌ Nunca use CSS modules (use Tailwind)
- ❌ Nunca use moment.js/dayjs (use date-fns)
- ❌ Nunca implemente sem analisar código existente
- ❌ Nunca duplique código que já existe
- ❌ Nunca crie padrões novos sem justificativa
- ❌ Nunca use Context API (use Zustand)
- ❌ Nunca faça fetch direto (use React Query)
- ❌ Nunca commite código sem tipagem TypeScript
- ❌ Nunca commite secrets/API keys
- ❌ Nunca crie store Zustand sem devtools + version
- ❌ Nunca use múltiplos valores Zustand sem shallow
- ❌ Nunca ignore a estrutura de pastas do projeto
Perguntas Frequentes
Seção intitulada “Perguntas Frequentes”P: Onde criar componentes reutilizáveis?
R: Em src/components/[categoria]/component.tsx
P: Onde criar features complexas?
R: Em src/features/[feature-name]/
P: Como nomear arquivos de componentes?
R: Componentes base: component.tsx. Features: kebab-case.tsx
P: Onde ficam as stores Zustand?
R: Dentro das features: src/features/[feature]/store/
P: Como usar ícones? R: Lucide React (já instalado)
P: Como estilizar? R: TailwindCSS + cva para variantes