mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
Extract supporter list SQL filters into a server-only module.
This commit is contained in:
parent
4e68e647d4
commit
4ee7d12085
5 changed files with 79 additions and 76 deletions
|
|
@ -19,24 +19,22 @@ export const PrimaryContactCombobox = ({
|
|||
name,
|
||||
current,
|
||||
search,
|
||||
}: PrimaryContactComboboxProps) => {
|
||||
return (
|
||||
<AsyncSearchCombobox
|
||||
name={name}
|
||||
label="Selecionar contato principal"
|
||||
value={current ? { id: current.id, label: contactLabel(current) } : null}
|
||||
emptyOptionLabel="Sem contato principal"
|
||||
dialogDescription="Busque entre as lideranças engajadas deste núcleo."
|
||||
search={async (query) => {
|
||||
const result = await search(query)
|
||||
const limit = result.current ? 99 : 100
|
||||
return result.options.slice(0, limit).map((contact) => ({
|
||||
id: contact.id,
|
||||
label: contactLabel(contact),
|
||||
}))
|
||||
}}
|
||||
pinnedOptions={current ? [{ id: current.id, label: contactLabel(current) }] : []}
|
||||
pinnedGroupHeading="Contato atual"
|
||||
/>
|
||||
)
|
||||
}
|
||||
}: PrimaryContactComboboxProps) => (
|
||||
<AsyncSearchCombobox
|
||||
name={name}
|
||||
label="Selecionar contato principal"
|
||||
value={current ? { id: current.id, label: contactLabel(current) } : null}
|
||||
emptyOptionLabel="Sem contato principal"
|
||||
dialogDescription="Busque entre as lideranças engajadas deste núcleo."
|
||||
search={async (query) => {
|
||||
const result = await search(query)
|
||||
const limit = result.current ? 99 : 100
|
||||
return result.options.slice(0, limit).map((contact) => ({
|
||||
id: contact.id,
|
||||
label: contactLabel(contact),
|
||||
}))
|
||||
}}
|
||||
pinnedOptions={current ? [{ id: current.id, label: contactLabel(current) }] : []}
|
||||
pinnedGroupHeading="Contato atual"
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { sql } from '@payloadcms/db-postgres'
|
||||
import type { Where } from 'payload'
|
||||
|
||||
import { isContactSearchQueryReady, normalizeContactSearchQuery } from '@/lib/contactSearchQuery'
|
||||
|
|
@ -11,11 +10,6 @@ export type SupporterSearchTerms = {
|
|||
phoneDigits: string | null
|
||||
}
|
||||
|
||||
export type AggregateSqlConditions = {
|
||||
conditions: ReturnType<typeof sql>[]
|
||||
needsContactJoin: boolean
|
||||
}
|
||||
|
||||
export const buildSupporterSearchTerms = (rawQ: string): SupporterSearchTerms | null => {
|
||||
if (!isContactSearchQueryReady(rawQ)) return null
|
||||
|
||||
|
|
@ -41,21 +35,6 @@ const buildPayloadSearchWhere = (terms: SupporterSearchTerms): Where => {
|
|||
return { or: searchFilters }
|
||||
}
|
||||
|
||||
const buildSqlSearchCondition = (terms: SupporterSearchTerms): ReturnType<typeof sql> => {
|
||||
const searchTerms: ReturnType<typeof sql>[] = [
|
||||
sql`"contact"."name" ILIKE ${`%${terms.q}%`}`,
|
||||
sql`"contact"."city" ILIKE ${`%${terms.q}%`}`,
|
||||
]
|
||||
|
||||
if (terms.normalizedPhone) {
|
||||
searchTerms.push(sql`"contact"."phone" = ${terms.normalizedPhone}`)
|
||||
} else if (terms.phoneDigits) {
|
||||
searchTerms.push(sql`"contact"."phone" ILIKE ${`%${terms.phoneDigits}%`}`)
|
||||
}
|
||||
|
||||
return sql`(${sql.join(searchTerms, sql` OR `)})`
|
||||
}
|
||||
|
||||
export const toPayloadWhere = (state: SupporterListState): Where => {
|
||||
const filters: Where[] = []
|
||||
|
||||
|
|
@ -78,31 +57,3 @@ export const toPayloadWhere = (state: SupporterListState): Where => {
|
|||
|
||||
return filters.length ? { and: filters } : {}
|
||||
}
|
||||
|
||||
export const toAggregateSqlConditions = (state: SupporterListState): AggregateSqlConditions => {
|
||||
const conditions: ReturnType<typeof sql>[] = []
|
||||
let needsContactJoin = false
|
||||
|
||||
if (state.voteIntention) {
|
||||
conditions.push(sql`"supporter"."vote_intention" = ${state.voteIntention}`)
|
||||
}
|
||||
|
||||
if (state.nucleus) {
|
||||
conditions.push(sql`"supporter"."nucleus_id" = ${state.nucleus}`)
|
||||
}
|
||||
|
||||
if (state.city) {
|
||||
needsContactJoin = true
|
||||
conditions.push(sql`"contact"."city" = ${state.city}`)
|
||||
}
|
||||
|
||||
if (state.q) {
|
||||
const searchTerms = buildSupporterSearchTerms(state.q)
|
||||
if (searchTerms) {
|
||||
needsContactJoin = true
|
||||
conditions.push(buildSqlSearchCondition(searchTerms))
|
||||
}
|
||||
}
|
||||
|
||||
return { conditions, needsContactJoin }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { Payload } from 'payload'
|
|||
import type { CampaignUser } from '@/payload-types'
|
||||
import { getCoordinatorNucleusIds, isCampaignGeneral } from '@/utilities/campaignAccess'
|
||||
import { drizzleResultRows } from '@/utilities/drizzleBulk'
|
||||
import { toAggregateSqlConditions } from '@/utilities/supporterListFilters'
|
||||
import { toAggregateSqlConditions } from '@/utilities/supporterListSqlFilters'
|
||||
import type { SupporterListState } from '@/utilities/supporterUi'
|
||||
import type { SupporterListOverviewViewModel } from '@/utilities/supporterViewModels'
|
||||
|
||||
|
|
|
|||
57
src/utilities/supporterListSqlFilters.ts
Normal file
57
src/utilities/supporterListSqlFilters.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import 'server-only'
|
||||
|
||||
import { sql } from '@payloadcms/db-postgres'
|
||||
|
||||
import {
|
||||
buildSupporterSearchTerms,
|
||||
type SupporterSearchTerms,
|
||||
} from '@/utilities/supporterListFilters'
|
||||
import type { SupporterListState } from '@/utilities/supporterUi'
|
||||
|
||||
export type AggregateSqlConditions = {
|
||||
conditions: ReturnType<typeof sql>[]
|
||||
needsContactJoin: boolean
|
||||
}
|
||||
|
||||
const buildSqlSearchCondition = (terms: SupporterSearchTerms): ReturnType<typeof sql> => {
|
||||
const searchTerms: ReturnType<typeof sql>[] = [
|
||||
sql`"contact"."name" ILIKE ${`%${terms.q}%`}`,
|
||||
sql`"contact"."city" ILIKE ${`%${terms.q}%`}`,
|
||||
]
|
||||
|
||||
if (terms.normalizedPhone) {
|
||||
searchTerms.push(sql`"contact"."phone" = ${terms.normalizedPhone}`)
|
||||
} else if (terms.phoneDigits) {
|
||||
searchTerms.push(sql`"contact"."phone" ILIKE ${`%${terms.phoneDigits}%`}`)
|
||||
}
|
||||
|
||||
return sql`(${sql.join(searchTerms, sql` OR `)})`
|
||||
}
|
||||
|
||||
export const toAggregateSqlConditions = (state: SupporterListState): AggregateSqlConditions => {
|
||||
const conditions: ReturnType<typeof sql>[] = []
|
||||
let needsContactJoin = false
|
||||
|
||||
if (state.voteIntention) {
|
||||
conditions.push(sql`"supporter"."vote_intention" = ${state.voteIntention}`)
|
||||
}
|
||||
|
||||
if (state.nucleus) {
|
||||
conditions.push(sql`"supporter"."nucleus_id" = ${state.nucleus}`)
|
||||
}
|
||||
|
||||
if (state.city) {
|
||||
needsContactJoin = true
|
||||
conditions.push(sql`"contact"."city" = ${state.city}`)
|
||||
}
|
||||
|
||||
if (state.q) {
|
||||
const searchTerms = buildSupporterSearchTerms(state.q)
|
||||
if (searchTerms) {
|
||||
needsContactJoin = true
|
||||
conditions.push(buildSqlSearchCondition(searchTerms))
|
||||
}
|
||||
}
|
||||
|
||||
return { conditions, needsContactJoin }
|
||||
}
|
||||
|
|
@ -2,11 +2,8 @@
|
|||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
buildSupporterSearchTerms,
|
||||
toAggregateSqlConditions,
|
||||
toPayloadWhere,
|
||||
} from '@/utilities/supporterListFilters'
|
||||
import { buildSupporterSearchTerms, toPayloadWhere } from '@/utilities/supporterListFilters'
|
||||
import { toAggregateSqlConditions } from '@/utilities/supporterListSqlFilters'
|
||||
|
||||
describe('supporterListFilters', () => {
|
||||
it('ignores search terms shorter than the contact search minimum', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue