mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 17:43:04 +00:00
Deliver auto-applied filters on Praças list and close filtros-auto fill-in.
Remove the Buscar step with debounced search, immediate select navigation, and pending UI; update roadmap, plan, and notebook.
This commit is contained in:
parent
7fe45ea963
commit
248f896d05
6 changed files with 119 additions and 32 deletions
|
|
@ -7,6 +7,7 @@ alwaysApply: false
|
|||
|
||||
## Status
|
||||
|
||||
- **Fill-in filtros-auto entregue em código (2026-07-21; deploy pendente com remodelagem):** debounce 1s + Enter imediato na busca de `/campanha/pracas`; remove Buscar; pending UI alinhado a `ActionPlanFilters`; `shouldUpdatePlazaSearchUrl` + no-op `buildPlazaFiltersKey`. Plano: [`filtros-auto-pracas.md`](../../../docs/plans/filtros-auto-pracas.md). Débitos pós-`/simplify` (sync back/forward, shell pending) → gatilhos no plano.
|
||||
- **B10 entregue em código (2026-07-21; deploy pendente com remodelagem):** hover/tap no Mapa das Praças — destaque + `MapFeatureReadout`; desktop click navega; mobile 2º tap; SSA/CMS N>1 foca `zoneBreakdown`; `plazasByIbgeCode` / `resolvePlazaMapNavigation`. Plano: [`hover-mapa-pracas.md`](../../../docs/plans/hover-mapa-pracas.md). Débito perf hover O(n) → **B6** [`escala-dry-pos-b3.md`](../../../docs/plans/escala-dry-pos-b3.md).
|
||||
- **B9 entregue em código (2026-07-21; deploy pendente com remodelagem):** edição rápida na lista — Assessores / Tendência / `expectedVotes` via `PlazaList*Control` + `listFormActions`; sem migration. Plano: [`edicao-rapida-lista-pracas.md`](../../../docs/plans/edicao-rapida-lista-pracas.md). Débitos `/simplify` → **A9+** F2 + **C8** F4.
|
||||
- **A9 entregue em código (2026-07-21; deploy pendente com remodelagem):** `plaza.expectedVotes` staff-only, fallback `expectedVotes ?? effectiveTotal` em mapa 2026/overview/dashboard, UI `/editar` + leitura lista/detalhe; migration `20260721_133444_add_plaza_expected_votes`. Plano: [`estimativa-votos-praca.md`](../../../docs/plans/estimativa-votos-praca.md). Fill-in pós-`/simplify`: **A9+** [`escala-dry-pos-a9.md`](../../../docs/plans/escala-dry-pos-a9.md) (loader compartilhado + revalidate escopada pós-B9).
|
||||
|
|
@ -123,6 +124,6 @@ alwaysApply: false
|
|||
- Em 2026-07-17 o roadmap foi repriorizado orientado ao calendário eleitoral (Res. TSE 23.760/2026: propaganda a partir de 16/08, 1º turno 04/10), com ordem de execução, trilhas paralelas (A dados eleitorais/território, B superfícies de coordenação, C operação de campo, D plataforma) e grafo de dependências. Decisões de sequenciamento: lançar o MVP antes da migração multi-município; multi-município antes de zonas-por-municipio/eventos/demandas; baseline TSE independente de zonas-por-municipio (dependência rebaixada a suave); WhatsApp Business API movido a fora de escopo (vedação Meta + Res. 23.610 art. 33).
|
||||
- Backlog consolidado do produto (admin RBAC, mapa, eventos, demandas, apoiadores, listas globais, site, white-label): [`docs/roadmap.md`](../../../docs/roadmap.md).
|
||||
- **B7 entregue (2026-07-21):** mapa em `/campanha/pracas` usa `buildPlazaListWhere` (lista/overview/mapa alinhados). Plano: [`docs/plans/mapa-pracas-filtrado.md`](../../../docs/plans/mapa-pracas-filtrado.md). Débito perf triplicado → **A9+**.
|
||||
- **A9 / B9 / B7 / B10 entregues (2026-07-21; deploy pendente):** ver bullets no topo do Status. Próximos abertos Janela 1: **B11**, **R6**, filtros-auto, deploy+smoke remodelagem; Janela 3: **B6** (setStyle incremental pós-B10), **B8**.
|
||||
- **A9 / B9 / B7 / B10 / filtros-auto entregues (2026-07-21; deploy pendente):** ver bullets no topo do Status. Próximos abertos Janela 1: **B11**, **R6**, deploy+smoke remodelagem; Janela 3: **B6** (setStyle incremental pós-B10), **B8**.
|
||||
- **B8 registrado (2026-07-21):** polígonos das Praças-zona Salvador/Camaçari. Pesquisa: SSA tem lista oficial TRE-BA zona↔bairro (RA 02/2017); CMS **não** — só locais de votação por ZE 170/171 + curadoria; TSE/TRE não publicam polígonos de ZE. Plano: F1 catálogo+UI na Praça; F2 dissolve malha de bairros (IBGE/GeoSalvador) → TopoJSON. Janela 3; F2 cortável. Plano: [`docs/plans/poligonos-pracas-zona.md`](../../../docs/plans/poligonos-pracas-zona.md).
|
||||
- **B11 registrado (2026-07-21):** opção de escala no Mapa das Praças — cor por % dos votos válidos da Praça (0–100% fixos; denominador `electionTally.votosValidos`; 2026 usa válidos 2022); absoluto permanece. Plano: [`docs/plans/escala-percentual-mapa-pracas.md`](../../../docs/plans/escala-percentual-mapa-pracas.md).
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
# Filtros auto-aplicados na lista de Praças
|
||||
|
||||
Status: rascunho
|
||||
Status: entregue em código (2026-07-21; deploy pendente com remodelagem)
|
||||
Atualizado em: 2026-07-21
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins)
|
||||
Impeccable: B — encaixe em `PlazaFilters` (`/campanha/pracas`); sem rota nova
|
||||
Appetite: ~0,5 dia eng; só client em `PlazaFilters` (+ pending a11y); sem migration
|
||||
Responsável: —
|
||||
|
||||
_Revisão 2026-07-21 (pós-implementação + `/simplify` + capture-review-debts): debounce 1s + Enter imediato + pending UI; `commitNavigation` com `buildPlazaFiltersKey` no-op; `shouldUpdatePlazaSearchUrl` em `plazaUi.ts`; testes `tests/unit/plazaUi.unit.spec.ts`. Débitos S1–S2 registrados em Adiado (não reabrir no simplify)._
|
||||
|
||||
## Design (Impeccable)
|
||||
|
||||
Âncoras: `PRODUCT.md` / `DESIGN.md` (register product — Field Desk) · tema `data-theme='campaign'` · shells existentes (`CampaignPageShell`, `CampaignSearchInput`, `PlazaFilters`).
|
||||
|
|
@ -78,7 +80,7 @@ Componentes:
|
|||
## Dependências
|
||||
|
||||
- **Dura:** nenhuma de outro plano (R2 já entregue).
|
||||
- **Suave:** B7 ([mapa-pracas-filtrado.md](mapa-pracas-filtrado.md)) — debounce aumenta frequência de troca de filtro; mapa filtrado fica mais útil, mas este item funciona com o mapa ainda “all accessible”.
|
||||
- **Suave:** B7 ([mapa-pracas-filtrado.md](mapa-pracas-filtrado.md)) ✓ entregue — cada replace de filtro também refresca o mapa filtrado.
|
||||
- Reusa: `plazaUi.ts` (`buildPlazaListHref`, `PlazaListState`), padrão pending de `ActionPlanFilters.tsx`, dwell do `RecentVisitTracker` (já re-arma o timer a cada `href` — debounce 1s + dwell ~2s evita visitar intermediários).
|
||||
|
||||
## Não escopo
|
||||
|
|
@ -94,8 +96,18 @@ Componentes:
|
|||
- **Debounce via lib (`use-debounce` etc.).** Dependência nova por 10 linhas. **Mitigação:** `setTimeout` + cleanup.
|
||||
- **Otimistic list client-side.** Duplicaria `buildPlazaListWhere` no browser. **Mitigação:** URL + RSC como hoje.
|
||||
|
||||
## Já resolvido no simplify/critique (não reabrir)
|
||||
|
||||
- Alias `plazaSearchParamFromInput` removido; `normalizedText` + `shouldUpdatePlazaSearchUrl` em `plazaUi.ts`.
|
||||
- No-op de navegação via `buildPlazaFiltersKey` (não dois `buildPlazaListHref`).
|
||||
- `hasActiveFilters` inclui texto local de busca (Limpar visível antes do debounce).
|
||||
- `clearDebounce` duplicado no `onSubmit` removido.
|
||||
- Impeccable `detect.mjs` no alvo: 0 findings (sem critique formal nesta sessão).
|
||||
|
||||
## Adiado com gatilho
|
||||
|
||||
- **Sync `search` local ↔ `state.q` no back/forward do browser.** `key={buildPlazaFiltersKey(state)}` remonta na maioria dos casos; edge case: campo pode mostrar texto stale após voltar/avançar. **Gatilho:** QA ou R6 reportar desync em `/campanha/pracas`.
|
||||
- **Shell compartilhado de pending UI** (`data-pending` + `aria-busy` + `aria-live` "Atualizando resultados…"). Hoje só `ActionPlanFilters` + `PlazaFilters` (`SupporterFilters` ainda sem pending). **Gatilho:** 3ª lista de campanha com o mesmo padrão pending, ou R6/critique citar duplicação em ≥2 superfícies.
|
||||
- **Mesmo padrão em `SupporterFilters` (e outras listas com Buscar).** Revisitar quando produto pedir ou quando R6/critique citar “Buscar” como atrito repetido em ≥2 listas.
|
||||
- **Extrair `useDebouncedSearchParam`.** Revisitar no 3º call site idêntico (Praças + apoiadores + 1).
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Roadmap — Teqo
|
||||
|
||||
Atualizado em: 2026-07-21 (B9 + B10 entregues; débitos B9 → A9+/C8 F4; débitos B10 → B6)
|
||||
Atualizado em: 2026-07-21 (fill-in filtros-auto entregue em código; B9 + B10 entregues; débitos B9 → A9+/C8 F4; débitos B10 → B6)
|
||||
|
||||
Registro canônico dos **próximos** planos e débitos. Histórico de entregas: resumo abaixo + planos em [`docs/plans/`](plans/) + notebook [`.cursor/rules/projects/nucleos-eleitorais.mdc`](../.cursor/rules/projects/nucleos-eleitorais.mdc).
|
||||
|
||||
|
|
@ -81,11 +81,11 @@ flowchart TD
|
|||
C2prod --> C5["C5 GOTV (validar)"]
|
||||
```
|
||||
|
||||
Paralelizáveis agora: **B11**, fill-ins da lista (filtros-auto, A9+, …). ~~**A9** / **B9** / **B7** / **B10**~~ entregues 2026-07-21. **B6** absorve o hot path de hover pós-B10 (Janela 3 / gatilho de densidade).
|
||||
Paralelizáveis agora: **B11**, fill-ins da lista (A9+, …). ~~**A9** / **B9** / **B7** / **B10** / **filtros-auto**~~ entregues 2026-07-21. **B6** absorve o hot path de hover pós-B10 (Janela 3 / gatilho de densidade).
|
||||
|
||||
### Sequência por janela
|
||||
|
||||
**Janela 1 — agora → 05/08 (convenções):** ~~R0 → R1 → R2~~ entregues; ~~**A9** estimativa de votos~~ / ~~**B9** edição rápida na lista~~ / ~~**B7** mapa filtrado~~ / ~~**B10** hover+click-nav~~ entregues; **deploy da remodelagem** (revisar SQL destrutivo da migração antes do build) + smoke em produção; **B11** escala % dos válidos; R6 critique/polish; Onda 0 jurídica em paralelo.
|
||||
**Janela 1 — agora → 05/08 (convenções):** ~~R0 → R1 → R2~~ entregues; ~~**A9** estimativa de votos~~ / ~~**B9** edição rápida na lista~~ / ~~**B7** mapa filtrado~~ / ~~**B10** hover+click-nav~~ / ~~**filtros-auto** lista de Praças~~ entregues; **deploy da remodelagem** (revisar SQL destrutivo da migração antes do build) + smoke em produção; **B11** escala % dos válidos; R6 critique/polish; Onda 0 jurídica em paralelo.
|
||||
|
||||
**Janela 2 — 05/08 → 16/08 (pré-propaganda):** C2 dados reais assim que o jurídico liberar; D2 se sobrar folga.
|
||||
|
||||
|
|
@ -108,6 +108,7 @@ Paralelizáveis agora: **B11**, fill-ins da lista (filtros-auto, A9+, …). ~~**
|
|||
- **B7 (2026-07-21)** — **Mapa das Praças filtrado pela lista** (`buildPlazaListWhere` em `loadPlazaMapBundle`; `rawSearchParams` na página; empty → omitir painel; int `plazaMapData.int.spec.ts`) — [plano](plans/mapa-pracas-filtrado.md).
|
||||
- **B9 (2026-07-21)** — **Edição rápida na lista de Praças** (Assessores / Tendência / `expectedVotes` via Popovers em `PlazaList*Control`; `listFormActions`; sem migration) — [plano](plans/edicao-rapida-lista-pracas.md). Débitos `/simplify`: **A9+** F2 (revalidate) + **C8** F4 (DRY formActions).
|
||||
- **B10 (2026-07-21)** — **Hover/tap no Mapa das Praças** (destaque + `MapFeatureReadout`; desktop click navega; mobile 2º tap; SSA/CMS N>1 → `zoneBreakdown`; `plazasByIbgeCode` / `resolvePlazaMapNavigation`) — [plano](plans/hover-mapa-pracas.md). Débito perf hover O(n) → **B6** [escala-dry-pos-b3.md](plans/escala-dry-pos-b3.md).
|
||||
- **Fill-in filtros-auto (2026-07-21)** — **Filtros auto-aplicados na lista de Praças** (`PlazaFilters`: debounce 1s no `q`, Enter imediato, selects imediatos, remove Buscar; `useTransition` + pending a11y; `shouldUpdatePlazaSearchUrl` + no-op via `buildPlazaFiltersKey`; sem migration) — [plano](plans/filtros-auto-pracas.md). Débitos pós-`/simplify`: sync back/forward `search`↔`state.q` e shell pending compartilhado — gatilhos no plano.
|
||||
|
||||
## Supersedidos pela remodelagem (2026-07-20)
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ Paralelizáveis agora: **B11**, fill-ins da lista (filtros-auto, A9+, …). ~~**
|
|||
- **C5** operação dia D / GOTV _(validar com produto)_ · design [`Dia-D-GOTV`](design-refs/latest/Dia-D-GOTV.png) · depende de C2 dados reais
|
||||
- **D2** push + sino in-app · soft: chave `campanha-notificacoes-push` (Onda 0) · [plano](plans/notifications.md)
|
||||
- **R6** critique/polish visual da vertical remodelada (ciclo /impeccable completo por superfície; smoke visual coordenador feito em 2026-07-21) · gatilho: antes de 16/08 · (opcional: mover filtros acima do mapa)
|
||||
- **Fill-ins:** filtros auto na lista de Praças (debounce 1s no `q`, selects já imediatos; remove Buscar) · [plano](plans/filtros-auto-pracas.md) · **A9+** loader compartilhado lista/mapa + revalidate escopada pós-B9 ([plano](plans/escala-dry-pos-a9.md)) · **C8** F4 DRY `listFormActions`↔`/editar` ([plano](plans/escala-dry-pos-c6.md)) · O0+ ([plano](plans/escala-dry-pos-onda0.md)) · RS+ auth read leve + shells de senha ([plano](plans/escala-dry-pos-reset-senha-perfil.md)) · higiene PascalCase
|
||||
- **Fill-ins:** **A9+** loader compartilhado lista/mapa + revalidate escopada pós-B9 ([plano](plans/escala-dry-pos-a9.md)) · **C8** F4 DRY `listFormActions`↔`/editar` ([plano](plans/escala-dry-pos-c6.md)) · O0+ ([plano](plans/escala-dry-pos-onda0.md)) · RS+ auth read leve + shells de senha ([plano](plans/escala-dry-pos-reset-senha-perfil.md)) · higiene PascalCase
|
||||
|
||||
## Bloqueadores atuais
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,26 @@
|
|||
'use client'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useRef, useState, useTransition } from 'react'
|
||||
|
||||
import { CampaignSearchInput } from '@/components/campaign/CampaignSearchInput'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Field, FieldLabel } from '@/components/ui/field'
|
||||
import { NativeSelect, NativeSelectOption } from '@/components/ui/native-select'
|
||||
import { bahiaIdentityTerritories } from '@/lib/bahiaTerritories'
|
||||
import { normalizedText } from '@/utilities/campaignListUrl'
|
||||
import {
|
||||
buildPlazaFiltersKey,
|
||||
buildPlazaListHref,
|
||||
plazaKindLabels,
|
||||
plazaListCoverageLabels,
|
||||
politicalTrendLabels,
|
||||
shouldUpdatePlazaSearchUrl,
|
||||
type PlazaListState,
|
||||
} from '@/utilities/plazaUi'
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 1000
|
||||
|
||||
type PlazaFiltersProps = {
|
||||
state: PlazaListState
|
||||
showStaffFilters: boolean
|
||||
|
|
@ -24,31 +29,83 @@ type PlazaFiltersProps = {
|
|||
export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) => {
|
||||
const router = useRouter()
|
||||
const [search, setSearch] = useState(state.q ?? '')
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
|
||||
const navigate = (next: PlazaListState) => {
|
||||
router.replace(buildPlazaListHref({ ...next, page: 1 }, 1), { scroll: false })
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const clearDebounce = () => {
|
||||
if (debounceRef.current) {
|
||||
clearTimeout(debounceRef.current)
|
||||
debounceRef.current = null
|
||||
}
|
||||
}
|
||||
|
||||
const commitNavigation = (patch: Partial<PlazaListState>) => {
|
||||
clearDebounce()
|
||||
const merged: PlazaListState = {
|
||||
...state,
|
||||
...patch,
|
||||
page: 1,
|
||||
q: normalizedText(patch.q !== undefined ? patch.q : search),
|
||||
}
|
||||
if (buildPlazaFiltersKey(merged) === buildPlazaFiltersKey({ ...state, page: 1 })) return
|
||||
|
||||
startTransition(() => {
|
||||
router.replace(buildPlazaListHref(merged, 1), { scroll: false })
|
||||
})
|
||||
}
|
||||
|
||||
const scheduleSearchNavigation = (value: string) => {
|
||||
clearDebounce()
|
||||
if (!shouldUpdatePlazaSearchUrl(value, state.q)) return
|
||||
|
||||
debounceRef.current = setTimeout(() => {
|
||||
debounceRef.current = null
|
||||
commitNavigation({ q: value })
|
||||
}, SEARCH_DEBOUNCE_MS)
|
||||
}
|
||||
|
||||
const hasActiveFilters = Boolean(
|
||||
state.q || state.region || state.kind || state.coverage || state.priority || state.trend,
|
||||
state.q ||
|
||||
normalizedText(search) ||
|
||||
state.region ||
|
||||
state.kind ||
|
||||
state.coverage ||
|
||||
state.priority ||
|
||||
state.trend,
|
||||
)
|
||||
|
||||
return (
|
||||
<form
|
||||
role="search"
|
||||
className="flex flex-col gap-3"
|
||||
className="flex flex-col gap-3 transition-opacity data-[pending=true]:opacity-70"
|
||||
data-pending={isPending}
|
||||
aria-busy={isPending}
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
navigate({ ...state, q: search.trim() || undefined })
|
||||
commitNavigation({ q: search })
|
||||
}}
|
||||
>
|
||||
<p className="sr-only" aria-live="polite">
|
||||
{isPending ? 'Atualizando resultados…' : ''}
|
||||
</p>
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-end">
|
||||
<CampaignSearchInput
|
||||
id="plaza-search"
|
||||
label="Buscar Praça"
|
||||
placeholder="Buscar por município ou zona…"
|
||||
value={search}
|
||||
onChange={(event) => setSearch(event.target.value)}
|
||||
onChange={(event) => {
|
||||
const value = event.target.value
|
||||
setSearch(value)
|
||||
scheduleSearchNavigation(value)
|
||||
}}
|
||||
/>
|
||||
<Field className="md:w-56">
|
||||
<FieldLabel htmlFor="plaza-filter-region">Território de identidade</FieldLabel>
|
||||
|
|
@ -56,8 +113,7 @@ export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) =>
|
|||
id="plaza-filter-region"
|
||||
value={state.region ?? ''}
|
||||
onChange={(event) =>
|
||||
navigate({
|
||||
...state,
|
||||
commitNavigation({
|
||||
region: (event.target.value || undefined) as PlazaListState['region'],
|
||||
})
|
||||
}
|
||||
|
|
@ -77,8 +133,7 @@ export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) =>
|
|||
id="plaza-filter-kind"
|
||||
value={state.kind ?? ''}
|
||||
onChange={(event) =>
|
||||
navigate({
|
||||
...state,
|
||||
commitNavigation({
|
||||
kind: (event.target.value || undefined) as PlazaListState['kind'],
|
||||
})
|
||||
}
|
||||
|
|
@ -100,8 +155,7 @@ export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) =>
|
|||
id="plaza-filter-coverage"
|
||||
value={state.coverage ?? ''}
|
||||
onChange={(event) =>
|
||||
navigate({
|
||||
...state,
|
||||
commitNavigation({
|
||||
coverage: (event.target.value || undefined) as PlazaListState['coverage'],
|
||||
})
|
||||
}
|
||||
|
|
@ -125,8 +179,7 @@ export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) =>
|
|||
id="plaza-filter-trend"
|
||||
value={state.trend ?? ''}
|
||||
onChange={(event) =>
|
||||
navigate({
|
||||
...state,
|
||||
commitNavigation({
|
||||
trend: (event.target.value || undefined) as PlazaListState['trend'],
|
||||
})
|
||||
}
|
||||
|
|
@ -148,8 +201,7 @@ export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) =>
|
|||
id="plaza-filter-priority"
|
||||
value={state.priority ?? ''}
|
||||
onChange={(event) =>
|
||||
navigate({
|
||||
...state,
|
||||
commitNavigation({
|
||||
priority: event.target.value === 'alta' ? 'alta' : undefined,
|
||||
})
|
||||
}
|
||||
|
|
@ -161,24 +213,24 @@ export const PlazaFilters = ({ state, showStaffFilters }: PlazaFiltersProps) =>
|
|||
</Field>
|
||||
</>
|
||||
) : null}
|
||||
<div className="flex gap-2">
|
||||
<Button type="submit" variant="secondary" className="min-h-11">
|
||||
Buscar
|
||||
</Button>
|
||||
{hasActiveFilters ? (
|
||||
{hasActiveFilters ? (
|
||||
<div className="flex shrink-0 gap-2 md:self-end">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
className="min-h-11"
|
||||
onClick={() => {
|
||||
clearDebounce()
|
||||
setSearch('')
|
||||
router.replace('/campanha/pracas', { scroll: false })
|
||||
startTransition(() => {
|
||||
router.replace('/campanha/pracas', { scroll: false })
|
||||
})
|
||||
}}
|
||||
>
|
||||
Limpar
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -158,6 +158,11 @@ export const buildPlazaFiltersKey = (state: PlazaListState): string =>
|
|||
export const buildPlazaListHref = (state: PlazaListState, page: number): string =>
|
||||
buildListHref(state, buildPlazaListSearchParams, '/campanha/pracas', page)
|
||||
|
||||
export const shouldUpdatePlazaSearchUrl = (
|
||||
input: string,
|
||||
currentQ: string | undefined,
|
||||
): boolean => normalizedText(input) !== currentQ
|
||||
|
||||
export const resolvePlazaListUrl = (
|
||||
params: PlazaListSearchParams,
|
||||
totalPages?: number,
|
||||
|
|
|
|||
16
tests/unit/plazaUi.unit.spec.ts
Normal file
16
tests/unit/plazaUi.unit.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { shouldUpdatePlazaSearchUrl } from '@/utilities/plazaUi'
|
||||
|
||||
describe('shouldUpdatePlazaSearchUrl', () => {
|
||||
it('returns false when canonical q matches the current URL q', () => {
|
||||
expect(shouldUpdatePlazaSearchUrl('salvador', 'salvador')).toBe(false)
|
||||
expect(shouldUpdatePlazaSearchUrl(' salvador ', 'salvador')).toBe(false)
|
||||
})
|
||||
|
||||
it('returns true when q differs or is newly set', () => {
|
||||
expect(shouldUpdatePlazaSearchUrl('salv', 'salvador')).toBe(true)
|
||||
expect(shouldUpdatePlazaSearchUrl('salvador', undefined)).toBe(true)
|
||||
expect(shouldUpdatePlazaSearchUrl('', 'salvador')).toBe(true)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue