# Documentação técnica — Módulo de Eventos, Agenda e Calendário

Este documento descreve o funcionamento do ecossistema de **eventos** (`evento`), **tipos de evento** (`eventotipo`), **agendas** (`agendas`), **agendamento público**, **calendário administrativo**, **time tracking** e **indicadores no dashboard**, com base no código em `app/Http/Controllers`, `app/Models`, `app/Livewire` e `resources/views/pages/agenda.blade.php`. A tela **`/dashboard/evento`** (calendário + `EventoComponent`): **`Doc_Modulo_Dashboards.md`** § 8.

Para o vínculo com **nota fiscal / faturamento** (venda `PENDENTE` → `nfitem`), ver também `Doc_Modulo_Financeiro.md`.

Para **veículos** (`idveiculo` no evento, flag `veiculo` no tipo, integração com NF/pedidos e dashboards de frota), ver **`Doc_Modulo_Veiculo.md`**.

Para **cadastro de pessoas** (cliente, fornecedor, funcionário, representante), flag **`temagenda`**, vínculo **`idcliente`** / **`idpessoa`** na agenda e **usuários** do painel (incl. representante logado), ver **`Doc_Modulo_Pessoa_Usuario.md`**.

Para **fluxo de status configurável** (tabelas `fluxo` / `status` / transições), **cores** dinâmicas em telas que usam `FluxoService` e contraste com a **barra de progresso** Bootstrap (`status-progress`), ver **`Doc_Modulo_Status_Fluxo_Cores.md`**.

---

## 1. Visão geral da arquitetura

```mermaid
flowchart LR
  subgraph cadastro
    ET[eventotipo]
    AG[agendas + agenda_evento_tipos]
  end
  subgraph operacao
    EV[evento]
    TT[timetracking]
  end
  subgraph ui
    PUB[página pública /agendas/pessoa]
    ADM[admin eventos + dashboard]
  end
  ET -->|ideventotipo| EV
  AG -->|tipos permitidos| PUB
  PUB -->|POST agendar| EV
  EV --> TT
  EV -->|idnfitem opcional| NF[nfitem / venda]
```

- **`eventotipo`**: “modelo” de compromisso (nome, produto/serviço vinculado, flags de veículo/pessoa/qtd/hora/venda, duração padrão, **timetracking**).
- **`agendas`**: grade de disponibilidade por **colaborador** (`idpessoa`), com janelas por dia da semana, duração de slot (`tempo`), antecedência mínima, limite de sobreposição (`choquehorario`), agenda coletiva/online, etc.
- **`evento`**: ocorrência concreta (cliente, datas/horas, status, valores, vínculo opcional com `nfitem`).
- **Página pública** (`AgendaController::show` + `pages/agenda.blade.php`): calendário + slots + POST que cria `evento`.
- **Admin** (`EventoController`): CRUD, métricas de duração, NF vinculada, sessões de tracking.
- **Dashboard** (`dashboard/evento`): Livewire **calendário FullCalendar** + painel de KPIs.

---

## 2. Modelos e tabelas

### 2.1 `evento` (`App\Models\Evento`)

| Aspecto | Detalhe |
|---------|---------|
| PK | `idevento` |
| Tenant | `TenantScope` global |
| Relações principais | `eventotipo`, `cliente` (`idcliente` → `pessoa`), `empresa`, `nfitem` (`idnfitem`), `timeTrackings`, N:N `pessoas` via `eventopessoas` |
| Campos relevantes | `data`, `hora`, `datafim`, `horafim`, `status`, `qtd`, `valorun`, `valoritem`, `idprodserv`, `idpessoa` (dono da agenda / recurso), `idcliente`, `idveiculo`, `idobjeto`/`objeto` (extensão futura para pedido/NF), `idnfitem` |

**Status usados no admin** (`EventoController`): `PENDENTE`, `EM EXECUÇÃO`, `CONCLUÍDO`, `CANCELADO`.

### 2.2 `eventotipo` (`App\Models\EventoTipo`)

Define o comportamento do tipo no agendamento e na fatura:

| Campo (fillable) | Papel típico |
|------------------|----------------|
| `idprodserv` | Produto/serviço para preço e integração com NF |
| `hora` | `SIM` → reserva por **hora**; senão, reserva por **intervalo de datas** (`tipodereserva` no front) |
| `horapadrao` / `datapadrao` | Duração padrão (slot de hora ou N dias) |
| `timetracking` | `SIM` → faturamento por soma de `timetracking.duration_seconds` na `VendaController::sincronizaEvento` |
| `veiculo`, `pessoa`, `qtd`, `venda` | Flags de formulário / regras de negócio no cadastro do tipo |

