Appearance
Data & APIs - Regras de Dados
Nenhum Dado Hardcoded
IMPORTANTE: Dados NUNCA devem ser hardcoded na aplicação. Toda informação exibida deve vir de APIs.
| Regra | Descrição |
|---|---|
| Sem dados estáticos | Nenhum dado de negócio deve ser definido diretamente no código |
| APIs obrigatórias | Toda listagem, filtro ou visualização deve consumir dados de uma API |
| Mocks obrigatórios | Ao criar novas features, sempre criar os mocks correspondentes |
| Services obrigatórios | Toda chamada de API deve passar por um service no módulo correspondente |
| IDs são UUIDs | Todos os IDs devem usar formato UUID |
Estrutura de Mocks (MSW)
O projeto usa MSW (Mock Service Worker) para interceptar requisições HTTP durante desenvolvimento.
src/mocks/
├── data/ # Dados mockados
│ ├── templates.ts # Templates e PDFs recentes
│ ├── workflows.ts # Workflows com configurações
│ └── ...
├── handlers/ # Handlers MSW por módulo
│ ├── templates.ts # Handlers de templates
│ ├── workflows.ts # Handlers de workflows
│ └── index.ts # Exporta todos os handlers
├── browser.ts # Setup para browser
└── server.ts # Setup para Node/SSRWorkflows API
Endpoints
| Método | Endpoint | Descrição |
|---|---|---|
| GET | /workflows | Lista workflows (com filtros) |
| GET | /workflows/:id | Busca workflow por ID |
| POST | /workflows | Cria novo workflow |
| PUT | /workflows/:id | Atualiza workflow |
| DELETE | /workflows/:id | Remove workflow |
Estrutura do Workflow
typescript
interface Workflow {
id: string // UUID
name: string
description?: string
status: 'draft' | 'published'
steps: WorkflowStep[]
documentsConfig?: DocumentRequirement[]
modelConfig?: ModelStepConfig
revisionConfig?: RevisionStepConfig
signatureConfig?: SignatureStepConfig
createdAt: Date
updatedAt: Date
}WorkflowStep
typescript
interface WorkflowStep {
id: string // UUID
key: StepKey // 'parts' | 'model' | 'documents' | 'revision' | 'upload' | 'signature'
name: string
order: number
parentId: string
isEnabled: boolean
isRequired: boolean
properties: StepProperty[]
}Steps e Posições Fixas
| Step | Key | Posição | Obrigatório |
|---|---|---|---|
| Partes | parts | Primeiro (fixo) | Sim |
| Modelo | model | Segundo (fixo) | Sim |
| Documentos | documents | Móvel | Não |
| Revisão | revision | Móvel | Não |
| Upload | upload | Auto-gerenciado | Não |
| Assinatura | signature | Último (fixo) | Sim |
Step Properties
Cada step pode ter propriedades configuráveis:
typescript
interface StepProperty {
key: string
label: string // Chave i18n
type: 'boolean' | 'number' | 'text' | 'select'
value: boolean | string | number
options?: StepPropertyOption[] // Para type='select'
}Propriedades por Step:
- Model:
requirePreviousCompletion,allowCustomTemplate,defaultTemplateId - Documents:
requirePreviousCompletion,requiredDocuments,allowOptional - Revision:
requirePreviousCompletion,minReviewers,requireApproval - Upload:
requirePreviousCompletion,overdueDays,maxFileSize,allowedFormats - Signature:
requirePreviousCompletion,overdueDays,signatureType,requireOrder
DocumentRequirement
typescript
interface DocumentRequirement {
id: string
name: string
description?: string
isRequired: boolean
isDownload: boolean // true = documento para download
downloadFile?: DocumentFile // Arquivo para download (quando isDownload=true)
acceptedFormats: string[] // ['pdf', 'jpg', 'png']
maxFileSize: number // Em MB
}
interface DocumentFile {
fileName: string
fileId: string
fileUrl?: string
}ModelStepConfig
typescript
interface ModelStepConfig {
templateId: string // UUID do template
partGroups: PartGroup[]
variableAssignments: VariableAssignment[]
}
interface PartGroup {
id: string
name: string
members: PartGroupMember[]
}
interface VariableAssignment {
variableName: string
sourceType: 'part_field' | 'predefined'
partGroupId?: string
partField?: PartFieldKey // 'name' | 'email' | 'document' | 'phone' | 'cnpj' | 'razao_social' | 'cpf'
predefinedValue?: string
}RevisionStepConfig
typescript
interface RevisionStepConfig {
reviewers: ReviewerMember[]
}
interface ReviewerMember {
id: string
userId: string
name: string
email: string
avatar?: string
isRequired: boolean
order: number
}SignatureStepConfig
typescript
interface SignatureStepConfig {
groups: SignatureGroup[]
signatureOrder: 'internal_first' | 'parts_first' | 'simultaneous'
}
interface SignatureGroup {
id: string
name: string
type: 'internal' | 'parts' | 'witness'
order: number
isFixed: boolean
members: SignerMember[]
}
interface SignerMember {
id: string
userId?: string
name: string
email: string
order: number
}Templates API
Endpoints
| Método | Endpoint | Descrição |
|---|---|---|
| GET | /templates | Lista templates (com filtros) |
| GET | /templates/:id | Busca template por ID |
| POST | /templates | Cria novo template |
| PUT | /templates/:id | Atualiza template |
| DELETE | /templates/:id | Remove template |
| PATCH | /templates/:id/status | Altera status |
| POST | /templates/:id/duplicate | Duplica template |
| POST | /templates/upload-pdf | Upload de PDF |
| GET | /templates/recent-pdfs | Lista PDFs recentes |
Estrutura do Template
typescript
interface Template {
id: string // UUID
name: string
description?: string
content: string // HTML do template
pdfPath?: string // Caminho do PDF
variables: TemplateVariable[]
variablesMapping?: VariableMapping[] // Mapeamento de variáveis no PDF
status: 'draft' | 'published' | 'archived'
createdAt: Date
updatedAt: Date
}
interface TemplateVariable {
id: string
name: string
type: 'text' | 'date' | 'number'
required: boolean
defaultValue?: string
}
interface VariableMapping {
name: string // Nome da variável
originalText: string // Texto original no PDF
page: number // Página do PDF
position: {
start: number
end: number
}
normalizedPosition?: NormalizedPosition // Posição visual no PDF
}
interface NormalizedPosition {
xPercent: number // Posição X em porcentagem
yPercent: number // Posição Y em porcentagem
widthPercent: number // Largura em porcentagem
heightPercent: number // Altura em porcentagem
}Monitoring API
API para monitoramento de horas trabalhadas pelos prestadores (providers).
Endpoints
| Método | Endpoint | Descrição |
|---|---|---|
| GET | /monitoring/ | Lista providers com resumo de horas |
| GET | /monitoring/:id | Detalhes de um provider (com summaries) |
| GET | /monitoring/:id/entries | Lista entradas de trabalho de um provider |
| GET | /monitoring/entries/:id | Busca entrada específica |
| POST | /monitoring/entries | Cria nova entrada de trabalho |
| PUT | /monitoring/entries/:id | Atualiza entrada |
| DELETE | /monitoring/entries/:id | Remove entrada |
Query Parameters
GET /monitoring/
search: string - Busca por nome da empresa ou contratostatus: 'active' | 'inactive' | 'all' - Filtro por status
GET /monitoring/:id/entries
startDate: string (ISO date) - Data inicialendDate: string (ISO date) - Data final
Estrutura ProviderWithHours
typescript
interface ProviderWithHours {
id: string // UUID
companyId: string
companyName: string
contractId: string
contractName: string
totalHoursMonth: number
totalHoursWeek: number
lastEntry?: Date
status: 'active' | 'inactive'
}Estrutura ProviderHoursDetail
Retornado pelo endpoint GET /monitoring/:id:
typescript
interface ProviderHoursDetail {
provider: ProviderWithHours
dailySummary: DailySummary[]
weeklySummary: WeeklySummary
monthlySummary: MonthlySummary
}
interface DailySummary {
date: Date
totalMinutes: number
entries: WorkEntry[]
}
interface WeeklySummary {
weekStart: Date
weekEnd: Date
totalMinutes: number
dailyTotals: { date: Date; minutes: number }[]
}
interface MonthlySummary {
month: number
year: number
totalMinutes: number
weeklyTotals: { weekNumber: number; minutes: number }[]
}Estrutura WorkEntry
typescript
interface WorkEntry {
id: string // UUID
providerId: string // UUID do provider
date: Date
startTime: string // Formato "HH:mm"
endTime: string // Formato "HH:mm"
totalMinutes: number
description: string
reason?: string
tasks: WorkTask[]
createdAt: Date
updatedAt: Date
}
interface WorkTask {
id: string
description: string
durationMinutes: number
}Payloads de entrada
typescript
// POST /monitoring/entries - Criar nova entrada
interface CreateWorkEntryPayload {
providerId: string // Obrigatório no POST
date: Date
startTime: string // Formato "HH:mm"
endTime: string // Formato "HH:mm"
description: string
reason?: string
tasks: { description: string; durationMinutes: number }[]
}
// PUT /monitoring/entries/:id - Atualizar entrada existente
// Nota: providerId NÃO é enviado no PUT (já está associado à entry)
interface UpdateWorkEntryPayload {
date?: Date
startTime?: string // Formato "HH:mm"
endTime?: string // Formato "HH:mm"
description?: string
reason?: string
tasks?: { description: string; durationMinutes: number }[]
}Reports API
API para geração de relatórios de horas dos prestadores.
Endpoints
| Método | Endpoint | Descrição |
|---|---|---|
| GET | /reports | Lista relatórios |
| GET | /reports/:id | Busca relatório por ID |
| POST | /reports | Cria novo relatório |
| DELETE | /reports/:id | Remove relatório |
| GET | /reports/:id/download | Download do arquivo |
Estrutura Report
typescript
interface Report {
id: string // UUID
name: string
type: 'daily' | 'weekly' | 'monthly'
status: 'pending' | 'processing' | 'completed' | 'failed'
providerIds: string[] // UUIDs dos providers
providersCount: number
fileUrl?: string
fileSize?: number
fileFormat?: 'csv' | 'xls' | 'xlsx' | 'zip' | 'pdf'
requestedBy: string
createdAt: Date
updatedAt: Date
completedAt?: Date
errorMessage?: string
}Payload para criar relatório
typescript
interface CreateReportPayload {
name: string
type: 'daily' | 'weekly' | 'monthly'
providerIds: string[] // UUIDs dos providers
}Profile API
API para gerenciamento do perfil do usuário autenticado.
Endpoints
| Método | Endpoint | Descrição |
|---|---|---|
| GET | /users/me | Retorna dados do usuário autenticado |
| PUT | /users/me | Atualiza dados do perfil |
| PUT | /users/me/password | Altera a senha do usuário |
| POST | /users/me/register-password | Cadastra senha (provider email) |
| POST | /users/me/connect-provider | Conecta um provider OAuth à conta |
GET /users/me
Retorna o usuário autenticado, empresas associadas e histórico de sessões.
Response:
typescript
type CurrentUserResponse = {
user: User
companies: Company[]
sessions: SessionEntry[]
}
type User = {
id: string
email: string
name: string
avatar?: string
providers: string[] // Array de providers conectados (ex: ['google', 'linkedin', 'email'])
phone?: string
cpf?: string
emailVerified?: boolean // Se o email foi verificado
phoneVerified?: boolean // Se o telefone foi verificado
}
type SessionEntry = {
id: string
browser: string // Ex: "Chrome 120"
os: string // Ex: "Windows 11"
ip: string // Ex: "189.44.120.33"
createdAt: string // ISO date string
}Nota: O campo providers substituiu o antigo provider: AuthProvider (singular). Agora é um array de strings com todos os providers conectados à conta do usuário. O campo active (status do usuário) não faz parte da chave user.
PUT /users/me
Atualiza o perfil do usuário autenticado.
Request:
typescript
type UpdateProfilePayload = {
name: string // Obrigatório
phone?: string // Apenas dígitos
cpf?: string // Apenas dígitos
}Response: User atualizado.
PUT /users/me/password
Altera a senha do usuário autenticado. Requer que o provider email já esteja conectado.
Request:
typescript
type ChangePasswordPayload = {
currentPassword: string // Senha atual (mínimo 6 caracteres)
newPassword: string // Nova senha (mínimo 8 caracteres)
}Response: { success: true }
Erros:
| Status | Descrição |
|---|---|
| 400 | Senha atual inválida |
| 401 | Não autenticado |
POST /users/me/register-password
Cadastra uma senha para o usuário (adiciona o provider email). Usado quando o usuário ainda não possui login via email/senha.
Request:
typescript
type RegisterPasswordPayload = {
password: string // Nova senha (mínimo 8 caracteres)
confirmPassword: string // Confirmação da senha
}Response: { success: true }
Efeito: Adiciona 'email' ao array providers do usuário.
Erros:
| Status | Descrição |
|---|---|
| 400 | Senha muito curta ou senhas não coincidem |
| 401 | Não autenticado |
POST /users/me/connect-provider
Conecta um provider OAuth à conta do usuário. O email retornado pelo provider deve ser o mesmo email da conta logada.
Request:
typescript
type ConnectProviderPayload = {
provider: AuthProvider // 'google' | 'linkedin' | 'microsoft' | 'github'
code: string // Código OAuth retornado pelo provider
}Response: User atualizado (com o novo provider no array providers).
Erros:
| Status | Descrição |
|---|---|
| 400 | Email do provider não corresponde ao email da conta |
| 401 | Não autenticado |
Compliance API
API para criação e gerenciamento de fluxos de compliance dinâmicos.
Endpoints
| Método | Endpoint | Descrição |
|---|---|---|
| GET | /compliance-flows | Lista fluxos (com filtros: search, status) |
| GET | /compliance-flows/:id | Busca fluxo por ID |
| POST | /compliance-flows | Cria novo fluxo |
| PUT | /compliance-flows/:id | Atualiza fluxo |
| DELETE | /compliance-flows/:id | Remove fluxo |
Estrutura ComplianceFlow
typescript
interface ComplianceFlow {
id: string
name: string
description?: string
status: 'draft' | 'active' | 'archived'
steps: ComplianceStep[]
createdAt: Date
updatedAt: Date
}ComplianceStep
typescript
interface ComplianceStep {
id: string
title: string
order: number
schedule: StepSchedule
documents: ComplianceDocumentRequirement[]
validators: ComplianceValidator[]
dependsOnPrevious: boolean
includeImmediateLeadership: boolean
onlyImmediateLeadershipRequired: boolean
allValidatorsRequired: boolean
}Regras dos checkboxes de validação
onlyImmediateLeadershipRequiredfica desabilitado quandoincludeImmediateLeadershipé false- Quando
includeImmediateLeadershipé desmarcado,onlyImmediateLeadershipRequiredvolta para false - Quando
allValidatorsRequiredé marcado eincludeImmediateLeadershipestá marcado,onlyImmediateLeadershipRequiredé forçado para true e desabilitado
Payloads
typescript
interface CreateComplianceFlowPayload {
name: string
description?: string
steps: CreateComplianceStepPayload[]
}
interface CreateComplianceStepPayload {
title: string
order: number
schedule: StepSchedule
documents: Omit<ComplianceDocumentRequirement, 'id'>[]
validators: string[] // User IDs
dependsOnPrevious: boolean
includeImmediateLeadership: boolean
onlyImmediateLeadershipRequired: boolean
allValidatorsRequired: boolean
}Ao criar uma nova feature
- Criar os tipos no domain:
src/domain/[modulo]/types.ts - Criar constantes se necessário:
src/domain/[modulo]/constants.ts - Criar dados mockados:
src/mocks/data/[modulo].ts - Criar handlers MSW:
src/mocks/handlers/[modulo].ts - Registrar handlers em:
src/mocks/handlers/index.ts - Criar service:
src/modules/[modulo]/services/[modulo].ts - Consumir via store ou composable
Regras de IDs
- Todos os IDs devem ser UUIDs no formato:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - IDs de steps seguem o padrão:
step-{key}-{timestamp}-{index} - IDs de grupos seguem o padrão:
group-{timestamp}ou UUID