Inspiration

Brazilian political offices run on chaos: constituent requests in WhatsApp threads, budget amendments in spreadsheets, campaign donors in someone's notebook. Off-the-shelf CRMs don't understand emendas parlamentares, indicações, electoral zones, or TSE campaign-finance rules. POLISConnect was born from watching a real cabinet drown in this — and realizing every one of Brazil's ~60,000 elected officials has the same problem.

What it does

POLISConnect is an all-in-one management platform for Brazilian political offices and campaigns. Each politician's office is an isolated tenant with modules for:

  • Pessoas (CRM) — constituents, leaders, and supporters with full relationship history
  • Demandas & Indicações — constituent requests and legislative indications, tracked from intake to resolution
  • Emendas — parliamentary budget amendments and their execution status
  • Agenda & Comunicação — the office's calendar and outreach
  • Mapa Político — territorial view of political strength by zone
  • Campanha — a per-tenant activatable module for Brazilian campaign management: electoral zones, goals, and field operations

A platform admin layer above all tenants handles provisioning, so the product runs as a licensable SaaS.

How we built it

  • Frontend: React 19 + Vite + TypeScript, Tailwind, Zustand for state, React Query for server cache
  • Backend: Supabase — Postgres with Row Level Security enforcing tenant isolation, Supabase Auth, and service-role API routes for admin operations
  • Deploy: Vercel, continuous deploy straight from main

Multi-tenancy is enforced at the database layer: every table carries a tenant_id and RLS policies guarantee a user can only ever see their own office's data — the app code never has to remember to filter.

Challenges we ran into

  • RLS vs. GRANT confusion. "Permission denied for table X" from service-role calls looked like RLS misconfiguration but was almost always a missing Postgres GRANT. We documented the pattern after losing hours to it.
  • UTC date shifts. Brazil is UTC-3, so date-only values (yyyy-mm-dd) parsed with new Date() silently became yesterday after ~9pm local time. Birthdays and due dates were off by one day — the same bug independently resurfaced in 5+ modules before we killed it with a shared date utility.
  • Schema drift. Production DB diverged from the repo's SQL files (columns added directly, tables dropped by later migrations). Seeds written from the repo failed with 42703/42P01 errors; we now treat production as the source of truth.
  • No staging. Deploys go straight to production, so every fix needs live verification in the browser, not just a green build.

Accomplishments that we're proud of

  • True multi-tenant SaaS, live in production at www.polisconnect.com.br — not a demo, a real product with database-level tenant isolation.
  • Full mandate lifecycle in one tool: the same platform covers governing (demandas, emendas, indicações) and campaigning — a combination no Brazilian off-the-shelf tool offers.
  • Security done at the right layer: RLS policies mean a data leak between offices is impossible by construction, not by code discipline.
  • A self-improving workflow: every non-trivial bug becomes a written diagnostic pattern, so the codebase gets easier to maintain over time instead of harder.

What we learned

  • Push invariants into the database (RLS, constraints) instead of trusting app code.
  • Domain fit beats generic tooling: a CRM that speaks emenda and indicação natively is the whole product.
  • Every non-trivial bug should leave behind a written diagnostic pattern — the second occurrence then costs minutes, not hours.

What's next for PolisConnect

Onboarding real offices as paying tenants, WhatsApp integration for constituent intake, and deeper campaign analytics for the 2026 election cycle.## Inspiration

Built With

Share this project:

Updates