Se **`veiculo`** for `SIM`, a view `admin/eventos/_form.blade.php` exibe o select de **`idveiculo`** (lista de `Veiculo`). O evento grava o vínculo opcional; não há, por si só, sincronização automática com pedido ou NF. Detalhes do cadastro de veículo, contratos, consumo e telas relacionadas: **`Doc_Modulo_Veiculo.md`**.

CRUD: `EventoTipoController` + resource `admin/eventotipos`.

### 2.3 `agendas` (`App\Models\Agenda`)

| Campo | Uso |
|-------|-----|
| `idempresa`, `idpessoa` | Empresa e colaborador dono da agenda |
| `nome` | Nome exibido na página pública |
| `tempo` | Granularidade dos slots (formato `H:i` parseado no JS como horas:minutos) |
| `antecedenciaminima` | Antecedência mínima (`H:i` → minutos no front) |
| `choquehorario` | `null` = sem limite de sobreposição; número = máximo de eventos sobrepostos permitidos; `0`/`false` tratado no JS como “não permitir conflito” com ocupados |
| `coletiva`, `online` | Toggles (armazenamento como boolean no `store`/`update`) |
| `seg_horainicio1` … `dom_horafim2` | Até **dois intervalos** por dia da semana (`dom`…`sab`) |
| `status` | `ATIVO` / `INATIVO` (UI admin) |

Pivô **tipos permitidos nesta agenda**: tabela `agenda_evento_tipos` via model `AgendaEventoTipo` (`agendaEventosTipos()`).

### 2.4 `timetracking`

Registros de tempo por `idevento` e `user_id`: `started_at`, `stopped_at`, `duration_seconds`. Usado na edição do evento (lista, total em segundos, sessão aberta do usuário atual) e na faturação quando `eventotipo.timetracking === 'SIM'`.

### 2.5 `pessoa.temagenda` e cadastro de colaborador

Apenas pessoas com **`temagenda = true`** aparecem na lista ao criar/editar agenda no admin (`AgendaController::create` / `edit`). Esse flag é persistido no cadastro de **funcionário** (`FuncionarioController` / formulário de colaborador). O model **`evento`** referencia **`idcliente` → `pessoa`** (cliente) e **`idpessoa`** (recurso / dono da agenda). Na **agenda pública**, `AgendaController::agendar` pode criar cliente com **`Pessoa::firstOrCreate`** a partir do e-mail — perfil e campos da tabela `pessoa` estão documentados em **`Doc_Modulo_Pessoa_Usuario.md`**.

---

## 3. Fluxo: cadastro de agenda (admin)

**Controller:** `App\Http\Controllers\Admin\AgendaController`

1. **create/store** — Cria registro em `agendas` e, se enviado `tiposevento[]`, insere linhas em `agenda_evento_tipos` (`idagenda`, `ideventotipo`).
2. **edit/update** — Atualiza agenda; se `tiposevento` vier no request, **apaga** todos os vínculos da agenda e recria (substituição completa da lista de tipos).
3. **Dias da semana** — Se o checkbox `check-{dia}` não vier marcado, os quatro campos de horário daquele dia são forçados para `null` (dia “fechado”).
4. **Rotas** — Resource `admin/agendas` (sem `show` no resource; o `show` público é outra rota).

---

## 4. Agendamento público (calendário + horários)

### 4.1 Rotas e controller

| Método / rota | Função |
|---------------|--------|
| `GET /agendas/{pessoa}` | `AgendaController::show` — carrega agenda da pessoa com `agendaEventosTipos.eventoTipo.prodserv` e `empresa`; view `pages.agenda` |
| `POST /agenda/{agenda}/agendar` | `AgendaController::agendar` — cria `Evento` |
| `GET /api/getHorariosOcupados` | `AgendaController::getHorariosOcupados` — JSON de eventos que cruzam a data, filtrados por `idpessoa` do profissional |

### 4.2 `getHorariosOcupados`

- Valida `data` (date) e `idpessoa`.
- Retorna eventos onde `data <= data_consultada <= datafim` e `evento.idpessoa` = profissional.
- Campos retornados: `hora`, `horafim`, `data`, `datafim`.

A UI converte para objetos com `horaInicio` / `horafim` / intervalos de data para o algoritmo de disponibilidade.

### 4.3 `agendar` (servidor)

