mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
Merge pull request #325 from fsolla/cursor/pool-i306-4a43a35f-79fc
B138 — Atividades: presets e busca na omnibox; remover abas
This commit is contained in:
commit
43dcd8da9b
6 changed files with 252 additions and 75 deletions
67
docs/plans/atividades-omnibox-presets-busca-impl.md
Normal file
67
docs/plans/atividades-omnibox-presets-busca-impl.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Impl: Atividades — presets (Próximos/…) e busca na omnibox; remover abas (B138)
|
||||
|
||||
Status: aprovado
|
||||
Atualizado em: 2026-08-02
|
||||
Issue: #306
|
||||
Intenção: docs/plans/atividades-omnibox-presets-busca.md
|
||||
Appetite restante: herdado (~0,5–1 dia eng)
|
||||
|
||||
## Leitura da intenção
|
||||
|
||||
- **Outcome:** Uma barra omnibox na lista de atividades — presets de janela (Próximos/Todos/Realizados/Rascunhos) e busca por texto como chips/sugestões; seletor de abas acima some; default Próximos intacto.
|
||||
- **O que NÃO negociar:** semântica de Próximos (planejado/confirmado + startAt ≥ agora); leader lockdown inalterado; deep-links `?tab=` preservados; tipo/município/status (em Todos) na omnibox.
|
||||
- **O que reavaliar:** hipótese de mover preset para dentro da omnibox — confirmada; não precisa de mudança no chassis `CampaignListOmnibox`.
|
||||
|
||||
## Abordagem recomendada
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
URL["URL ?tab=&q=&kind=&…"] --> parse["parseActivityListParams"]
|
||||
parse --> where["buildActivityListWhere"]
|
||||
parse --> omnibox["activityOmnibox adapter"]
|
||||
omnibox --> UI["ActivityFilters → CampaignListOmnibox"]
|
||||
```
|
||||
|
||||
**Opções consideradas:**
|
||||
|
||||
- A) Tab como chip de apresentação (não-URL, como scenario em municípios)
|
||||
- B) Tab permanece em `?tab=` com chip/sugestões na omnibox
|
||||
- C) Tab vira filtro de status composto (rejeitado pelo produto)
|
||||
|
||||
**Recomendação:** B — mantém deep-links, reusa `activityUi` existente; só muda superfície.
|
||||
**Rejeitadas:** A (quebra bookmarks `?tab=realizados`); C (perde modo diário nomeado).
|
||||
|
||||
### Componentes / mudanças
|
||||
|
||||
- **`activityUi.ts`**: adicionar `q` ao estado/URL; `buildActivityListWhere` com OR `title` + `responsible.name`.
|
||||
- **`activityOmnibox.ts`**: seeds grupo `Janela` (`tab:*`); chip de janela quando `tab !== proximos`; chip `Busca:`; apply/remove/clear para `q` e `tab`.
|
||||
- **`ActivityFilters.tsx`**: remover `ActivityTabSwitch`; wire `onCommitQuery`; placeholder atualizado.
|
||||
- **Migration:** sem migration.
|
||||
- **Access / Consent:** inalterado (staff-only lista).
|
||||
- **UI:** Impeccable B — espelhar `SupporterFilters` (uma barra, chips, pending boundary existente).
|
||||
|
||||
### Dados → forma
|
||||
|
||||
- Chip de janela mostra `activityTabLabels[tab]` (ex. "Realizados"), não status cru.
|
||||
- Busca: título + nome do responsável (já no card; precedente `contact.name` em leadership).
|
||||
|
||||
## Fases verificáveis
|
||||
|
||||
1. **Server/URL** — `activityUi` + testes unitários (`q`, where OR).
|
||||
2. **Omnibox adapter + UI** — `activityOmnibox`, `ActivityFilters`; testes B128 estendidos.
|
||||
3. **Gates** — `pnpm gate:fast`; push via `pnpm push`.
|
||||
|
||||
## Rabbit holes / Não escopo (engenharia)
|
||||
|
||||
- Saved filters B18; intervalo de datas genérico; mudanças em outras listas.
|
||||
- Novo arquivo `activityOmnibox.unit.spec.ts` — estender `listOmniboxB128` + `activityUi` (evitar twin de test file).
|
||||
|
||||
## Riscos e mitigação
|
||||
|
||||
- **Tab + status em Todos:** ao mudar tab, limpar `status` se sair de `todos` (mesmo comportamento do antigo `buildTabHref`).
|
||||
- **`q` + tab:** filtros AND — busca restringe dentro da janela ativa.
|
||||
|
||||
## Débitos deferidos (simplify B138)
|
||||
|
||||
- **DRY municipality/setExclusiveField helpers** (score 3, `defer_trigger`): extrair quando 3º domínio omnibox precisar do mesmo padrão — já ledgerado em B128; não bloqueia B138.
|
||||
- **Int test `responsible.name` contains** (score 3, `defer_trigger`): where JSON coberto em unit; int espelhando supporter quando houver fixture de activity com responsável.
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import { CampaignListOmnibox } from '@/components/campaign/shared/CampaignListOmnibox'
|
||||
import type { RelationOption } from '@/components/campaign/shared/RelationMultiSelect'
|
||||
import { useCampaignListFilterNavigation } from '@/components/campaign/shared/useCampaignListFilterNavigation'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
applyActivityOmniboxSuggestion,
|
||||
buildActivityOmniboxChips,
|
||||
|
|
@ -16,46 +14,7 @@ import {
|
|||
removeActivityOmniboxChip,
|
||||
type ActivityOmniboxAction,
|
||||
} from '@/utilities/activityOmnibox'
|
||||
import {
|
||||
activityTabLabels,
|
||||
activityTabs,
|
||||
buildActivityListHref,
|
||||
buildActivityListSearchParams,
|
||||
type ActivityListState,
|
||||
type ActivityTab,
|
||||
} from '@/utilities/activityUi'
|
||||
|
||||
const buildTabHref = (state: ActivityListState, tab: ActivityTab): string => {
|
||||
const params = buildActivityListSearchParams({
|
||||
page: 1,
|
||||
tab,
|
||||
kind: state.kind,
|
||||
status: tab === 'todos' ? state.status : undefined,
|
||||
municipality: state.municipality,
|
||||
})
|
||||
const query = params.toString()
|
||||
return query ? `/campanha/atividades?${query}` : '/campanha/atividades'
|
||||
}
|
||||
|
||||
const ActivityTabSwitch = ({ state }: { state: ActivityListState }) => (
|
||||
<nav aria-label="Janela de atividades" className="flex flex-wrap gap-2">
|
||||
{activityTabs.map((tab) => {
|
||||
const active = tab === state.tab
|
||||
return (
|
||||
<Button
|
||||
key={tab}
|
||||
asChild
|
||||
variant={active ? 'default' : 'outline'}
|
||||
className="min-h-11 rounded-[6px]"
|
||||
>
|
||||
<Link href={buildTabHref(state, tab)} aria-current={active ? 'page' : undefined}>
|
||||
{activityTabLabels[tab]}
|
||||
</Link>
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
import { buildActivityListHref, type ActivityListState } from '@/utilities/activityUi'
|
||||
|
||||
export const ActivityFilters = ({
|
||||
state,
|
||||
|
|
@ -116,35 +75,34 @@ export const ActivityFilters = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<ActivityTabSwitch state={state} />
|
||||
|
||||
<form
|
||||
role="search"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
<form
|
||||
role="search"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
}}
|
||||
>
|
||||
<CampaignListOmnibox
|
||||
id="activity-omnibox"
|
||||
label="Filtrar atividades"
|
||||
placeholder="Digite para filtrar (janela, tipo, título, município…)"
|
||||
chips={chips}
|
||||
suggestions={suggestions}
|
||||
query={query}
|
||||
onQueryChange={setQuery}
|
||||
isPending={isPending}
|
||||
onSelectSuggestion={(suggestionId) => {
|
||||
runAction(applyActivityOmniboxSuggestion({ state, suggestionId }))
|
||||
}}
|
||||
>
|
||||
<CampaignListOmnibox
|
||||
id="activity-omnibox"
|
||||
label="Filtrar atividades"
|
||||
placeholder="Digite para filtrar (tipo, status, município…)"
|
||||
chips={chips}
|
||||
suggestions={suggestions}
|
||||
query={query}
|
||||
onQueryChange={setQuery}
|
||||
isPending={isPending}
|
||||
onSelectSuggestion={(suggestionId) => {
|
||||
runAction(applyActivityOmniboxSuggestion({ state, suggestionId }))
|
||||
}}
|
||||
onRemoveChip={(chipId) => {
|
||||
runAction(removeActivityOmniboxChip({ state, chipId }))
|
||||
}}
|
||||
onClearAll={() => {
|
||||
runAction(clearActivityOmnibox(state))
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
onCommitQuery={(text) => {
|
||||
runAction(applyActivityOmniboxSuggestion({ state, suggestionId: `q:${text}` }))
|
||||
}}
|
||||
onRemoveChip={(chipId) => {
|
||||
runAction(removeActivityOmniboxChip({ state, chipId }))
|
||||
}}
|
||||
onClearAll={() => {
|
||||
runAction(clearActivityOmnibox(state))
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Activity list omnibox adapter (B128). Pure / client-safe.
|
||||
* Tab preset stays outside the omnibox (product map).
|
||||
* Activity list omnibox adapter (B128 + B138). Pure / client-safe.
|
||||
* Window presets and text search live inside the omnibox.
|
||||
*/
|
||||
import {
|
||||
createOmniboxSuggestionSeed,
|
||||
|
|
@ -8,8 +8,11 @@ import {
|
|||
type CampaignListOmniboxChip,
|
||||
type CampaignListOmniboxSuggestion,
|
||||
} from '@/lib/campaignListOmnibox'
|
||||
import { isContactSearchQueryReady, normalizeContactSearchQuery } from '@/lib/contactSearchQuery'
|
||||
import { activityKindLabels, activityStatusLabels } from '@/lib/schemas/activity'
|
||||
import {
|
||||
activityTabLabels,
|
||||
activityTabs,
|
||||
buildActivityListSearchParams,
|
||||
parseActivityListParams,
|
||||
type ActivityListState,
|
||||
|
|
@ -52,6 +55,24 @@ const setExclusiveField = (
|
|||
return parseStateFromParams(raw)
|
||||
}
|
||||
|
||||
const tabKeywords: Record<ActivityTab, string[]> = {
|
||||
proximos: ['proximos', 'próximos', 'proximo', 'próximo'],
|
||||
todos: ['todos', 'todas'],
|
||||
realizados: ['realizados', 'realizado'],
|
||||
rascunhos: ['rascunhos', 'rascunho'],
|
||||
}
|
||||
|
||||
const setTab = (state: ActivityListState, tab: ActivityTab): ActivityListState => {
|
||||
const params = buildActivityListSearchParams(withPageReset({ ...state, tab }))
|
||||
const raw: Record<string, string | string[] | undefined> = Object.fromEntries(params.entries())
|
||||
|
||||
if (tab !== 'todos') {
|
||||
delete raw.status
|
||||
}
|
||||
|
||||
return parseStateFromParams(raw)
|
||||
}
|
||||
|
||||
export const buildActivityOmniboxChips = ({
|
||||
state,
|
||||
municipalityLabelsById,
|
||||
|
|
@ -61,6 +82,15 @@ export const buildActivityOmniboxChips = ({
|
|||
}): CampaignListOmniboxChip[] => {
|
||||
const chips: CampaignListOmniboxChip[] = []
|
||||
|
||||
if (state.q) chips.push({ id: 'q', label: chipLabel('Busca', state.q) })
|
||||
|
||||
if (state.tab !== 'proximos') {
|
||||
chips.push({
|
||||
id: `tab:${state.tab}`,
|
||||
label: activityTabLabels[state.tab],
|
||||
})
|
||||
}
|
||||
|
||||
if (state.kind) {
|
||||
chips.push({
|
||||
id: `kind:${state.kind}`,
|
||||
|
|
@ -97,6 +127,20 @@ export const buildActivityOmniboxSuggestionSeeds = ({
|
|||
}) => {
|
||||
const seeds = []
|
||||
|
||||
for (const tabValue of activityTabs) {
|
||||
seeds.push(
|
||||
createOmniboxSuggestionSeed(
|
||||
{
|
||||
id: `tab:${tabValue}`,
|
||||
group: 'Janela',
|
||||
label: activityTabLabels[tabValue],
|
||||
keywords: ['janela', ...tabKeywords[tabValue]],
|
||||
},
|
||||
{ emptyQueryVisible: true },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
for (const [value, label] of Object.entries(activityKindLabels)) {
|
||||
seeds.push(
|
||||
createOmniboxSuggestionSeed(
|
||||
|
|
@ -153,6 +197,19 @@ export const applyActivityOmniboxSuggestion = ({
|
|||
state: ActivityListState
|
||||
suggestionId: string
|
||||
}): ActivityOmniboxAction => {
|
||||
if (suggestionId.startsWith('q:')) {
|
||||
const { trimmed } = normalizeContactSearchQuery(suggestionId.slice(2))
|
||||
const q = isContactSearchQueryReady(trimmed) ? trimmed : undefined
|
||||
return { kind: 'url', state: withPageReset({ ...state, q }) }
|
||||
}
|
||||
|
||||
if (suggestionId.startsWith('tab:')) {
|
||||
const value = suggestionId.slice(4) as ActivityTab
|
||||
if (!activityTabs.includes(value)) return { kind: 'url', state }
|
||||
const next = state.tab === value ? setTab(state, 'proximos') : setTab(state, value)
|
||||
return { kind: 'url', state: next }
|
||||
}
|
||||
|
||||
if (suggestionId.startsWith('kind:')) {
|
||||
const value = suggestionId.slice(5)
|
||||
const next =
|
||||
|
|
@ -190,6 +247,12 @@ export const removeActivityOmniboxChip = ({
|
|||
state: ActivityListState
|
||||
chipId: string
|
||||
}): ActivityOmniboxAction => {
|
||||
if (chipId === 'q') return { kind: 'url', state: withPageReset({ ...state, q: undefined }) }
|
||||
|
||||
if (chipId.startsWith('tab:')) {
|
||||
return { kind: 'url', state: setTab(state, 'proximos') }
|
||||
}
|
||||
|
||||
if (chipId.startsWith('kind:')) {
|
||||
return { kind: 'url', state: setExclusiveField(state, 'kind', undefined) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import type { Where } from 'payload'
|
||||
|
||||
import { isContactSearchQueryReady, normalizeContactSearchQuery } from '@/lib/contactSearchQuery'
|
||||
import { activityKinds, activityStatuses, type ActivityKind } from '@/lib/schemas/activity'
|
||||
import {
|
||||
buildListHref,
|
||||
firstValue,
|
||||
normalizedText,
|
||||
resolveListUrl,
|
||||
strictDecimalInteger,
|
||||
type RawSearchParams as CampaignListRawSearchParams,
|
||||
|
|
@ -26,6 +28,7 @@ export const activityTabLabels: Record<ActivityTab, string> = {
|
|||
export type ActivityListState = {
|
||||
page: number
|
||||
tab: ActivityTab
|
||||
q?: string
|
||||
kind?: ActivityKind
|
||||
status?: ActivityStatus
|
||||
municipality?: number
|
||||
|
|
@ -33,7 +36,7 @@ export type ActivityListState = {
|
|||
|
||||
type RawSearchParams = CampaignListRawSearchParams
|
||||
|
||||
const activityListParamNames = ['tab', 'kind', 'status', 'municipality', 'page'] as const
|
||||
const activityListParamNames = ['q', 'tab', 'kind', 'status', 'municipality', 'page'] as const
|
||||
|
||||
const activityListParamNameSet = new Set<string>(activityListParamNames)
|
||||
|
||||
|
|
@ -46,8 +49,15 @@ const isActivityKind = (value: string | undefined): value is ActivityKind =>
|
|||
const isActivityStatus = (value: string | undefined): value is ActivityStatus =>
|
||||
activityStatuses.includes(value as ActivityStatus)
|
||||
|
||||
const isActivityListSearchReady = (raw: string | undefined): string | undefined => {
|
||||
if (!raw) return undefined
|
||||
const { trimmed } = normalizeContactSearchQuery(raw)
|
||||
return isContactSearchQueryReady(trimmed) ? trimmed : undefined
|
||||
}
|
||||
|
||||
export const parseActivityListParams = (params: RawSearchParams): ActivityListState => {
|
||||
const rawPage = strictDecimalInteger(firstValue(params.page))
|
||||
const q = isActivityListSearchReady(normalizedText(firstValue(params.q)))
|
||||
const rawTab = firstValue(params.tab)
|
||||
const tab = isActivityTab(rawTab) ? rawTab : 'proximos'
|
||||
const rawKind = firstValue(params.kind)
|
||||
|
|
@ -59,6 +69,7 @@ export const parseActivityListParams = (params: RawSearchParams): ActivityListSt
|
|||
return {
|
||||
page: rawPage ?? 1,
|
||||
tab,
|
||||
...(q ? { q } : {}),
|
||||
...(kind ? { kind } : {}),
|
||||
...(status ? { status } : {}),
|
||||
...(municipality ? { municipality } : {}),
|
||||
|
|
@ -68,6 +79,15 @@ export const parseActivityListParams = (params: RawSearchParams): ActivityListSt
|
|||
export const buildActivityListWhere = (state: ActivityListState, now: Date): Where => {
|
||||
const filters: Where[] = []
|
||||
|
||||
if (state.q) {
|
||||
const q = isActivityListSearchReady(state.q)
|
||||
if (q) {
|
||||
filters.push({
|
||||
or: [{ title: { contains: q } }, { 'responsible.name': { contains: q } }],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (state.kind) filters.push({ kind: { equals: state.kind } })
|
||||
if (state.municipality) filters.push({ municipality: { equals: state.municipality } })
|
||||
|
||||
|
|
@ -91,6 +111,7 @@ export const buildActivityListSearchParams = (
|
|||
): URLSearchParams => {
|
||||
const canonicalState = parseActivityListParams({
|
||||
page: String(page),
|
||||
q: state.q,
|
||||
tab: state.tab,
|
||||
kind: state.kind,
|
||||
status: state.status,
|
||||
|
|
@ -98,6 +119,7 @@ export const buildActivityListSearchParams = (
|
|||
})
|
||||
const params = new URLSearchParams()
|
||||
|
||||
if (canonicalState.q) params.set('q', canonicalState.q)
|
||||
if (canonicalState.tab !== 'proximos') params.set('tab', canonicalState.tab)
|
||||
if (canonicalState.kind) params.set('kind', canonicalState.kind)
|
||||
if (canonicalState.status) params.set('status', canonicalState.status)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@ describe('parseActivityListParams', () => {
|
|||
expect(parseActivityListParams({ municipality: '12' }).municipality).toBe(12)
|
||||
expect(parseActivityListParams({ municipality: '0' }).municipality).toBeUndefined()
|
||||
})
|
||||
|
||||
it('parses and trims q', () => {
|
||||
expect(parseActivityListParams({ q: ' comício ' }).q).toBe('comício')
|
||||
expect(parseActivityListParams({ q: ' ' }).q).toBeUndefined()
|
||||
expect(parseActivityListParams({ q: 'a' }).q).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildActivityListWhere (tab → where matrix)', () => {
|
||||
|
|
@ -81,6 +87,19 @@ describe('buildActivityListWhere (tab → where matrix)', () => {
|
|||
],
|
||||
})
|
||||
})
|
||||
|
||||
it('filters by title or responsible name when q is set', () => {
|
||||
const where = buildActivityListWhere({ page: 1, tab: 'proximos', q: 'Maria' }, NOW)
|
||||
expect(where).toEqual({
|
||||
and: [
|
||||
{
|
||||
or: [{ title: { contains: 'Maria' } }, { 'responsible.name': { contains: 'Maria' } }],
|
||||
},
|
||||
{ status: { in: ['planejado', 'confirmado'] } },
|
||||
{ startAt: { greater_than_equal: NOW.toISOString() } },
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildActivityListSearchParams', () => {
|
||||
|
|
@ -95,11 +114,14 @@ describe('buildActivityListSearchParams', () => {
|
|||
const params = buildActivityListSearchParams({
|
||||
page: 1,
|
||||
tab: 'todos',
|
||||
q: 'comício',
|
||||
kind: 'caminhada',
|
||||
status: 'cancelado',
|
||||
municipality: 9,
|
||||
})
|
||||
expect(params.toString()).toBe('tab=todos&kind=caminhada&status=cancelado&municipality=9')
|
||||
expect(params.toString()).toBe(
|
||||
'q=com%C3%ADcio&tab=todos&kind=caminhada&status=cancelado&municipality=9',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ import {
|
|||
import {
|
||||
applyActivityOmniboxSuggestion,
|
||||
buildActivityOmniboxChips,
|
||||
buildActivityOmniboxSuggestionSeeds,
|
||||
clearActivityOmnibox,
|
||||
filterActivityOmniboxSuggestions,
|
||||
removeActivityOmniboxChip,
|
||||
} from '@/utilities/activityOmnibox'
|
||||
import { parseActivityListParams } from '@/utilities/activityUi'
|
||||
import { parseAdvisorListParams } from '@/utilities/advisor/advisorListUrl'
|
||||
|
|
@ -189,6 +192,48 @@ describe('list omnibox adapters (B128)', () => {
|
|||
expect(applied.kind).toBe('url')
|
||||
})
|
||||
|
||||
it('activity applies window preset and search (B138)', () => {
|
||||
const base = parseActivityListParams({})
|
||||
const seeds = buildActivityOmniboxSuggestionSeeds({ tab: base.tab, municipalityOptions: [] })
|
||||
const suggestions = filterActivityOmniboxSuggestions(seeds, 'realizados')
|
||||
expect(suggestions.some((entry) => entry.id === 'tab:realizados')).toBe(true)
|
||||
|
||||
const tabApplied = applyActivityOmniboxSuggestion({
|
||||
state: base,
|
||||
suggestionId: 'tab:realizados',
|
||||
})
|
||||
expect(tabApplied.kind).toBe('url')
|
||||
if (tabApplied.kind === 'url') expect(tabApplied.state.tab).toBe('realizados')
|
||||
|
||||
const searchApplied = applyActivityOmniboxSuggestion({
|
||||
state: parseActivityListParams({ tab: 'todos', status: 'planejado' }),
|
||||
suggestionId: 'q:comício',
|
||||
})
|
||||
expect(searchApplied.kind).toBe('url')
|
||||
if (searchApplied.kind === 'url') {
|
||||
expect(searchApplied.state.q).toBe('comício')
|
||||
expect(searchApplied.state.status).toBe('planejado')
|
||||
}
|
||||
|
||||
const leavingTodos = applyActivityOmniboxSuggestion({
|
||||
state: parseActivityListParams({ tab: 'todos', status: 'planejado' }),
|
||||
suggestionId: 'tab:realizados',
|
||||
})
|
||||
if (leavingTodos.kind === 'url') expect(leavingTodos.state.status).toBeUndefined()
|
||||
|
||||
const chips = buildActivityOmniboxChips({
|
||||
state: parseActivityListParams({ tab: 'realizados', q: 'Maria' }),
|
||||
municipalityLabelsById: new Map(),
|
||||
})
|
||||
expect(chips.map((chip) => chip.id)).toEqual(['q', 'tab:realizados'])
|
||||
|
||||
const removedTab = removeActivityOmniboxChip({
|
||||
state: parseActivityListParams({ tab: 'realizados' }),
|
||||
chipId: 'tab:realizados',
|
||||
})
|
||||
if (removedTab.kind === 'url') expect(removedTab.state.tab).toBe('proximos')
|
||||
})
|
||||
|
||||
it('demand toggles exclusive status, kind and search; preserves activity on clear', () => {
|
||||
const state = parseDemandListParams({
|
||||
status: 'aberta',
|
||||
|
|
|
|||
Loading…
Reference in a new issue