Commit Graph

3 Commits

Author SHA1 Message Date
Arnas Donauskas
296a905976
feat(broadcasts) C: custom-field audience, exclude tags, live preview
Stacked on PR B (#22). Merge A → B → C in order.

Step 2 — Audience
- Implement the "Custom Field" audience option (was a stub): field
  picker + operator (is / is not / contains) + value input. Estimated
  count query joins contact_custom_values with the operator mapped
  to eq / neq / ilike. PostgREST ilike handles "contains" case-
  insensitively.
- New "Exclude contacts with these tags" section — applies to every
  audience type. Estimated count subtracts excluded contacts
  client-side against the resolved base set.
- Switching audience type clears stale shape fields (tagIds /
  customField / csvContacts) so old selections don't leak across.

Step 3 — Personalize
- Add "Custom Field" mapping option alongside static + contact field.
- Live Preview now pulls the most recent real contact (+ their custom
  values) instead of hard-coded sample data. Falls back to sample if
  the user has no contacts yet. Rendered in a WhatsApp-style bubble
  so the user gets a realistic feel for how the recipient sees it.

Hook — use-broadcast-sending
- AudienceConfig gains customField + excludeTagIds.
- resolveAudience: custom-field branch + exclude-tag subtraction
  applied after the base resolution (works for all / tags / custom
  field).
- resolveVariables accepts a pre-built (fieldId → value) map per
  contact. Before the send loop we bulk-fetch contact_custom_values
  for all resolved contacts in one query (paged by 500 for PostgREST
  limits) so we never N+1 per recipient.

new/page.tsx — passes customField + excludeTagIds into the hook;
variables state widens to allow type='custom_field'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:39:24 +03:00
Arnas Donauskas
5d5c5f0a20
fix(broadcasts): per-recipient variables, webhook status sync, rate limit
Three correctness bugs were making the existing broadcast flow wrong,
not just incomplete.

1. Per-recipient variables were broken
   use-broadcast-sending.ts computed templateParams per contact but shipped
   only templateParams[0] to the API, so every contact in a batch received
   contact 0's personalization (name/email/etc).

   Fix: extend POST /api/whatsapp/broadcast to accept a per-recipient
   shape `{ recipients: [{ phone, params }], ... }`. Legacy
   `{ phone_numbers, template_params }` still accepted. Hook sends per-
   recipient payloads; API returns whatsapp_message_id per phone.

2. Webhook never updated broadcast_recipients
   handleStatusUpdate in webhook/route.ts only mirrored Meta status onto
   the `messages` table. broadcast_recipients had no correlation column,
   so delivered_count / read_count / replied_count never advanced for
   real broadcasts.

   Fix (migration 003):
     - broadcast_recipients.whatsapp_message_id (+ UNIQUE index) for
       Meta-message-id correlation
     - composite index (broadcast_id, status) for fast aggregate scans
     - AFTER INSERT/UPDATE/DELETE trigger that recomputes parent
       broadcasts counts via COUNT(*) FILTER — sender + webhook only
       touch the recipient row, aggregates stay consistent

   Webhook:
     - handleStatusUpdate also updates broadcast_recipients with forward-
       only status guard (read cannot regress to sent on a replay)
     - processMessage flags the replied recipient row when an inbound
       message arrives from a recent broadcast contact

3. Rate limiting did not match spec
   Batch 50 / no delay → Batch 10 / 1s between batches, matching the spec
   and Meta's per-phone-number rate ceiling.

Also: extract statusConfig from both page components into
src/lib/broadcast-status.ts (single source of truth for PR B). Add
whatsapp_message_id to BroadcastRecipient type.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:22:38 +03:00
Arnas Donauskas
658d070811
Initial scaffold: WhatsApp CRM with Next.js + Supabase
Complete WhatsApp CRM application built with Next.js 14 (App Router),
Supabase (PostgreSQL + Auth + Realtime), and shadcn/ui.

Features:
- Auth: Login, signup, forgot password with Supabase Auth
- Dashboard: Analytics overview with stats, recent activity
- Inbox: Real-time WhatsApp conversations with message threading
- Contacts: CRUD with tags, custom fields, notes, CSV import
- Broadcasts: 4-step wizard with audience selection, template sending
- Pipelines: Kanban board with deal management
- Settings: WhatsApp API config, templates, tags
- WhatsApp API: Webhook (verify + receive), send, broadcast, media proxy
- Database: 16 tables with RLS policies, indexes, realtime subscriptions
- Dark theme throughout with emerald accents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:17:31 +03:00