mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Add full support for WhatsApp Cloud API interactive messages across the inbox, automations, and a new quick-replies feature. Extends the interactive plumbing that previously existed only for the Flows engine. Sending - send-message core handles `interactive` (reply buttons ≤3, list ≤10 rows) via the existing Meta senders; validated server-side before send. - Inbox composer gains a "+" menu → builder dialog with live WhatsApp-style preview + Meta char limits (button 20, list row 24, body 1024). - Automation "Send Buttons" / "Send List" steps in the builder. - Public v1 /messages accepts type=interactive. Storage / round-trip - migration 035: messages.interactive_payload JSONB + quick_replies table. - Outbound interactive persists the structured payload (composer, automations, and Flows) so the thread re-renders the buttons/rows. Receiving - Bubble renders outbound interactive via the shared preview; inbound taps (button_reply/list_reply) keep the "chosen option" affordance. Automations - New `interactive_reply` trigger matches on tapped button/list IDs (exact), enabling chained multi-step menus. Quick replies - New account-scoped quick_replies (text or interactive snippets), managed in Settings and insertable from the composer; "Save as quick reply" in the builder. Shared core in src/lib/whatsapp/interactive.ts + src/components/interactive/. Includes unit tests for the validator, send-param validation, the interactive_reply trigger match, and the new automation step/trigger validation. typecheck + lint clean; full suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.9 KiB
SQL
62 lines
2.9 KiB
SQL
-- ============================================================
|
|
-- 035_interactive_messages.sql
|
|
--
|
|
-- Full support for WhatsApp interactive messages (reply buttons +
|
|
-- list messages) beyond the Flows subsystem.
|
|
--
|
|
-- 1. messages.interactive_payload — the structured payload of an
|
|
-- OUTBOUND interactive message (buttons / list) so it round-trips:
|
|
-- the thread can re-render the buttons/rows we sent, not just the
|
|
-- body text. Migration 010 already added 'interactive' to the
|
|
-- content_type CHECK and the inbound `interactive_reply_id`
|
|
-- column, so no CHECK change is needed here.
|
|
--
|
|
-- 2. quick_replies — reusable snippets (plain text OR a saved
|
|
-- interactive message) an agent can insert from the inbox
|
|
-- composer. Account-scoped, same tenancy model as automations.
|
|
-- ============================================================
|
|
|
|
-- 1. Outbound interactive payload -----------------------------
|
|
ALTER TABLE messages
|
|
ADD COLUMN IF NOT EXISTS interactive_payload JSONB;
|
|
|
|
-- 2. Quick replies --------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS quick_replies (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
-- Tenancy. Every member of the account shares its quick replies.
|
|
account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
-- Author / audit only — never used for tenancy isolation.
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
-- 'text' snippets carry `content_text`; 'interactive' snippets carry
|
|
-- `interactive_payload` (validated app-side against Meta's limits).
|
|
kind TEXT NOT NULL DEFAULT 'text' CHECK (kind IN ('text', 'interactive')),
|
|
content_text TEXT,
|
|
interactive_payload JSONB,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_quick_replies_account ON quick_replies(account_id);
|
|
|
|
ALTER TABLE quick_replies ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Account-scoped policies mirroring automations (see 017): any member
|
|
-- can read; agent+ can create / edit / delete.
|
|
DROP POLICY IF EXISTS quick_replies_select ON quick_replies;
|
|
DROP POLICY IF EXISTS quick_replies_insert ON quick_replies;
|
|
DROP POLICY IF EXISTS quick_replies_update ON quick_replies;
|
|
DROP POLICY IF EXISTS quick_replies_delete ON quick_replies;
|
|
CREATE POLICY quick_replies_select ON quick_replies FOR SELECT
|
|
USING (is_account_member(account_id));
|
|
CREATE POLICY quick_replies_insert ON quick_replies FOR INSERT
|
|
WITH CHECK (is_account_member(account_id, 'agent'));
|
|
CREATE POLICY quick_replies_update ON quick_replies FOR UPDATE
|
|
USING (is_account_member(account_id, 'agent'));
|
|
CREATE POLICY quick_replies_delete ON quick_replies FOR DELETE
|
|
USING (is_account_member(account_id, 'agent'));
|
|
|
|
DROP TRIGGER IF EXISTS set_updated_at ON quick_replies;
|
|
CREATE TRIGGER set_updated_at BEFORE UPDATE ON quick_replies
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|