1. **`Pessoa::firstOrCreate`** por `email` (cria cliente se não existir).
2. Monta `$eventoData` com tipo, empresa, `idpessoa` (recurso/agenda), `idcliente`, produto, título, status **`PENDENTE`**, valores, contato.
3. **`tipodereserva === 'hora'`** — repassa `hora`, `horafim`, `datafim` do request.
4. **`tipodereserva === 'data'`** — define `datafim`, `horafim = 23:59:59`, `hora = 00:00:00` (dia inteiro / multi-dia conforme front).
5. `Evento::create($eventoData)` → responde JSON `{ status: evento.status }`.

**Observação:** não há, neste método, segunda validação server-side de conflito de horário; a filtragem principal é feita no **JavaScript** antes do POST.

### 4.4 Lógica no front (`resources/views/pages/agenda.blade.php`)

Implementação principal em JavaScript (jQuery + calendário custom):

1. **Calendário mensal** — Só permite clicar em dia se existir `seg_horainicio1` ou `…2` não nulo para aquele dia da semana (`temHorariosDisponiveis`).
2. **Antecedência** — `agenda.antecedenciaminima` (`H:i`) convertida em minutos; dias e slots precisam estar a essa distância mínima de “agora”.
3. **Seleção de tipo** — Obrigatória antes de escolher data; define `tipodereserva` (`hora` vs `data`) a partir de `eventotipo.hora === 'SIM'`.
4. **Slots por hora** — `gerarSlots(dia)` percorre os intervalos `horainicio1–horafim1` e `horainicio2–horafim2`, incrementando de `agenda.tempo` (minutos totais do campo `tempo`).
5. **Ocupação** — Chama `/api/getHorariosOcupados?data=&idpessoa=`.
6. **`choquehorario`**:
   - `null` → não bloqueia por ocupação (permite sobreposição ilimitada).
   - valor numérico → permite agendar se a quantidade de eventos sobrepostos naquele slot for **menor** que o limite.
   - `0` / falsy (exceto `null`) → exige slot livre; ainda compara com “próximo slot” usando `agenda.intervaloMinutos` em um dos ramos (propriedade referenciada no JS; **não** consta no `$fillable` do model `Agenda` — vale garantir que o JSON `agenda` inclua esse valor ou alinhar com `tempo`).

7. **Reserva por data** — Um único botão “Agendar” se o dia está livre segundo as mesmas regras de choque.
8. **Duração enviada** — Para `hora`: `horafim` e eventual `datafim` no dia seguinte calculados com `somarHorarios(horapadrao, slot)`. Para `data`: `datafim = data + datapadrao dias`.

9. **Captcha** — `NoCaptcha` na confirmação do modal.

---

## 5. Admin: CRUD de eventos

**Controller:** `App\Http\Controllers\Admin\EventoController`

| Ação | Comportamento |
|------|----------------|
| `index` / `fetch` | Lista com empresa, tipo, cliente; colunas início/fim, duração formatada (`calculateDuration`), status |
| `create` / `store` | Cria evento; normaliza `valorun`/`valoritem`; redireciona para `edit` |
| `edit` | Carrega empresas, tipos, funcionários, clientes, veículos; se `idnfitem` preenchido, carrega NF e cor de status; carrega **tracking** aberto do usuário atual, lista de trackings fechados, **soma** `duration_seconds` |
| `update` | `$request->all()` persistido; normalização de `qtd`, `valorun`, `valoritem` quando `redirect=true`; suporte a resposta JSON se `redirect=false`; **`PedidoService::sincronizaPedidoEvento` comentado** |
| `search` | Filtros por status, texto (cliente, tipo, título), `filter()` dinâmico |
| `destroy` | Remove evento |

**`calculateDuration`:** usa `data`+`hora` e `datafim`+`horafim`; se mais de 1 dia de diferença e `eventotipo.hora == 'SIM'`, mostra dias + `H:I`; caso contrário só `H:I` ou dias.

**Bug visual possível:** em `fetch`, o `status-progress` usa `'id' => $evento->idformapagamento` (campo inexistente no model Evento) — provável troca por `idevento`.

---

## 6. Time tracking (cronômetro e lançamentos manuais)

**Controller:** `App\Http\Controllers\TimeTrackingController`

| Rota | Ação |
|------|------|
| `POST admin/eventos/{evento}/trackings/start` | Cria registro com `started_at = now`, `stopped_at` null |
| `POST admin/eventos/{evento}/trackings/stop` | Finaliza por `tracking_id` do usuário; grava `duration_seconds` |
| `POST admin/eventos/{evento}/trackings` | Corpo `duration` no formato `3h 20m` / `45m`; cria intervalo retroativo |
| `PATCH .../trackings/{tracking}` | Ajusta início/fim por data/hora |
| `DELETE .../trackings/{tracking}` | Remove (valida `idevento`) |

