mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
Collection actionPlan -> activity (labels "Atividade"/"Atividades"), campo
campaignDemand.actionPlan -> activity, rota /campanha/planos ->
/campanha/atividades (criacao em atividades/nova), sidebar "Atividades" e
rotulos alinhados a entidade ("Tipo de atividade", "Origem da atividade",
"Resultado da atividade"). Valores de enum kind/status/origin inalterados.
Schema movido por migration data-preserving escrita a mao
(20260725_213000_rename_action_plan_to_activity): 4 tabelas, 3 enums, 2
colunas, 39 indices (4 pkeys inclusos), 14 FKs e 2 sequences, cada statement
guardado na existencia do objeto de origem, com down() simetrico. O gerador do
Payload emitiria DROP+CREATE e nao ve o indice parcial escrito a mao
action_plan_upcoming_start_at_idx, entao a lista veio de
pg_indexes/pg_constraint/pg_type/pg_sequences e o snapshot .json foi derivado
do anterior por transformacao textual -- validado por migrate:create __probe
sem mudanca de schema. A mesma migration alinha os 8 indices fosseis %plaza%
que a remodelagem de municipios deixou (renomeou as colunas plaza_id, nao os
indices).
Guard: o describe "banned campaign terminology" de
codebaseConventions.unit.spec.ts virou tabela de termos varrendo src+tests+
scripts (migrations fora -- historia congelada), banindo os compostos
actionPlan, a copy "plano de acao" e a rota antiga, mas nao plan/plano isolado
(colidiria com os planos de assinatura da Fase 2). Allowlist: o proprio spec,
scripts/generate-remodel-municipalities-migration.mjs e o comentario historico
em campaignMigrationReconciliation.int.spec.ts.
Plano: docs/plans/renomear-plano-acao-para-atividade.md
132 lines
4.6 KiB
TypeScript
132 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
activityTabs,
|
|
buildActivityFiltersKey,
|
|
buildActivityListHref,
|
|
buildActivityListSearchParams,
|
|
buildActivityListWhere,
|
|
parseActivityListParams,
|
|
resolveActivityListUrl,
|
|
} from '@/utilities/activityUi'
|
|
|
|
const NOW = new Date('2026-07-25T12:00:00.000Z')
|
|
|
|
describe('parseActivityListParams', () => {
|
|
it('defaults to the proximos tab on page 1', () => {
|
|
expect(parseActivityListParams({})).toEqual({ page: 1, tab: 'proximos' })
|
|
})
|
|
|
|
it('falls back to proximos on an unknown tab', () => {
|
|
expect(parseActivityListParams({ tab: 'passados' }).tab).toBe('proximos')
|
|
})
|
|
|
|
it('accepts every declared tab', () => {
|
|
for (const tab of activityTabs) {
|
|
expect(parseActivityListParams({ tab }).tab).toBe(tab)
|
|
}
|
|
})
|
|
|
|
it('honors status only on the todos tab', () => {
|
|
expect(parseActivityListParams({ status: 'realizado' }).status).toBeUndefined()
|
|
expect(parseActivityListParams({ tab: 'todos', status: 'realizado' }).status).toBe('realizado')
|
|
expect(parseActivityListParams({ tab: 'todos', status: 'inexistente' }).status).toBeUndefined()
|
|
})
|
|
|
|
it('validates kind and municipality', () => {
|
|
expect(parseActivityListParams({ kind: 'caminhada' }).kind).toBe('caminhada')
|
|
expect(parseActivityListParams({ kind: 'festa' }).kind).toBeUndefined()
|
|
expect(parseActivityListParams({ municipality: '12' }).municipality).toBe(12)
|
|
expect(parseActivityListParams({ municipality: '0' }).municipality).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('buildActivityListWhere (tab → where matrix)', () => {
|
|
it('proximos = planejado/confirmado starting from now', () => {
|
|
const where = buildActivityListWhere({ page: 1, tab: 'proximos' }, NOW)
|
|
expect(where).toEqual({
|
|
and: [
|
|
{ status: { in: ['planejado', 'confirmado'] } },
|
|
{ startAt: { greater_than_equal: NOW.toISOString() } },
|
|
],
|
|
})
|
|
})
|
|
|
|
it('realizados and rascunhos filter by their status', () => {
|
|
expect(buildActivityListWhere({ page: 1, tab: 'realizados' }, NOW)).toEqual({
|
|
and: [{ status: { equals: 'realizado' } }],
|
|
})
|
|
expect(buildActivityListWhere({ page: 1, tab: 'rascunhos' }, NOW)).toEqual({
|
|
and: [{ status: { equals: 'rascunho' } }],
|
|
})
|
|
})
|
|
|
|
it('todos is unfiltered unless a status is chosen', () => {
|
|
expect(buildActivityListWhere({ page: 1, tab: 'todos' }, NOW)).toEqual({})
|
|
expect(buildActivityListWhere({ page: 1, tab: 'todos', status: 'cancelado' }, NOW)).toEqual({
|
|
and: [{ status: { equals: 'cancelado' } }],
|
|
})
|
|
})
|
|
|
|
it('prepends kind and municipality filters on any tab', () => {
|
|
const where = buildActivityListWhere(
|
|
{ page: 1, tab: 'realizados', kind: 'comicio', municipality: 3 },
|
|
NOW,
|
|
)
|
|
expect(where).toEqual({
|
|
and: [
|
|
{ kind: { equals: 'comicio' } },
|
|
{ municipality: { equals: 3 } },
|
|
{ status: { equals: 'realizado' } },
|
|
],
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('buildActivityListSearchParams', () => {
|
|
it('omits the default tab and page 1', () => {
|
|
expect(buildActivityListSearchParams({ page: 1, tab: 'proximos' }).toString()).toBe('')
|
|
expect(buildActivityListSearchParams({ page: 2, tab: 'todos' }).toString()).toBe(
|
|
'tab=todos&page=2',
|
|
)
|
|
})
|
|
|
|
it('serializes filters in canonical order', () => {
|
|
const params = buildActivityListSearchParams({
|
|
page: 1,
|
|
tab: 'todos',
|
|
kind: 'caminhada',
|
|
status: 'cancelado',
|
|
municipality: 9,
|
|
})
|
|
expect(params.toString()).toBe('tab=todos&kind=caminhada&status=cancelado&municipality=9')
|
|
})
|
|
})
|
|
|
|
describe('hrefs and canonical URL resolution', () => {
|
|
it('builds hrefs against the atividades base path', () => {
|
|
expect(buildActivityListHref({ page: 1, tab: 'proximos' }, 1)).toBe('/campanha/atividades')
|
|
expect(buildActivityListHref({ page: 1, tab: 'realizados' }, 2)).toBe(
|
|
'/campanha/atividades?tab=realizados&page=2',
|
|
)
|
|
})
|
|
|
|
it('derives the filters key from the canonical serialization', () => {
|
|
expect(buildActivityFiltersKey({ page: 1, tab: 'todos', kind: 'comicio' })).toBe(
|
|
'tab=todos&kind=comicio',
|
|
)
|
|
})
|
|
|
|
it('redirects an explicit default tab out of the URL', () => {
|
|
const result = resolveActivityListUrl({ tab: 'proximos' })
|
|
expect(result.href).toBe('/campanha/atividades')
|
|
expect(result.redirectHref).toBe('/campanha/atividades')
|
|
})
|
|
|
|
it('redirects on unsupported params and clamps the page', () => {
|
|
expect(resolveActivityListUrl({ foo: 'bar' }).redirectHref).toBe('/campanha/atividades')
|
|
const clamped = resolveActivityListUrl({ tab: 'todos', page: '50' }, 2)
|
|
expect(clamped.state.page).toBe(2)
|
|
expect(clamped.redirectHref).toBe('/campanha/atividades?tab=todos&page=2')
|
|
})
|
|
})
|