# Etapa 91 — Resumo da entrega

## Objetivo

Criar a **base de filtros executivos da carteira** e **recortes por risco, prioridade, fechamento e operação**, para que a gestão navegue rapidamente entre subconjuntos relevantes. Sem quebrar a camada Quest nem as camadas web já existentes. A nova etapa **adiciona** filtros e recortes; não substitui priorização nem visão resumida.

---

## Ficheiros criados

### Web (Assets/Web/)

| Caminho | Descrição |
|---------|-----------|
| **Domain/EventCyclePortfolioFilter.cs** | FilterId, Name, RiskLevelFilter, PriorityLevelFilter, ClosureStatusFilter, ProducerStatusFilter, IncludeBlockedOnly, IncludeClosingSoonOnly, IncludeRecoveryOpportunitiesOnly, IncludeCompletedWithIssuesOnly, IncludeExecutionActiveOnly, Notes. |
| **Domain/EventCyclePortfolioFilteredViewState.cs** | ActiveFilter, FilteredItems, FilteredCount, FilterSummary, HighestPriorityFilteredItem, LastFilteredAt. |
| **Application/ActivePortfolioFilterStore.cs** | SetActiveFilter(filterId), GetActiveFilterId(), ClearFilter(), HasActiveFilter — em memória. |
| **Application/EventCyclePortfolioFilterController.cs** | GetAvailableFilters() — presets: AtRisk, Blocked, HighPriority, ClosingSoon, RecoveryOpportunity, CompletedWithIssues, ExecutionActive. ApplyFilter(priorityState, filter) — filtra itens com MatchesFilter(item, filter); devolve FilteredViewState com FilteredItems, FilteredCount, HighestPriorityFilteredItem. ApplyActiveFilterOrNone(priorityState) — usa ActivePortfolioFilterStore para aplicar o filtro activo ou nenhum. SetActiveFilter, ClearActiveFilter. |
| **Presentation/EventCyclePortfolioFilterPanel.cs** | Lista de filtros (botões); Limpar filtro; resumo do recorte; total filtrado; item mais crítico; última filtragem; lista de itens no recorte com ★ no prioritário. |
| **Presentation/EventCyclePortfolioFilterStripView.cs** | Filtro ativo: nome, total filtrado, item prioritário, timestamp; sem filtro: total na carteira. |

### Documentação

| Caminho | Descrição |
|---------|-----------|
| **Docs/Web/WEB_EVENT_CYCLE_PORTFOLIO_FILTERS.md** | Filtros executivos, recortes, gestão rápida, UI, limitações. |
| **Docs/Web/ETAPA_91_RESUMO.md** | Este resumo. |

---

## Ficheiros alterados

| Caminho | Alteração |
|---------|-----------|
| **Presentation/EventCycleDashboardPanel.cs** | filteredState = ApplyActiveFilterOrNone(portfolioState); quando ActiveFilter != null, linha "Filtro ativo: [nome] | N item(ns) | Crítico no recorte: [título] | timestamp". |
| **Presentation/HotelCRMDashboardController.cs** | Secções EventCyclePortfolioFilterStripView ("Filtros da carteira (resumo)") e EventCyclePortfolioFilterPanel ("Filtros executivos e recortes da carteira"). |

---

## Como os filtros da carteira foram implementados

1. **Presets**  
   GetAvailableFilters() devolve sete filtros com FilterId, Name e flags (IncludeBlockedOnly, IncludeClosingSoonOnly, etc.) e RiskLevelFilter/PriorityLevelFilter/ClosureStatusFilter quando aplicável.

2. **Predicado**  
   MatchesFilter(item, filter) aplica: IncludeBlockedOnly → RiskLevelLabel "Bloqueado"; IncludeClosingSoonOnly → razão/closure/estágio "final"; IncludeRecoveryOpportunitiesOnly → TopRecoveryAction e SimulatedBestScenarioDelta; IncludeCompletedWithIssuesOnly → closure concluído e score &lt; 70; IncludeExecutionActiveOnly → não concluído e não bloqueado; RiskLevelFilter AtRisk/Blocked; PriorityLevelFilter High (≥70).

3. **Vista filtrada**  
   ApplyFilter(priorityState, filter) filtra Items com MatchesFilter; ordena por PriorityScore e toma o primeiro como HighestPriorityFilteredItem; gera FilterSummary e LastFilteredAt. ApplyActiveFilterOrNone usa o FilterId do store para escolher o preset ou nenhum.

4. **Store**  
   ActivePortfolioFilterStore guarda o FilterId do filtro activo; a UI chama SetActiveFilter ao aplicar e ClearFilter ao limpar; o dashboard e o strip leem o store via ApplyActiveFilterOrNone.

5. **Strip, painel e dashboard**  
   Strip mostra se há filtro e, se sim, nome, total e prioritário. Painel mostra botões por filtro (activo em destaque), resumo, total, crítico, lista filtrada. Dashboard mostra uma linha condicional quando há filtro activo.

---

## Garantia de que Quest e camadas web existentes não foram quebradas

- **Quest** — Nenhuma alteração em QuestPresentation, QuestPresentationSession, controllers Quest, runtime XR ou apresentação.
- **Web Hotel** — CRM mantido; apenas duas novas secções (Strip e Panel de filtros) e uma linha condicional no dashboard. Visão resumida, priorização e restantes secções inalterados.
- **Web Produtor** — Painel do produtor e módulos operacionais não foram alterados.
- **Camada agregadora** — Os filtros **só consomem** EventCyclePortfolioPriorityState e aplicam predicados; não alteram priorização, overview nem outros módulos; não introduzem dependência circular.
