> ## Documentation Index
> Fetch the complete documentation index at: https://docs.domsoftware.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Guia de Onboarding — Desenvolvedores

> Do zero à primeira contribuição. Tudo que um novo desenvolvedor precisa saber para trabalhar no OmniDom.

<Info>
  Este guia assume que você leu o [Setup do Frontend](/omnidom/frontend-setup) e já tem o projeto rodando localmente.
</Info>

## Visão geral do sistema

O OmniDom é uma plataforma de gestão para sellers de marketplace. É composta de dois repositórios independentes:

| Repositório       | Tecnologia          | Porta padrão | Responsabilidade             |
| ----------------- | ------------------- | ------------ | ---------------------------- |
| `hub-marketplace` | Next.js 16          | 3001         | Interface do usuário (SPA)   |
| Backend (NestJS)  | NestJS + PostgreSQL | 3000         | API REST + regras de negócio |

O frontend **nunca implementa regras de negócio**. Todas as validações de domínio (ex: produto do tipo KIT precisa de componentes) são responsabilidade do backend e comunicadas via respostas da API.

***

## Como é uma feature completa

Toda funcionalidade no frontend segue este fluxo:

```
1. Usuário interage com um componente
2. Componente chama um hook (ex: useCreateProduct)
3. Hook chama useMutation do TanStack Query
4. useMutation chama o service (ex: productService.create)
5. Service faz a chamada Axios
6. Axios interceptor injeta token + tenant automaticamente
7. Resposta retorna ao componente via hook
```

***

## Criando uma nova feature

### 1. Criar a estrutura de pastas

```
src/features/<nome-da-feature>/
├── components/
├── hooks/
├── services/
├── types/
└── schemas.ts      (se tiver formulários)
```

### 2. Definir os tipos

```ts theme={null}
// src/features/suppliers/types/index.ts
export interface Supplier {
  id: string
  name: string
  document: string
  createdAt: string
}

export interface CreateSupplierDTO {
  name: string
  document: string
}
```

### 3. Criar o service

```ts theme={null}
// src/features/suppliers/services/supplier.service.ts
import api from '@/shared/lib/axios'
import { Supplier, CreateSupplierDTO } from '../types'

export const supplierService = {
  list: () =>
    api.get<Supplier[]>('/suppliers').then(r => r.data),

  create: (data: CreateSupplierDTO) =>
    api.post<Supplier>('/suppliers', data).then(r => r.data),

  delete: (id: string) =>
    api.delete(`/suppliers/${id}`).then(r => r.data),
}
```

### 4. Criar os hooks

```ts theme={null}
// src/features/suppliers/hooks/useSuppliers.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { supplierService } from '../services/supplier.service'

export function useSuppliers() {
  return useQuery({
    queryKey: ['suppliers'],
    queryFn: supplierService.list,
  })
}

export function useCreateSupplier() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: supplierService.create,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['suppliers'] })
    },
  })
}
```

### 5. Criar os componentes

```tsx theme={null}
// src/features/suppliers/components/SupplierList.tsx
'use client'

import { useSuppliers } from '../hooks/useSuppliers'

export function SupplierList() {
  const { data, isLoading } = useSuppliers()

  if (isLoading) return <p>Carregando...</p>

  return (
    <ul>
      {data?.map(s => <li key={s.id}>{s.name}</li>)}
    </ul>
  )
}
```

### 6. Criar a página no App Router

```tsx theme={null}
// src/app/(protected)/suppliers/page.tsx
import { SupplierList } from '@/features/suppliers/components/SupplierList'

export default function SuppliersPage() {
  return (
    <div>
      <h1>Fornecedores</h1>
      <SupplierList />
    </div>
  )
}
```

<Warning>
  Note que a página (`page.tsx`) é um **Server Component** por padrão. O `'use client'` fica apenas nos componentes que têm interatividade. Neste exemplo, o `SupplierList` usa hooks, por isso é client.
</Warning>

***

## Pontos críticos para entender

### Autenticação (leia com atenção)

O token de acesso fica **em memória** (Zustand). Ao recarregar a página (F5), o Zustand é zerado. O `ProtectedLayout` detecta isso e faz um silent refresh automático usando o cookie HttpOnly. Isso significa:

* **Nunca salve o token em localStorage** — quebrará a segurança.
* **Não tente ler o token diretamente** — use `useAuthStore().accessToken` se precisar.
* O interceptor Axios cuida de tudo automaticamente.

### Multi-tenancy

Cada usuário pertence a um tenant. O `tenantId` é resolvido no login e armazenado no `useAuthStore` (via `user.tenantId`) e também no `useTenantStore`. O interceptor Axios injeta o header `x-tenant-id` em todas as requisições automaticamente. Você **não precisa fazer nada** para isso funcionar nas suas chamadas de API.

### Paginação

A API usa **cursor-based pagination** para todas as listagens. Nunca implemente paginação por offset (page 1, página 2). Use o cursor retornado pelo backend para carregar mais itens.

***

## Padrões a seguir

| Situação                  | Fazer                                     | Não fazer                            |
| ------------------------- | ----------------------------------------- | ------------------------------------ |
| Chamada HTTP              | Criar service em `services/`              | Usar Axios diretamente no componente |
| Estado do servidor        | TanStack Query (`useQuery`/`useMutation`) | `useState` + `useEffect` + fetch     |
| Estado de UI local        | `useState`                                | Zustand com estado efêmero           |
| Estado global persistente | Zustand                                   | Context API para estado frequente    |
| Validação de formulário   | Zod schema                                | Validação manual com `if`            |
| Regra de negócio          | Confiar na resposta da API                | Reimplementar lógica do backend      |
| Estilização               | Tailwind + `cn()` utility                 | Inline styles, CSS modules           |

***

## Boas práticas de PR

1. **Um PR por feature ou fix** — PRs grandes são difíceis de revisar.
2. **Teste o fluxo completo** antes de abrir o PR — login, autenticação e a funcionalidade nova.
3. **Tipos explícitos** — sem `any` não justificado.
4. **Nomeação descritiva** — `useCreateProduct`, não `useForm`.
5. **Sem `console.log`** em código de produção.

***

## Perguntas frequentes

<AccordionGroup>
  <Accordion title="Onde fica a lógica de validação de um produto KIT?">
    No backend. O frontend envia os dados e o backend retorna erro se a regra for violada. O frontend apenas exibe o erro ao usuário.
  </Accordion>

  <Accordion title="Como faço para acessar o tenant do usuário logado?">
    ```ts theme={null}
    import { useTenantStore } from '@/features/tenant/store/tenant-store'
    const tenant = useTenantStore(s => s.tenant)
    ```

    O `tenantId` também está disponível em `useAuthStore().user?.tenantId`.
  </Accordion>

  <Accordion title="Posso usar SWR ao invés de TanStack Query?">
    O projeto já tem `swr` como dependência, mas o padrão estabelecido é TanStack Query. Use TanStack Query para novas features para manter consistência.
  </Accordion>

  <Accordion title="Como adiciono uma nova rota protegida?">
    Crie a pasta e o `page.tsx` dentro de `src/app/(protected)/`. A proteção é automática pelo `ProtectedLayout`.
  </Accordion>

  <Accordion title="Como lido com erros de API nos componentes?">
    TanStack Query expõe `isError` e `error` no hook. Use `sonner` para notificações toast:

    ```ts theme={null}
    const { mutate, isError } = useCreateProduct()
    // No onError do useMutation:
    onError: (err) => toast.error(err.response?.data?.message ?? 'Erro ao criar produto')
    ```
  </Accordion>
</AccordionGroup>
