B138 — simplify: min-length e normalização da busca

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-08-02 23:08:06 +00:00
parent f0a7758eb3
commit 4ea2085239
No known key found for this signature in database
4 changed files with 22 additions and 10 deletions

View file

@ -60,8 +60,7 @@ flowchart LR
- **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.
## Aceite de engenharia
## Débitos deferidos (simplify B138)
- [x] Aceite de produto da intenção ainda coberto
- [x] Invariantes AGENTS/engineering-standards
- [x] Testes de domínio previstos (unit) onde URL/where mudam
- **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.

View file

@ -8,6 +8,7 @@ import {
type CampaignListOmniboxChip,
type CampaignListOmniboxSuggestion,
} from '@/lib/campaignListOmnibox'
import { isContactSearchQueryReady, normalizeContactSearchQuery } from '@/lib/contactSearchQuery'
import { activityKindLabels, activityStatusLabels } from '@/lib/schemas/activity'
import {
activityTabLabels,
@ -197,8 +198,9 @@ export const applyActivityOmniboxSuggestion = ({
suggestionId: string
}): ActivityOmniboxAction => {
if (suggestionId.startsWith('q:')) {
const q = suggestionId.slice(2)
return { kind: 'url', state: withPageReset({ ...state, q: q || undefined }) }
const { trimmed } = normalizeContactSearchQuery(suggestionId.slice(2))
const q = isContactSearchQueryReady(trimmed) ? trimmed : undefined
return { kind: 'url', state: withPageReset({ ...state, q }) }
}
if (suggestionId.startsWith('tab:')) {

View file

@ -1,5 +1,6 @@
import type { Where } from 'payload'
import { isContactSearchQueryReady, normalizeContactSearchQuery } from '@/lib/contactSearchQuery'
import { activityKinds, activityStatuses, type ActivityKind } from '@/lib/schemas/activity'
import {
buildListHref,
@ -48,9 +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 = normalizedText(firstValue(params.q))
const q = isActivityListSearchReady(normalizedText(firstValue(params.q)))
const rawTab = firstValue(params.tab)
const tab = isActivityTab(rawTab) ? rawTab : 'proximos'
const rawKind = firstValue(params.kind)
@ -73,9 +80,12 @@ export const buildActivityListWhere = (state: ActivityListState, now: Date): Whe
const filters: Where[] = []
if (state.q) {
filters.push({
or: [{ title: { contains: state.q } }, { 'responsible.name': { contains: 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 } })

View file

@ -43,6 +43,7 @@ describe('parseActivityListParams', () => {
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()
})
})