@fayz-ai/plugin-conversations 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkGrC,CAAA"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAqGrC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkGvC,CAAA"}
1
+ {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAqGvC,CAAA"}
@@ -5,6 +5,7 @@ export declare const MIGRATION_003_A_CAMPANHA_SAI_NO_RITMO_QUE_O_NUMERO_AGUENTA
5
5
  export declare const MIGRATION_004_A_TELA_SABE_SE_PODE_FALAR_ANTES_DE_DEIXAR_ESCREVER = "-- ---------------------------------------------------------------------------\n-- 004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever.sql \u2014 a janela de 24h,\n-- como DADO, para a tela responder antes de algu\u00E9m digitar.\n--\n-- \u2500\u2500 o defeito que isto conserta \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- Na API oficial do WhatsApp, texto livre s\u00F3 sai dentro de 24h contadas da\n-- \u00FAltima mensagem DO CLIENTE. Fora dela a Meta recusa, e a recusa chega como um\n-- c\u00F3digo no log \u2014 n\u00E3o na tela de quem escreveu. O atendente digita, aperta\n-- enviar, v\u00EA a mensagem aparecer na conversa, e ela nunca chega.\n--\n-- Isso \u00E9 pior que um erro: \u00E9 uma tela que mente. E some inteiro num provedor\n-- n\u00E3o oficial, onde n\u00E3o existe janela nenhuma \u2014 ent\u00E3o o MESMO campo de texto\n-- funciona ou falha calado dependendo de qual provedor est\u00E1 ativo.\n--\n-- A corre\u00E7\u00E3o n\u00E3o \u00E9 um aviso depois. \u00C9 a tela saber ANTES: janela aberta, texto\n-- livre; janela fechada, s\u00F3 template, e o campo diz isso em vez de aceitar.\n--\n-- \u2500\u2500 quem abre a janela \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- O CLIENTE, e s\u00F3 ele. Mensagem nossa n\u00E3o abre nada \u2014 nem template aprovado.\n-- \u00C9 o erro mais comum de quem implementa isso, e \u00E9 por isso que o c\u00E1lculo\n-- abaixo olha exclusivamente `direction = 'inbound'`.\n--\n-- (Origem: developers.facebook.com/documentation/business-messaging/whatsapp \u2014\n-- \"The user messages you about the product. This opens a 24 hour customer\n-- service window.\" Cliques em an\u00FAncio Click-to-WhatsApp abrem 72h; n\u00E3o\n-- distinguimos ainda, e 24h \u00E9 o limite conservador dos dois.)\n-- ---------------------------------------------------------------------------\n\nDROP VIEW IF EXISTS public.v_conversations_window;\nCREATE VIEW public.v_conversations_window\nWITH (security_invoker = true) AS\n SELECT c.id AS conversation_id,\n c.tenant_id,\n c.channel,\n c.contact_handle,\n c.contact_person_id,\n u.last_inbound_at,\n -- Quando fecha. Nulo = nunca abriu, que \u00E9 o caso de todo lead que\n -- ainda n\u00E3o respondeu \u2014 a maioria de uma lista de feira.\n (u.last_inbound_at + interval '24 hours') AS window_expires_at,\n (u.last_inbound_at IS NOT NULL\n AND u.last_inbound_at > now() - interval '24 hours') AS window_open,\n -- Quanto falta, em minutos. A tela mostra \"faltam 3h\" e n\u00E3o um\n -- carimbo de data: quem est\u00E1 respondendo quer saber se d\u00E1 tempo.\n CASE WHEN u.last_inbound_at IS NOT NULL\n THEN greatest(0, floor(extract(epoch FROM\n (u.last_inbound_at + interval '24 hours') - now()) / 60))::int\n END AS minutes_left,\n -- Quem pediu para n\u00E3o ser mais contatado. Some da tela antes de\n -- qualquer janela: n\u00E3o importa se est\u00E1 aberta.\n EXISTS (SELECT 1 FROM public.plg_conversations_optouts o\n WHERE o.tenant_id = c.tenant_id\n AND o.phone_e164 = regexp_replace(coalesce(c.contact_handle,''), '\\D', '', 'g')\n AND o.active) AS opted_out\n FROM public.plg_conversations c\n LEFT JOIN LATERAL (\n SELECT max(m.at) AS last_inbound_at\n FROM public.plg_conversation_messages m\n WHERE m.conversation_id = c.id\n AND m.direction = 'inbound'\n ) u ON true\n WHERE c.tenant_id IN (SELECT public.user_tenant_ids());\n\nGRANT SELECT ON public.v_conversations_window TO authenticated;\n\nCOMMENT ON VIEW public.v_conversations_window IS\n 'Se d\u00E1 para mandar texto livre para esta conversa agora. A janela de 24h s\u00F3 abre por mensagem DO CLIENTE \u2014 template nosso n\u00E3o abre nada (004).';\n\n-- O \u00EDndice que a LATERAL usa. Sem ele, cada linha da caixa varre as mensagens\n-- da conversa inteira, e a caixa \u00E9 justamente a tela que abre o dia todo.\nCREATE INDEX IF NOT EXISTS plg_conversation_messages_inbound_at\n ON public.plg_conversation_messages (conversation_id, at DESC)\n WHERE direction = 'inbound';\n";
6
6
  export declare const MIGRATION_005_A_CONVERSA_CONTA_AO_FUNIL_QUE_HOUVE_CONTATO = "-- ---------------------------------------------------------------------------\n-- 005_a_conversa_conta_ao_funil_que_houve_contato.sql \u2014 a ponte que faltava.\n--\n-- \u2500\u2500 o buraco, medido \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- No cluster: 8 conversas, 42 mensagens trocadas \u2014 incluindo respostas de\n-- gente de verdade \u2014 e 315 neg\u00F3cios TODOS parados em \"Novo (feira)\", com\n-- `contact_status = 'not_contacted'`. Zero atividades vindas da caixa.\n--\n-- N\u00E3o era defeito: n\u00E3o existia liga\u00E7\u00E3o nenhuma. Nenhum `INSERT` em\n-- `plg_crm_activities` fora do plugin-crm, nenhuma fonte de linha do tempo\n-- registrada. A caixa de entrada e o funil olhavam para as mesmas pessoas e\n-- n\u00E3o se falavam.\n--\n-- O maquin\u00E1rio do CRM j\u00E1 estava pronto e correto: `crm_recompute_deal_status`\n-- (crm/020) l\u00EA as atividades, pega o \u00DALTIMO `outcome` e deriva o status; o\n-- gatilho `crm_activity_restatus` dispara a cada atividade inserida. Faltava\n-- algu\u00E9m inserir.\n--\n-- \u2500\u2500 por que o `outcome` \u00E9 o que \u00E9 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- mensagem RECEBIDA \u2192 `replied` o lead respondeu. \u00C9 o fato mais forte\n-- que a caixa produz, e o que mais move\n-- o status (\"Respondeu\").\n-- mensagem ENVIADA \u2192 `left_message` deixamos recado. Mandar N\u00C3O \u00E9 ser\n-- lido: tratar envio como contato feito\n-- encheria o funil de \"conectado\" sem\n-- ningu\u00E9m do outro lado. A 020 mapeia\n-- este valor para \"N\u00E3o atendeu\", que \u00E9\n-- literalmente o estado \u2014 ainda sem\n-- resposta.\n--\n-- O vocabul\u00E1rio \u00E9 FECHADO por CHECK (crm/020:49): `connected, no_answer,\n-- left_message, replied, scheduled`. Conferido antes de escrever \u2014 a primeira\n-- vers\u00E3o desta migration usava `attempted`, que a regra da 020 nomeia mas a\n-- restri\u00E7\u00E3o da coluna N\u00C3O aceita, e teria falhado em toda mensagem enviada.\n--\n-- \u2500\u2500 e o `deal_id` n\u00E3o \u00E9 opcional \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `crm_recompute_deal_status` agrega `WHERE a.deal_id = p_deal_id`. Uma\n-- atividade s\u00F3 com `lead_id` n\u00E3o entra na conta e n\u00E3o move status nenhum \u2014\n-- por isso o gatilho procura o neg\u00F3cio do lead e o carimba.\n--\n-- \u2500\u2500 uma atividade por CONVERSA E POR DIA, n\u00E3o por mensagem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- Uma conversa de trinta mensagens n\u00E3o s\u00E3o trinta contatos: \u00E9 um. Registrar\n-- cada mensagem encheria a linha do tempo de ru\u00EDdo e afogaria o que importa \u2014\n-- a liga\u00E7\u00E3o, a visita, a proposta. Ent\u00E3o a primeira mensagem do dia abre a\n-- atividade e as seguintes ATUALIZAM a mesma linha (o corpo mais recente, e o\n-- `outcome` mais forte do dia).\n--\n-- Atualizar tamb\u00E9m dispara o `crm_activity_restatus` (o gatilho \u00E9 INSERT OR\n-- UPDATE), ent\u00E3o uma resposta que chega depois de um envio move o status no\n-- mesmo dia, que \u00E9 o comportamento que se quer.\n--\n-- \u2500\u2500 e se o contato n\u00E3o for um lead \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `contact_person_id` \u00E9 nulo enquanto ningu\u00E9m casou o telefone com uma pessoa.\n-- A\u00ED n\u00E3o h\u00E1 a quem pendurar o fato e a fun\u00E7\u00E3o sai calada. N\u00E3o inventa lead: a\n-- caixa recebe mensagem de entregador, de banco e de engano, e cada um deles\n-- viraria um card no funil de algu\u00E9m.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE FUNCTION public.conversations_log_crm_activity() RETURNS trigger\n LANGUAGE plpgsql SECURITY DEFINER\n SET search_path TO ''\n AS $$\nDECLARE\n v_tenant uuid;\n v_person uuid;\n v_canal text;\n v_deal uuid;\n v_outcome text;\n v_titulo text;\n v_corpo text;\n v_dia date := (NEW.at AT TIME ZONE 'UTC')::date;\n v_ativ uuid;\nBEGIN\n -- Sem CRM montado n\u00E3o h\u00E1 o que escrever. `to_regclass` em vez de refer\u00EAncia\n -- direta porque conversas \u00E9 plugin OPCIONAL e pode viver num cluster que n\u00E3o\n -- tem funil nenhum.\n IF to_regclass('public.plg_crm_activities') IS NULL THEN RETURN NULL; END IF;\n\n SELECT c.tenant_id, c.contact_person_id, c.channel\n INTO v_tenant, v_person, v_canal\n FROM public.plg_conversations c WHERE c.id = NEW.conversation_id;\n\n IF v_person IS NULL THEN RETURN NULL; END IF;\n\n SELECT d.id INTO v_deal\n FROM public.plg_crm_deals d\n WHERE d.tenant_id = v_tenant AND d.lead_id = v_person\n ORDER BY d.created_at DESC LIMIT 1;\n\n IF NEW.direction = 'inbound' THEN\n v_outcome := 'replied';\n v_titulo := 'Respondeu no ' || coalesce(v_canal, 'canal');\n ELSE\n v_outcome := 'left_message';\n v_titulo := 'Mensagem enviada no ' || coalesce(v_canal, 'canal');\n END IF;\n\n v_corpo := left(coalesce(NEW.body, ''), 2000);\n\n -- A atividade do dia, se j\u00E1 houver.\n SELECT a.id INTO v_ativ\n FROM public.plg_crm_activities a\n WHERE a.tenant_id = v_tenant\n AND a.lead_id = v_person\n AND a.activity_type = 'whatsapp'\n AND (a.completed_at AT TIME ZONE 'UTC')::date = v_dia\n ORDER BY a.completed_at DESC LIMIT 1;\n\n IF v_ativ IS NULL THEN\n INSERT INTO public.plg_crm_activities (\n tenant_id, deal_id, lead_id, contact_id, activity_type,\n title, description, outcome, completed_at, created_by_name\n ) VALUES (\n v_tenant, v_deal, v_person, v_person, 'whatsapp',\n v_titulo, v_corpo, v_outcome, NEW.at,\n -- Quem fez foi a caixa, n\u00E3o uma pessoa: o autor em uuid seria mentira,\n -- e \"(sistema)\" \u00E9 a resposta honesta para quem l\u00EA a linha do tempo.\n 'Caixa de entrada'\n );\n ELSE\n UPDATE public.plg_crm_activities a SET\n -- `replied` NUNCA \u00E9 rebaixado por um envio posterior: o lead ter\n -- respondido \u00E9 o fato do dia, e mandar outra mensagem depois n\u00E3o o\n -- desfaz.\n outcome = CASE WHEN a.outcome = 'replied' THEN 'replied' ELSE v_outcome END,\n title = CASE WHEN a.outcome = 'replied' THEN a.title ELSE v_titulo END,\n description = v_corpo,\n completed_at = NEW.at,\n deal_id = coalesce(a.deal_id, v_deal)\n WHERE a.id = v_ativ;\n END IF;\n\n RETURN NULL;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_log_crm_activity() FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.conversations_log_crm_activity() TO authenticated, service_role;\n\nCOMMENT ON FUNCTION public.conversations_log_crm_activity() IS\n 'Uma mensagem vira contato na linha do tempo do lead e do neg\u00F3cio, e o status do card deriva disso pelo gatilho da crm/020. Uma atividade por conversa por dia (005).';\n\n-- AFTER: falhar ao registrar no CRM n\u00E3o pode recusar uma mensagem que j\u00E1\n-- chegou do provedor. A caixa \u00E9 a fonte; o funil \u00E9 quem escuta.\nDROP TRIGGER IF EXISTS conversations_log_crm_activity ON public.plg_conversation_messages;\nCREATE TRIGGER conversations_log_crm_activity\n AFTER INSERT ON public.plg_conversation_messages\n FOR EACH ROW EXECUTE FUNCTION public.conversations_log_crm_activity();\n\n-- O \u00EDndice que a busca da atividade do dia usa. Sem ele, cada mensagem varre\n-- as atividades do inquilino inteiro \u2014 e mensagem \u00E9 o que mais chega aqui.\nDO $$\nBEGIN\n -- Guarda pela mesma raz\u00E3o da fun\u00E7\u00E3o: conversas \u00E9 opcional e pode viver sem\n -- CRM. Um \u00EDndice sobre tabela que n\u00E3o existe recusa o arquivo inteiro.\n IF to_regclass('public.plg_crm_activities') IS NULL THEN RETURN; END IF;\n CREATE INDEX IF NOT EXISTS plg_crm_activities_lead_day\n ON public.plg_crm_activities (tenant_id, lead_id, activity_type, completed_at DESC);\nEND $$;\n";
7
7
  export declare const MIGRATION_006_A_CAIXA_AVISA_QUE_CHEGOU = "-- ---------------------------------------------------------------------------\n-- 006_a_caixa_avisa_que_chegou.sql\n--\n-- \u2500\u2500 o sil\u00EAncio, e de onde ele vinha \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- A caixa recebia mensagens e ningu\u00E9m era avisado. Sem som, sem contagem no\n-- menu, sem destaque na lista \u2014 e a suspeita natural \u00E9 que falta a tela que\n-- avisa. N\u00E3o faltava: o evento nunca sa\u00EDa do banco.\n--\n-- `plg_conversation_messages` e `plg_conversations` n\u00E3o estavam em\n-- `supabase_realtime`. A publica\u00E7\u00E3o existe (`puballtables = false`, ou seja,\n-- cada tabela entra nomeada) e nenhuma das duas fora nomeada. Qualquer cliente\n-- que assinasse `postgres_changes` sobre elas ficaria subscrito para sempre a\n-- um canal por onde nada passa \u2014 e subscrever com sucesso a um canal mudo \u00E9 o\n-- modo de falhar mais convincente que h\u00E1: o c\u00F3digo parece certo, o console\n-- fica limpo, e nada acontece.\n--\n-- \u2500\u2500 as duas, e n\u00E3o s\u00F3 as mensagens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- A mensagem \u00E9 o que dispara o alerta; a CONVERSA \u00E9 o que carrega o estado que\n-- a lista pinta (`status`, `unread_count`, `last_message_direction`). Publicar\n-- s\u00F3 a primeira daria o som sem o badge parar de mentir depois de algu\u00E9m ler.\n--\n-- \u2500\u2500 o RLS continua a valer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- Publicar n\u00E3o abre nada: o Realtime do Supabase aplica as pol\u00EDticas da tabela\n-- por assinante, e quem n\u00E3o pode ler a linha n\u00E3o a recebe. O que a publica\u00E7\u00E3o\n-- decide \u00E9 se o evento EXISTE, n\u00E3o quem o v\u00EA.\n--\n-- \u2500\u2500 idempotente, e silenciosa onde n\u00E3o se aplica \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `ALTER PUBLICATION ... ADD TABLE` rebenta se a tabela j\u00E1 l\u00E1 est\u00E1, e a\n-- publica\u00E7\u00E3o `supabase_realtime` s\u00F3 existe num Postgres da Supabase. As duas\n-- condi\u00E7\u00F5es s\u00E3o verificadas antes, porque esta migration tamb\u00E9m roda no banco\n-- de teste, que n\u00E3o tem nenhuma delas.\n-- ---------------------------------------------------------------------------\n\nDO $$\nDECLARE\n t text;\n v_n integer := 0;\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_publication WHERE pubname = 'supabase_realtime') THEN\n RAISE NOTICE 'conversations/006: sem publica\u00E7\u00E3o supabase_realtime \u2014 nada a fazer';\n RETURN;\n END IF;\n\n FOREACH t IN ARRAY ARRAY['plg_conversation_messages', 'plg_conversations'] LOOP\n CONTINUE WHEN to_regclass('public.' || t) IS NULL;\n CONTINUE WHEN EXISTS (\n SELECT 1 FROM pg_publication_tables\n WHERE pubname = 'supabase_realtime' AND schemaname = 'public' AND tablename = t);\n EXECUTE format('ALTER PUBLICATION supabase_realtime ADD TABLE public.%I', t);\n v_n := v_n + 1;\n END LOOP;\n\n RAISE NOTICE 'conversations/006: % tabela(s) publicada(s) no realtime', v_n;\nEND $$;\n\n-- `REPLICA IDENTITY FULL` na conversa: sem ela o UPDATE chega com a chave\n-- prim\u00E1ria e mais nada, e a tela n\u00E3o consegue saber que `unread_count` mudou \u2014\n-- que \u00E9 exatamente o UPDATE que interessa aqui. Na tabela de mensagens n\u00E3o \u00E9\n-- preciso: o que importa \u00E9 o INSERT, e esse vem inteiro de qualquer forma.\nDO $$\nBEGIN\n IF to_regclass('public.plg_conversations') IS NOT NULL THEN\n ALTER TABLE public.plg_conversations REPLICA IDENTITY FULL;\n END IF;\nEND $$;\n\n-- \u2500\u2500 e a conta de n\u00E3o lidas, que ningu\u00E9m mantinha \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `unread_count` estava em ZERO nas oito conversas do cluster, incluindo as\n-- quatro cuja \u00FAltima mensagem \u00E9 de entrada. N\u00E3o era um zero verdadeiro: n\u00E3o h\u00E1\n-- (n\u00E3o havia) nada que a incremente. A coluna existe desde a baseline, a tela\n-- da lista decide o destaque por ela (`unreadCount > 0`) e a RPC de marcar como\n-- lida zera-a \u2014 s\u00F3 faltava o lado que soma.\n--\n-- Por isso `isWaitingOnUs` recorre a `lastMessageDirection` quando a conta \u00E9\n-- zero: foi escrita j\u00E1 sabendo que a conta n\u00E3o era de fiar. Esta migration\n-- torna-a de fiar, e a recorr\u00EAncia volta a ser o que devia ser \u2014 uma rede, e\n-- n\u00E3o o caminho normal.\n--\n-- S\u00F3 o que CHEGA conta. Somar o que n\u00F3s enviamos faria a caixa dizer que h\u00E1\n-- doze por ler logo depois de algu\u00E9m responder doze vezes.\nCREATE OR REPLACE FUNCTION public.plg_conversations_bump_unread() RETURNS trigger\n LANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\n AS $$\nBEGIN\n IF NEW.direction IS DISTINCT FROM 'inbound' THEN RETURN NULL; END IF;\n UPDATE public.plg_conversations\n SET unread_count = coalesce(unread_count, 0) + 1\n WHERE id = NEW.conversation_id;\n RETURN NULL;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.plg_conversations_bump_unread() FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.plg_conversations_bump_unread() TO authenticated, service_role;\n\nCOMMENT ON FUNCTION public.plg_conversations_bump_unread() IS\n 'Soma uma n\u00E3o lida por mensagem que CHEGA. A coluna existia e era zerada ao ler, mas nada a incrementava (006).';\n\n-- AFTER, como o irm\u00E3o que carimba a dire\u00E7\u00E3o: falhar a contar n\u00E3o pode recusar\n-- uma mensagem que o provedor j\u00E1 entregou.\nDROP TRIGGER IF EXISTS plg_conversation_messages_bump_unread ON public.plg_conversation_messages;\nCREATE TRIGGER plg_conversation_messages_bump_unread\n AFTER INSERT ON public.plg_conversation_messages\n FOR EACH ROW EXECUTE FUNCTION public.plg_conversations_bump_unread();\n\n-- O retroativo, e o que ele pode e n\u00E3o pode saber.\n--\n-- N\u00E3o h\u00E1 coluna de \"lido at\u00E9 quando\" nesta tabela \u2014 `markRead` zera a conta e\n-- mais nada. Ent\u00E3o n\u00E3o d\u00E1 para reconstituir quantas mensagens ficaram por ler:\n-- o que d\u00E1 para saber \u00E9 que a conversa EST\u00C1 \u00E0 espera, porque a \u00FAltima mensagem\n-- dela \u00E9 de entrada.\n--\n-- Nesse caso a conta vira 1. Um n\u00FAmero inventado seria pior que um n\u00FAmero\n-- modesto: 1 diz \"h\u00E1 algo aqui\", que \u00E9 verdade e \u00E9 o que faz a linha destacar;\n-- dizer \"7\" alegaria uma precis\u00E3o que esta tabela nunca guardou. A partir de\n-- agora o gatilho conta de verdade.\nUPDATE public.plg_conversations\n SET unread_count = 1\n WHERE last_message_direction = 'inbound'\n AND status = 'open'\n AND coalesce(unread_count, 0) = 0;\n";
8
+ export declare const MIGRATION_007_A_CONVERSA_DIZ_DE_QUEM_E_O_LEAD = "-- ---------------------------------------------------------------------------\n-- 007_a_conversa_diz_de_quem_e_o_lead.sql\n--\n-- \u2500\u2500 duas coisas chamadas \"respons\u00E1vel\" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- A caixa j\u00E1 mostra um \"Respons\u00E1vel\", e ele \u00E9 `plg_conversations.assigned_to`:\n-- quem est\u00E1 ATENDENDO aquela conversa. N\u00E3o \u00E9 o dono do lead.\n--\n-- Os dois convivem e precisam conviver: a Carolina \u00E9 dona do lead, e quem\n-- responde a mensagem que chegou \u00E0s 2h da manh\u00E3 pode ser outra pessoa. Colapsar\n-- num s\u00F3 campo faria uma das duas perguntas ficar sem resposta \u2014 e a que\n-- desapareceria \u00E9 a do funil, que \u00E9 a que decide quem faz o follow-up.\n--\n-- Esta view acrescenta a SEGUNDA, com nome pr\u00F3prio (`lead_owner_*`), sem tocar\n-- na primeira.\n--\n-- \u2500\u2500 por que view, e n\u00E3o uma coluna na conversa \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- O dono do lead muda \u2014 algu\u00E9m clica \"Assumir\" no funil e a carteira troca de\n-- m\u00E3os. Uma coluna copiada aqui seria uma segunda verdade que envelhece em\n-- sil\u00EAncio: a conversa diria Carolina depois de o lead j\u00E1 ser do Julio, e\n-- ningu\u00E9m saberia dizer qual das duas telas est\u00E1 certa.\n--\n-- \u00C9 exatamente o problema que a 047 acabou de resolver do outro lado, onde o\n-- dono estava escrito em dois lugares que n\u00E3o se falavam. Derivar \u00E9 o que\n-- impede a repeti\u00E7\u00E3o.\n--\n-- \u2500\u2500 e por que `v_leads` e n\u00E3o `plg_crm_deals` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `v_leads.assigned_to_id` j\u00E1 resolve a preced\u00EAncia entre o que a PESSOA\n-- declara (`metadata.assignedToId`) e o que o NEG\u00D3CIO diz (047). Ir direto ao\n-- neg\u00F3cio aqui reimplementaria essa regra, e as duas divergiriam no dia em que\n-- uma mudasse.\n--\n-- \u2500\u2500 o CRM \u00E9 opcional \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `plugin-conversations` roda em casa sem funil nenhum. `v_leads` \u00E9 da espinha\n-- (n\u00E3o do CRM), ent\u00E3o a depend\u00EAncia \u00E9 leg\u00EDtima \u2014 mas a jun\u00E7\u00E3o \u00E9 LEFT: uma\n-- conversa de quem n\u00E3o \u00E9 lead continua aparecendo, com o dono nulo.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE VIEW public.v_conversations WITH (security_invoker = true) AS\n SELECT c.*,\n l.assigned_to_id AS lead_owner_id,\n p.name AS lead_owner_name\n FROM public.plg_conversations c\n -- Pelo contato: \u00E9 `contact_person_id` que liga a conversa a uma pessoa da\n -- base. Nulo enquanto ningu\u00E9m casou o telefone com algu\u00E9m, e a\u00ED n\u00E3o h\u00E1\n -- lead a quem perguntar o dono.\n LEFT JOIN public.v_leads l\n ON l.id = c.contact_person_id\n -- O NOME do dono, que \u00E9 o que a tela mostra. O id sozinho obrigaria cada\n -- tela a resolver o mesmo de-para \u2014 foi o que fez o filtro de leads exibir\n -- UUID em produ\u00E7\u00E3o por um dia.\n LEFT JOIN public.v_team_members m\n ON m.user_id::text = l.assigned_to_id AND m.tenant_id = c.tenant_id\n LEFT JOIN public.people p\n ON p.id = m.person_id;\n\nCOMMENT ON VIEW public.v_conversations IS\n 'A conversa com o DONO DO LEAD junto \u2014 diferente de `assigned_to`, que \u00E9 quem atende a conversa. Derivado de `v_leads` para n\u00E3o virar uma segunda verdade que envelhece (007).';\n";
8
9
  export declare const MIGRATIONS: Array<{
9
10
  id: string;
10
11
  sql: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,2rhCAkgBlC,CAAA;AAED,eAAO,MAAM,gDAAgD,i0LAuG5D,CAAA;AAED,eAAO,MAAM,oDAAoD,m5bAuOhE,CAAA;AAED,eAAO,MAAM,0DAA0D,81rBAwWtE,CAAA;AAED,eAAO,MAAM,gEAAgE,+lJA2E5E,CAAA;AAED,eAAO,MAAM,yDAAyD,w1RAmKrE,CAAA;AAED,eAAO,MAAM,sCAAsC,mtOA6HlD,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAQzD,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,2rhCAkgBlC,CAAA;AAED,eAAO,MAAM,gDAAgD,i0LAuG5D,CAAA;AAED,eAAO,MAAM,oDAAoD,m5bAuOhE,CAAA;AAED,eAAO,MAAM,0DAA0D,81rBAwWtE,CAAA;AAED,eAAO,MAAM,gEAAgE,+lJA2E5E,CAAA;AAED,eAAO,MAAM,yDAAyD,w1RAmKrE,CAAA;AAED,eAAO,MAAM,sCAAsC,mtOA6HlD,CAAA;AAED,eAAO,MAAM,6CAA6C,qmIA6DzD,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CASzD,CAAA"}
package/dist/store.d.ts CHANGED
@@ -17,6 +17,8 @@ export interface ConversationsUIState {
17
17
  selectedId: string | null;
18
18
  refreshMessages: () => Promise<void>;
19
19
  channelFilter: Channel | 'all';
20
+ /** Recorte por DONO DO LEAD. `'none'` é a fila sem dono. */
21
+ leadOwnerFilter?: string | 'none';
20
22
  search: string;
21
23
  loading: boolean;
22
24
  sending: boolean;
@@ -24,6 +26,7 @@ export interface ConversationsUIState {
24
26
  select(id: string): Promise<void>;
25
27
  deselect(): void;
26
28
  setChannelFilter(channel: Channel | 'all'): Promise<void>;
29
+ setLeadOwnerFilter(ownerId?: string | 'none'): Promise<void>;
27
30
  setSearch(search: string): Promise<void>;
28
31
  create(input: CreateConversationInput): Promise<Conversation>;
29
32
  /** Abrir a conversa DESTA pessoa neste canal, criando-a se ainda não houver. */
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EACV,YAAY,EACZ,OAAO,EACP,OAAO,EACP,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,SAAS,CAAA;AAEhB;kEACkE;AAClE,MAAM,WAAW,kBAAkB;IACjC;4EACwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB;AAUD,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,GAAG,KAAK,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAEhB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,QAAQ,IAAI,IAAI,CAAA;IAChB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC7D,gFAAgF;IAChF,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrD;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,qBAAqB,GAC9B,QAAQ,CAAC,oBAAoB,CAAC,CAyKhC"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EACV,YAAY,EACZ,OAAO,EACP,OAAO,EACP,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,SAAS,CAAA;AAEhB;kEACkE;AAClE,MAAM,WAAW,kBAAkB;IACjC;4EACwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB;AAUD,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,GAAG,KAAK,CAAA;IAC9B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAEhB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,QAAQ,IAAI,IAAI,CAAA;IAChB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC7D,gFAAgF;IAChF,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrD;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,qBAAqB,GAC9B,QAAQ,CAAC,oBAAoB,CAAC,CAgLhC"}
package/dist/types.d.ts CHANGED
@@ -30,7 +30,13 @@ export interface Conversation {
30
30
  lastMessageDirection?: MessageDirection;
31
31
  unreadCount: number;
32
32
  status: ConversationStatus;
33
+ /** Quem ATENDE esta conversa. */
33
34
  assignedTo?: string;
35
+ /** Quem é dono do LEAD — outra pergunta, e a que decide o follow-up.
36
+ * Derivado de `v_leads` pela view `v_conversations`: copiar aqui faria uma
37
+ * segunda verdade que envelhece quando alguém clica "Assumir" no funil. */
38
+ leadOwnerId?: string;
39
+ leadOwnerName?: string;
34
40
  /** Tailwind-ish accent for the avatar bubble */
35
41
  accent: string;
36
42
  tags: string[];
@@ -56,6 +62,10 @@ export interface ListConversationsQuery {
56
62
  channel?: Channel | 'all';
57
63
  status?: ConversationStatus | 'all';
58
64
  search?: string;
65
+ /** Recorta pelo DONO DO LEAD. `'none'` traz quem ainda não é lead de
66
+ * ninguém — a fila que precisa de dono, e a pergunta que a caixa sozinha
67
+ * não respondia. */
68
+ leadOwnerId?: string | 'none';
59
69
  }
60
70
  export interface SendMessageInput {
61
71
  conversationId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAA;AAE5E,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE9D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,CAAA;AAErD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;;OAWG;IACH,oBAAoB,CAAC,EAAE,gBAAgB,CAAA;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,kBAAkB,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,EAAE,CAAA;IAEd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,gBAAgB,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd;;6EAEyE;IACzE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACzB,MAAM,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAMlD,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAIjE"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAA;AAE5E,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE9D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,CAAA;AAErD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;;OAWG;IACH,oBAAoB,CAAC,EAAE,gBAAgB,CAAA;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,kBAAkB,CAAA;IAC1B,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;gFAE4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,EAAE,CAAA;IAEd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,gBAAgB,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd;;6EAEyE;IACzE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACzB,MAAM,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;yBAEqB;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAMlD,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAIjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContactPanel.d.ts","sourceRoot":"","sources":["../../src/views/ContactPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAkB5C,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE;IAC5D,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBA0EA"}
1
+ {"version":3,"file":"ContactPanel.d.ts","sourceRoot":"","sources":["../../src/views/ContactPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAkB5C,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE;IAC5D,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBAmFA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationList.d.ts","sourceRoot":"","sources":["../../src/views/ConversationList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBA6HrE"}
1
+ {"version":3,"file":"ConversationList.d.ts","sourceRoot":"","sources":["../../src/views/ConversationList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAkKrE"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "status": "preview",
5
5
  "dependencies": []
6
6
  },
7
- "version": "0.15.0",
7
+ "version": "0.16.0",
8
8
  "description": "[experimental] Fayz SDK — unified conversations / omni-channel inbox plugin",
9
9
  "type": "module",
10
10
  "sideEffects": false,
@@ -27,9 +27,9 @@
27
27
  "lucide-react": ">=0.400.0 <1.0.0"
28
28
  },
29
29
  "dependencies": {
30
- "@fayz-ai/core": "^0.26.0",
31
- "@fayz-ai/ui": "^0.26.0",
32
- "@fayz-ai/admin": "^0.27.0"
30
+ "@fayz-ai/core": "^0.28.0",
31
+ "@fayz-ai/ui": "^0.28.0",
32
+ "@fayz-ai/admin": "^0.28.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/react": "^18.3.0",