mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:05:19 +00:00
- Contact.phone (text único) → phones (array field Payload, subcampo value): fonte única; principal = phones[0]; beforeValidate normaliza, filtra vazios, rejeita inválido e duplicado DENTRO da ficha (C111 entre fichas preservado) - migration add_contact_phones_array: backfill phone → phones[0] (gen_random_uuid) antes do drop da coluna + índice em value (hand-edit documentado; snapshot .json gerado mantém as colunas C99/C100 já existentes) - primaryPhoneOf / phoneValuesOf / reorderWithPrimaryPhone em src/lib/phone.ts (ordem = prioridade; inline edit = seta o principal preservando o resto) - findOrCreateContactByPhone aceita phones[] (dedupe pelo primário; reuse faz append dos faltantes; locks em todos os números digitados) - sync conta→ficha (CampaignUser): phone da conta faz APPEND quando a ficha não tem (nunca reordena nem remove); tombstone LGPD substitui TODOS os telefones - convite redemption: telefone digitado vira principal, demais preservados - import CSV: preview sinaliza compartilhado também por número secundário; bulk insere contact_phones (JOIN nas leituras, uuid no insert) - busca por telefone (apoiadores, pickers, combobox) casa qualquer número - UI: PhonesFieldEditor compartilhado (repetível mascarado, remover, ↑/↓, modo inline com Salvar) nas 5 superfícies — liderança (form/ficha/lista), apoiador (form/ficha), dobradinha (ficha), assessor (ficha), wizard - UI pública (petição/WhatsApp) intocada: zod e forms ficam com phone; as ações mapeiam phone → phones[0] - testes: spec novo campaignMultiPhones (aceite completo), specs C111/C99 atualizados, e2e wizard/apoiador ajustados
87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
// @vitest-environment node
|
|
|
|
import type { ToCSVFunction } from '@payloadcms/plugin-import-export/types'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { signatureContactToCSV, signaturePetitionToCSV } from '@/utilities/signatureExport'
|
|
|
|
const runToCSV = (toCSV: ToCSVFunction, value: unknown) => {
|
|
const row: Record<string, unknown> = {}
|
|
toCSV({
|
|
columnName: 'field',
|
|
data: row,
|
|
doc: row,
|
|
row,
|
|
siblingDoc: {},
|
|
value,
|
|
} as unknown as Parameters<ToCSVFunction>[0])
|
|
return row
|
|
}
|
|
|
|
describe('signatureExport', () => {
|
|
describe('signatureContactToCSV', () => {
|
|
it('writes operational contact columns from a populated relationship', () => {
|
|
expect(
|
|
runToCSV(signatureContactToCSV, {
|
|
id: 1,
|
|
name: 'Maria Silva',
|
|
email: 'maria@example.com',
|
|
phones: [{ value: '71999990000' }],
|
|
state: 'BA',
|
|
city: 'Salvador',
|
|
}),
|
|
).toEqual({
|
|
contact_name: 'Maria Silva',
|
|
contact_email: 'maria@example.com',
|
|
contact_phone: '71999990000',
|
|
contact_state: 'BA',
|
|
contact_city: 'Salvador',
|
|
})
|
|
})
|
|
|
|
it('keeps SMS-only rows with empty email', () => {
|
|
const row = runToCSV(signatureContactToCSV, {
|
|
id: 2,
|
|
name: 'João Souza',
|
|
email: null,
|
|
phones: [{ value: '71988887777' }],
|
|
state: 'BA',
|
|
city: 'Feira de Santana',
|
|
})
|
|
|
|
expect(row.contact_email).toBe('')
|
|
expect(row.contact_phone).toBe('71988887777')
|
|
})
|
|
|
|
it('writes empty cells when contact is not populated', () => {
|
|
expect(runToCSV(signatureContactToCSV, 42)).toEqual({
|
|
contact_name: '',
|
|
contact_email: '',
|
|
contact_phone: '',
|
|
contact_state: '',
|
|
contact_city: '',
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('signaturePetitionToCSV', () => {
|
|
it('writes petition id and title from a populated relationship', () => {
|
|
expect(
|
|
runToCSV(signaturePetitionToCSV, {
|
|
id: 'saude-baiana',
|
|
title: 'Saúde para toda a Bahia',
|
|
}),
|
|
).toEqual({
|
|
petition_id: 'saude-baiana',
|
|
petition_title: 'Saúde para toda a Bahia',
|
|
})
|
|
})
|
|
|
|
it('writes empty cells when petition is not populated', () => {
|
|
expect(runToCSV(signaturePetitionToCSV, 'saude-baiana')).toEqual({
|
|
petition_id: '',
|
|
petition_title: '',
|
|
})
|
|
})
|
|
})
|
|
})
|