# Feature Specification: Filtro de Busca Avançada — FilterSidebar **Feature Branch**: `024-filtro-busca-avancada` **Created**: 2026-04-20 **Status**: Draft **Fonte**: Solicitação de melhoria UX no sidebar de filtros da página `/imoveis` --- ## Contexto O `FilterSidebar` da página `/imoveis` já existe e é funcional, mas apresenta três lacunas de usabilidade que impactam a descoberta de imóveis: 1. **Ausência de busca cross-categoria dentro do sidebar**: o usuário precisa abrir seção por seção para encontrar um tipo de imóvel ou bairro específico. 2. **Todas as seções abertas por padrão**: ao carregar a página, o sidebar está completamente expandido, gerando sobrecarga visual e exigindo scroll antes mesmo de ver os resultados. 3. **Listas longas sem hierarquia de popularidade**: bairros e tipos são listados em ordem arbitrária; itens raramente usados ocupam o mesmo espaço visual que os mais procurados, dificultando a seleção rápida. --- ## User Scenarios & Testing ### User Story 1 — Campo de Busca Cross-Categoria no Sidebar (Priority: P1) Um visitante que sabe exatamente o que procura (ex.: "Copacabana", "Cobertura") digita o termo no campo de busca do sidebar e vê instantaneamente em qual categoria aquela opção se enquadra, podendo selecioná-la com um clique sem precisar abrir seções manualmente. **Why this priority**: A busca cross-categoria resolve o maior obstáculo de navegação do sidebar: o usuário com intenção definida não sabe em qual seção procurar. É o ganho de UX mais alto com menor complexidade de implementação — nenhum dado novo de backend é necessário, pois os dados já chegam via `catalog.ts`. **Independent Test**: Digitar "Copa" no campo de busca do sidebar e verificar que aparece uma lista de sugestões agrupadas mostrando "Copacabana" sob o grupo "Bairro"; clicar em "Copacabana" e confirmar que o filtro de bairro é aplicado. **Acceptance Scenarios**: 1. **Given** a página `/imoveis` com o sidebar visível, **When** o usuário vê o topo do sidebar, **Then** um campo de busca com placeholder "Buscar filtro…" está disponível acima das seções accordion. 2. **Given** o campo de busca do sidebar com o valor "apar", **When** o usuário para de digitar por 200ms, **Then** uma lista de sugestões aparece inline (não em dropdown popup) mostrando entradas agrupadas, por exemplo: grupo "Tipo de imóvel" → "Apartamento". 3. **Given** sugestões de busca visíveis com múltiplas categorias correspondentes, **When** o usuário vê a lista, **Then** cada grupo tem um cabeçalho de categoria (ex.: "Tipo de imóvel", "Bairro", "Cidade") e os itens correspondentes abaixo. 4. **Given** uma sugestão visível, **When** o usuário clica nela, **Then** o filtro correspondente é aplicado (equivalente a selecionar o item na seção accordion), o campo de busca é limpo e a seção relevante é expandida para mostrar o item selecionado. 5. **Given** o campo de busca preenchido sem nenhuma correspondência, **When** a busca é executada, **Then** uma mensagem "Nenhum filtro encontrado para "[termo]"" é exibida no lugar das sugestões. 6. **Given** o campo de busca preenchido, **When** o usuário pressiona Escape ou limpa o campo, **Then** as sugestões são ocultadas e o estado das seções retorna ao normal. 7. **Given** a navegação por teclado com sugestões visíveis, **When** o usuário pressiona as teclas de seta (↓↑) e Enter, **Then** ele pode selecionar uma sugestão sem usar o mouse. --- ### User Story 2 — Seção de Preço Aberta por Padrão, Demais Colapsadas (Priority: P1) Um visitante que acaba de chegar na página `/imoveis` encontra o sidebar com uma experiência limpa: apenas a seção de preço está expandida, tornando o filtro mais importante imediatamente visível, enquanto as demais seções ficam colapsadas e acessíveis sob demanda. **Why this priority**: Juntamente com a busca cross-categoria, esta mudança tem impacto imediato na percepção de organização do sidebar sem exigir dados adicionais do backend. A seção de preço é o filtro de maior influência na decisão do usuário, justificando seu destaque inicial. **Independent Test**: Carregar `/imoveis` pela primeira vez (ou sem parâmetros de URL) e verificar que somente a seção "Preço" está expandida; todas as demais seções (Tipo, Quartos, Bairros, etc.) estão colapsadas. **Acceptance Scenarios**: 1. **Given** a página `/imoveis` carregada sem filtros ativos, **When** o sidebar renderiza, **Then** apenas a seção "Preço" está expandida (`defaultOpen = true`); todas as demais seções têm `defaultOpen = false`. 2. **Given** filtros ativos presentes na URL (ex.: `?city=1&bedrooms=2`), **When** o sidebar renderiza, **Then** as seções que contêm filtros ativos ficam automaticamente expandidas, além da seção de Preço. 3. **Given** o usuário colapsou manualmente a seção de Preço e navega para outra página e retorna, **When** o sidebar renderiza, **Then** o estado de colapso/expansão das seções volta ao padrão (Preço aberto, demais fechados), pois esse estado não é persistido. --- ### User Story 3 — Listas com "Ver mais" e Ordenação por Popularidade (Priority: P2) Um visitante que navega pelos filtros de bairro ou tipo de imóvel vê imediatamente os N itens mais relevantes (com mais imóveis disponíveis), pode expandir para ver todos com "Ver mais", e identifica visualmente os itens mais populares por meio de badges ou destaque. **Why this priority**: Listas longas sem hierarquia sobrecarregam a interface e enterram as opções mais úteis. Exibir os mais populares primeiro e truncar com "Ver mais" é o padrão de portais imobiliários líderes. Requer dados de contagem do backend, tornando-o de implementação mais complexa que as stories P1. **Independent Test**: Abrir a seção "Bairros" no sidebar e verificar que apenas os 5 bairros com mais imóveis são exibidos inicialmente, com badge "Popular" no primeiro; clicar em "Ver mais" e confirmar que todos os bairros são exibidos. **Acceptance Scenarios**: 1. **Given** uma seção de filtro com mais de 5 itens (ex.: Bairros), **When** o accordion é expandido, **Then** apenas os 5 primeiros itens são exibidos, ordenados do mais popular (mais imóveis associados) para o menos popular. 2. **Given** uma seção com mais de 5 itens exibindo os primeiros 5, **When** o usuário clica no botão "Ver mais (N)", **Then** todos os itens restantes são exibidos inline (sem modal ou navegação), e o botão muda para "Ver menos". 3. **Given** uma seção expandida com todos os itens visíveis, **When** o usuário clica em "Ver menos", **Then** a lista retorna a exibir apenas os 5 primeiros e o scroll retorna ao início da seção. 4. **Given** os itens de uma seção ordenados por popularidade, **When** o usuário vê a lista, **Then** os 3 itens com mais imóveis associados exibem um badge "Popular" ao lado do nome. 5. **Given** um filtro já selecionado que não está entre os 5 primeiros da lista, **When** o usuário reabre a seção com o filtro ativo, **Then** o item selecionado é exibido mesmo que esteja além dos 5 iniciais (o truncamento não oculta itens selecionados). 6. **Given** uma seção com 5 itens ou menos, **When** o accordion é expandido, **Then** o botão "Ver mais" não é exibido. --- ### Edge Cases - O que acontece quando a busca cross-categoria retorna o mesmo item em múltiplas categorias (ex.: cidade e bairro com o mesmo nome)? - Como o sistema lida com termos de busca contendo caracteres especiais (acentos, cedilha, hifens)? - O que acontece quando um filtro selecionado não aparece mais na lista porque o backend não o retornou (ex.: cidade removida do catálogo)? - Como o "Ver mais" se comporta quando um item selecionado está entre os ocultos — ele precisa estar visível mesmo sem clicar em "Ver mais"? - O que acontece se os dados de popularidade (contagem de imóveis por bairro/tipo) ainda estão carregando quando o accordion é expandido? - Como a seção de preço se comporta quando o parâmetro `listing_type` muda entre "venda" e "aluguel" (os presets já mudam — o estado de expansão da seção é preservado)? - Qual é o comportamento do campo de busca do sidebar quando o catálogo ainda está carregando (`catalogLoading = true`)? --- ## Requirements ### Functional Requirements #### Busca Cross-Categoria no Sidebar - **FR-001**: O `FilterSidebar` DEVE exibir um campo de busca textual no topo, acima de todas as seções accordion, com placeholder "Buscar filtro…". - **FR-002**: O campo de busca DEVE pesquisar simultaneamente em todas as categorias de filtro disponíveis: tipos de imóvel, cidades, bairros e comodidades. - **FR-003**: A busca DEVE aplicar debounce de 200ms antes de exibir resultados, para não travar a interface durante digitação rápida. - **FR-004**: Os resultados DEVEM ser apresentados agrupados por categoria, com o cabeçalho de cada grupo claramente identificado (ex.: "Tipo de imóvel", "Bairro", "Cidade", "Comodidade"). - **FR-005**: A busca DEVE ser case-insensitive e ignorar acentuação para maximizar correspondências (ex.: "copacabana" deve encontrar "Copacabana", "apto" deve encontrar "Apartamento"). - **FR-006**: Ao clicar em uma sugestão, o filtro correspondente DEVE ser aplicado imediatamente, o campo de busca DEVE ser limpo e a seção do accordion correspondente DEVE ser expandida. - **FR-007**: Quando `catalogLoading = true`, o campo de busca DEVE estar desabilitado com estado visual de loading (cursor não permitido, opacidade reduzida). - **FR-008**: A navegação por teclado (↑↓ para mover entre sugestões, Enter para selecionar, Escape para fechar) DEVE ser suportada para acessibilidade. #### Estado Inicial das Seções - **FR-009**: Ao carregar o `FilterSidebar` sem filtros pré-ativos na URL, apenas a seção "Preço" DEVE ter `defaultOpen = true`; todas as demais seções DEVEM ter `defaultOpen = false`. - **FR-010**: Quando filtros pré-ativos existem na URL, as seções que contêm esses filtros ativos DEVEM ser inicialmente expandidas além da seção de Preço. - **FR-011**: O estado de expansão das seções NÃO DEVE ser persistido entre sessões — cada carregamento de página retorna ao estado padrão. #### Listas com Truncamento e Popularidade - **FR-012**: Seções com mais de 5 itens DEVEM exibir apenas os 5 primeiros inicialmente, com um botão "Ver mais (N)" indicando quantos itens adicionais existem. - **FR-013**: Os itens de cada seção DEVEM ser ordenados por popularidade, definida como a contagem de imóveis ativos associados àquele item (ex.: número de imóveis em cada bairro, número de imóveis de cada tipo). - **FR-014**: Os 3 itens mais populares de cada seção DEVEM exibir um badge "Popular" ao lado do label. - **FR-015**: Itens com filtro ativo (selecionados) DEVEM sempre ser visíveis, independentemente de estarem entre os 5 primeiros ou não. - **FR-016**: O backend DEVE fornecer dados de contagem de imóveis por categoria (tipo de imóvel, cidade, bairro) para permitir ordenação por popularidade na camada de catálogo. - **FR-017**: O botão "Ver mais" DEVE ter comportamento toggle: ao ser clicado novamente, exibe "Ver menos" e retorna a lista ao truncamento inicial. ### Requisitos Não-Funcionais - **NFR-001**: A busca cross-categoria DEVE executar inteiramente no cliente (sem chamadas adicionais à API), utilizando os dados de catálogo já carregados. - **NFR-002**: A animação de expansão/colapso das sugestões de busca DEVE seguir a mesma curva de animação das seções accordion existentes (CSS grid trick, `duration-200 ease-out`). - **NFR-003**: O visual de todos os elementos novos (campo de busca, badges, botão "Ver mais") DEVE seguir os design tokens do projeto: `textTertiary`, `textSecondary`, `borderSubtle`, `borderStandard`, `surface`, `brand`. - **NFR-004**: Nenhuma alteração visual ou de comportamento DEVE impactar o funcionamento dos filtros existentes (os filtros aplicados via accordion continuam funcionando normalmente). - **NFR-005**: A contagem de imóveis por categoria retornada pelo backend DEVE ser calculada apenas sobre imóveis ativos e disponíveis, sem expor dados de imóveis inativos. ### Key Entities - **CatalogItem com Contagem**: Extensão das entidades de catálogo existentes (tipo de imóvel, cidade, bairro) com o campo `property_count` indicando quantos imóveis ativos estão associados. - **SugestãoDeFiltro**: Resultado da busca cross-categoria, composto por `category` (grupo), `label` (texto exibido), `value` (identificador) e `filterKey` (chave do filtro a ser aplicado em `PropertyFilters`). - **EstadoDeExpansãoDaSeção**: Mapeamento interno de `sectionKey → boolean` controlando quais seções do accordion estão abertas; inicializado a partir dos filtros ativos da URL. --- ## Success Criteria ### Measurable Outcomes - **SC-001**: Usuários com intenção definida (sabem o bairro ou tipo que buscam) conseguem aplicar o filtro desejado em menos de 3 interações a partir do campo de busca do sidebar. - **SC-002**: A seção de preço é a primeira coisa interativa que o usuário vê ao abrir o sidebar em 100% dos carregamentos sem filtros pré-ativos. - **SC-003**: Em seções com mais de 5 itens, os 5 exibidos inicialmente cobrem pelo menos 70% do volume de imóveis disponíveis (validação pelo ordenamento por popularidade). - **SC-004**: O tempo de resposta do campo de busca cross-categoria (desde o fim do debounce até a exibição das sugestões) é imperceptível para o usuário (abaixo de 50ms, pois é processamento local). - **SC-005**: Nenhum filtro previamente funcional quebra após a implementação — todos os cenários de aceitação da spec `023-ux-melhorias-imoveis` continuam passando. --- ## Assumptions - Os dados de catálogo (tipos, cidades, bairros, comodidades) já estão disponíveis via `catalog.ts` no momento em que o `FilterSidebar` renderiza; não há nova chamada de API necessária para a busca cross-categoria. - A ordenação por popularidade requer uma única adição ao endpoint de catálogo existente (`property_count` por item), não um endpoint separado. - O número de itens por seção raramente ultrapassa 20–30 em produção para este SaaS (portfólio de uma imobiliária), tornando o processamento de busca local viável sem paginação. - O estado de expansão das seções não será persistido em `localStorage` nesta versão; a persistência pode ser adicionada em iteração futura se houver demanda. - O badge "Popular" é puramente visual e não afeta a lógica de filtragem. - A busca cross-categoria opera sobre dados do catálogo (tipos, cidades, bairros, comodidades) e não sobre dados de imóveis individuais (endereços, códigos) — a busca por endereço/código já está coberta pelo campo de busca global da feature `023`. --- ## Fora do Escopo - Salvar buscas favoritas ou histórico de filtros por usuário. - Busca cross-categoria que inclua imóveis individuais (por código, endereço ou título) — isso é responsabilidade do campo de busca global já especificado em `023-ux-melhorias-imoveis`. - Ordenação de resultados de imóveis por popularidade de bairro (apenas os filtros do sidebar são ordenados por popularidade, não os cards de imóveis). - Criação de um sistema completo de analytics de filtros — `property_count` é suficiente como proxy de popularidade nesta versão. - Filtros de busca cross-categoria em tela mobile (sheet/modal) — este spec cobre apenas o sidebar em desktop; a experiência mobile é tratada em feature separada. - Internacionalização dos labels de categoria ou dos badges.