teqo-backup/src/lib/campaignQuickActionMount.ts
Francisco Solla 1daaaf9f4e feat(C139): domínio de contatos — URL state, loader com vínculos e invariante de nome
Fase 2 do plano aprovado. Três módulos em src/utilities/contacts/:
- contactListUrl: contrato de params (q|gender|state|city|ausencia|vinculo|sort|dir|page),
  canonicalize, hrefs de sort e where de fonte com 'phones.value' like (ILIKE verificado
  no Postgres local).
- contactListData: loader com UM find em contact (user threadado, overrideAccess false),
  vínculos (liderancas/dobradinhas/assessores/equipe) como facets sobre o conjunto
  legível, filtro/sort/paginação em memória, nulos no fim (precedente B15).
- contactNameInvariant: lock contact-name:{normalized} + prefilter ILIKE + veredito
  exato em memória; mensagem de conflito pt-BR.

38 testes unit novos; format + tsc limpos.
2026-08-13 14:05:24 -03:00

37 lines
1.8 KiB
TypeScript

import { CAMPAIGN_ACTIONS_HOME } from '@/lib/campaignActionRoutes'
import { isAdvisorsPath } from '@/lib/campaignAdvisorQuickActions'
import { CAMPAIGN_CONTACTS_HOME, CAMPAIGN_HOME, LEADER_CONTACTS_HOME } from '@/lib/campaignPaths'
import { isActivityTourComposerPath, isListPath } from '@/lib/campaignQuickActionPaths'
import {
isStaffCampaignRole,
isUnrestrictedCampaignRole,
type CampaignRole,
} from '@/lib/campaignRoles'
/** Same exact-match rule as the sidebar nav. */
export const isCampaignHomePath = (pathname: string): boolean => isListPath(pathname, CAMPAIGN_HOME)
export const isCampaignActionsPath = (pathname: string): boolean =>
pathname === CAMPAIGN_ACTIONS_HOME || pathname.startsWith(`${CAMPAIGN_ACTIONS_HOME}/`)
export const isLeaderContactsPath = (pathname: string): boolean =>
pathname === LEADER_CONTACTS_HOME || pathname.startsWith(`${LEADER_CONTACTS_HOME}/`)
/** C139 — the staff contacts page owns a create FAB of its own, so the
* quick-actions drawer stays out of it. */
export const isContactsPath = (pathname: string): boolean =>
pathname === CAMPAIGN_CONTACTS_HOME || pathname.startsWith(`${CAMPAIGN_CONTACTS_HOME}/`)
/**
* Whether the quick-actions FAB should mount on this navigation (all viewports).
*/
export const shouldMountQuickActionsFab = (pathname: string, role: CampaignRole): boolean => {
if (isCampaignHomePath(pathname)) return false
if (isCampaignActionsPath(pathname)) return false
// B84: E13 compositor already exposes primary CTAs — skip competing drawer chrome.
if (isActivityTourComposerPath(pathname)) return false
if (role === 'leader') return isLeaderContactsPath(pathname)
if (isContactsPath(pathname)) return false
if (isAdvisorsPath(pathname)) return isUnrestrictedCampaignRole(role)
return isStaffCampaignRole(role)
}