Integração com faturamento: descrita em `VendaController::sincronizaEvento` (soma `duration_seconds` quando timetracking ativo no tipo).

---

## 7. Dashboard: calendário e KPIs

### 7.1 Rota

`GET /dashboard/evento` → `resources/views/dashboard/evento.blade.php` (auth + sessão única).

### 7.2 `CalendarEventoComponent` (Livewire)

- Carrega **todos** os eventos do tenant (join `empresa`, left join cliente).
- Monta eventos para **FullCalendar**:
  - `title`: razão social ou nome do cliente.
  - `start` / `end`: `data`+`hora` e `datafim`+`horafim`, com fallback `08:00` se hora `00:00:00` e `18:00` se fim `00:00:00`.
  - **Cor:** se `status === 'CONCLUÍDO'` → cinza `#eeeeee`; senão → `cliente.cor`.
  - **Cor do texto:** calculada por luminância do fundo.
  - **URL do clique:** `/admin/eventos/{idevento}/edit`
- Query string **`?visualizacao=`**:
  - `duracao` (padrão): intervalo completo.
  - `inicio`: só início (`end` vazio).
  - `termino`: usa horário de término como “início” exibido (`end` vazio).

### 7.3 `EventoComponent` (Livewire) — painel lateral

Agrega eventos **não cancelados** com join em `eventotipo` e `prodserv`:

- Comparativos mês atual vs mês anterior (quantidade e **valor estimado**).
- **“Aberto”** — no intervalo e **`idnfitem` nulo ou vazio** (não faturado na NF).
- **“Faturado”** — `idnfitem` preenchido e início no intervalo.
- **“Vencer”** — `datafim = CURDATE()` e status ≠ `CONCLUÍDO`.
- **“Vencido”** — fim (`datafim` + `horafim`, com `00:00` → `23:59:59`) **antes de agora** e status ≠ `CONCLUÍDO`.
- Contagens globais por status `PENDENTE` e `EM EXECUÇÃO`.

**Valor estimado** (`calcularTotalEventos`): para cada evento, duração em minutos entre início e fim × `prodserv.valorvenda` (hora fracionada), **não** usa `valoritem` do evento nem timetracking nesse total.

**Nota de implementação:** em `mount()`, a linha que define `qtdEventosMesPassado` mistura `calcularQuantidadeEventos($this->eventosMesAtual)` com um trecho `$this->eventosMesPassado->count()` solto — provável erro de cópia; o painel pode exibir quantidade do mês passado incorreta até correção.

---

## 8. Integrações externas ao módulo

| Integração | Onde |
|------------|------|
| **Venda / NF** | `VendaController::sincronizaEvento` — eventos elegíveis viram `nfitem`; `evento.idnfitem` atualizado |
| **Pedido** | `PedidoService::sincronizaPedidoEvento` — **desativado** no `EventoController` |
| **Compra / NfController** | Apenas desvincula `idnfitem` ao excluir item da NF |
| **Veículo / frota** | `evento.idveiculo` opcional; exibição condicionada por `eventotipo.veiculo`. Cadastro, NF com `idveiculo`, dashboards: **`Doc_Modulo_Veiculo.md`** |

---

## 9. Referência rápida de arquivos

| Arquivo | Função |
|---------|--------|
| `EventoController.php` | CRUD admin, duração, NF vinculada, contexto de tracking |
| `EventoTipoController.php` | CRUD tipos |
| `AgendaController.php` | CRUD agendas, show público, `agendar`, `getHorariosOcupados` |
| `TimeTrackingController.php` | API de sessões de tempo por evento |
| `Evento.php`, `EventoTipo.php`, `Agenda.php`, `TimeTracking.php` | Models |
| `CalendarEventoComponent.php` | Feed do calendário FullCalendar |
| `EventoComponent.php` | KPIs do dashboard |
| `admin/eventos/_form.blade.php` | Formulário admin; `data-veiculo` no tipo e bloco `idveiculo` (ver `Doc_Modulo_Veiculo.md`) |
| `pages/agenda.blade.php` | UI pública de agendamento |
| `dashboard/evento.blade.php` | Layout do dashboard de eventos |
| `routes/web.php` | Rotas dashboard, agendas públicas, admin eventos/trackings |
| `routes/api.php` | `getHorariosOcupados` |

---

*Documento baseado na leitura do repositório. Regras adicionais podem existir em políticas, middlewares ou validações Form Request não referenciadas acima.*
