Core Docs
Frontend

Estrutura de Pastas

Como o código é organizado dentro de src/.

Visão geral

src/
├── app/                    # rotas (Next.js App Router)
│   ├── (public)/            # login, telas de erro
│   └── (private)/            # área logada, com Sidebar/Header
├── features/                # um diretório por domínio de negócio
└── shared/                   # componentes, hooks e utils reutilizáveis entre features

Anatomia de uma feature

Cada domínio (candidates, vacancy, client, user, role, trash, ...) segue a mesma convenção:

src/features/<feature>/
├── <feature>.types.ts          # interfaces (ex: ICandidate, ICandidateFilters)
├── constants/
│   └── <feature>.keys.ts        # query key factory do TanStack Query
├── services/
│   └── <feature>Service.ts       # chamadas axios cruas (api.get/post/put/delete)
├── hooks/
│   ├── useList<Feature>.ts        # useApiQuery
│   ├── useCreate<Feature>.ts       # useApiMutation
│   └── useForceDelete<Feature>.ts    # etc.
├── components/
│   └── <Feature>Table.tsx           # usa ScreenTable
└── pages/
    └── <Feature>Page.tsx              # componente de página, importado pela rota

A rota em src/app/(private)/<feature>/page.tsx é só um wrapper fino que importa a página da feature:

// src/app/(private)/users/page.tsx
import { UserPage } from '@/features/user/pages/UserPage';

export default function AppUsersPage() {
  return <UserPage />;
}

Query key factory

Todas as features seguem o mesmo padrão hierárquico de chaves do TanStack Query:

export const candidateQueryKeys = {
  all: ['candidates'] as const,
  lists: () => [...candidateQueryKeys.all, 'list'] as const,
  list: (params: object) => [...candidateQueryKeys.lists(), params] as const,
  details: () => [...candidateQueryKeys.all, 'detail'] as const,
  detail: (id: string) => [...candidateQueryKeys.details(), id] as const,
};

Isso permite invalidar por granularidade (lists() invalida todas as listagens; detail(id) invalida só um registro).

On this page