@fayz-ai/plugin-conversations 0.17.1 → 0.18.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":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAwBpD,MAAM,WAAW,2BAA2B;IAC1C,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAA;IAC9C,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AA8CD,wBAAgB,mCAAmC,CACjD,MAAM,CAAC,EAAE,2BAA2B,GACnC,qBAAqB,CAuRvB"}
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAwBpD,MAAM,WAAW,2BAA2B;IAC1C,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAA;IAC9C,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAmDD,wBAAgB,mCAAmC,CACjD,MAAM,CAAC,EAAE,2BAA2B,GACnC,qBAAqB,CAuRvB"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React11 from 'react';
2
2
  import React11__default, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
3
- import { Loader2, MessageSquare, AlertTriangle, Pause, Play, Send, RefreshCw, Plus, Clock, CheckCircle2, Check, ShieldCheck, ShieldAlert, QrCode, Zap, ExternalLink, SquarePen, Globe, Mail, Instagram, Phone, Search, User, Inbox, CheckCheck, ChevronLeft, Archive, PanelRight, X, MapPin, Tag, StickyNote, Link2, MoreVertical, Snowflake, Sun, Flame, Building2 } from 'lucide-react';
3
+ import { Loader2, MessageSquare, AlertTriangle, Pause, Play, Send, RefreshCw, Plus, Clock, CheckCircle2, Check, ShieldCheck, ShieldAlert, QrCode, Zap, ExternalLink, SquarePen, Globe, Mail, Instagram, Phone, Search, User, Inbox, CheckCheck, ChevronLeft, Archive, PanelRight, X, MapPin, Tag, StickyNote, Link2, MoreVertical, Snowflake, Sun, Flame, Building2, UserRound, Copy } from 'lucide-react';
4
4
  import { toast, Button, Input, defineKpiWidget, defineTableWidget, PageHeaderActions, cn, Badge, FilterChip, Skeleton, KpiCard, TableWidget } from '@fayz-ai/ui';
5
5
  import { createConnectionStore, connectionRuns, connectionStatus, getActiveTenantId, getSupabaseClientOptional, useActiveTenantId, registerTranslations, useTranslation, countByTenant, CONNECTOR_RUNTIME_TOKEN_HEADER, connectorRuntimeToken, errorMessage } from '@fayz-ai/core';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1199,6 +1199,51 @@ function MessageMenu({ m }) {
1199
1199
  ] }) })
1200
1200
  ] });
1201
1201
  }
1202
+ function ContactCard({ contacts, outbound }) {
1203
+ const [copied, setCopied] = React11__default.useState(null);
1204
+ const copy = (value) => {
1205
+ void navigator.clipboard?.writeText(value);
1206
+ setCopied(value);
1207
+ window.setTimeout(() => setCopied((c) => c === value ? null : c), 1500);
1208
+ };
1209
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: contacts.map((c, i) => /* @__PURE__ */ jsxs(
1210
+ "div",
1211
+ {
1212
+ className: cn(
1213
+ "flex items-start gap-2.5 rounded-xl px-2.5 py-2",
1214
+ outbound ? "bg-white/15" : "bg-muted"
1215
+ ),
1216
+ children: [
1217
+ /* @__PURE__ */ jsx(UserRound, { className: cn("mt-0.5 h-4 w-4 shrink-0", outbound ? "text-white/80" : "text-muted-foreground") }),
1218
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1219
+ /* @__PURE__ */ jsx("p", { className: "truncate font-medium", children: c.name }),
1220
+ c.phones.map((p) => /* @__PURE__ */ jsxs(
1221
+ "button",
1222
+ {
1223
+ type: "button",
1224
+ onClick: () => copy(p.display),
1225
+ className: cn(
1226
+ "group/phone mt-0.5 flex items-center gap-1.5 text-left text-[13px] tabular-nums",
1227
+ outbound ? "text-white/90 hover:text-white" : "text-muted-foreground hover:text-foreground"
1228
+ ),
1229
+ children: [
1230
+ p.display,
1231
+ copied === p.display ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3 opacity-0 transition-opacity group-hover/phone:opacity-100" })
1232
+ ]
1233
+ },
1234
+ p.digits
1235
+ ))
1236
+ ] })
1237
+ ]
1238
+ },
1239
+ `${c.name}-${i}`
1240
+ )) });
1241
+ }
1242
+ function MessageBody({ m, outbound }) {
1243
+ const contacts = m.messageType === "contact" ? m.payload?.contacts ?? [] : [];
1244
+ if (contacts.length > 0) return /* @__PURE__ */ jsx(ContactCard, { contacts, outbound });
1245
+ return /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words", children: m.body });
1246
+ }
1202
1247
  function buildRows(messages) {
1203
1248
  const rows = [];
1204
1249
  let lastDay = "";
@@ -1317,7 +1362,7 @@ function MessageThread({ selected, onTogglePanel, panelOpen, onBack, className }
1317
1362
  ),
1318
1363
  style: outbound ? { backgroundColor: failed ? "hsl(var(--destructive))" : accent.color } : void 0,
1319
1364
  children: [
1320
- /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words", children: m.body }),
1365
+ /* @__PURE__ */ jsx(MessageBody, { m, outbound }),
1321
1366
  /* @__PURE__ */ jsxs("div", { className: cn("mt-0.5 flex items-center justify-end gap-1 text-[10px]", outbound ? "text-white/70" : "text-muted-foreground"), children: [
1322
1367
  new Date(m.at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
1323
1368
  /* @__PURE__ */ jsx(DeliveryMark, { m })
@@ -2078,7 +2123,12 @@ function mapMessage(r) {
2078
2123
  at: r.at ?? "",
2079
2124
  author: r.author ?? "",
2080
2125
  deliveryStatus: r.delivery_status ?? null,
2081
- providerMessageId: r.provider_message_id ?? null
2126
+ providerMessageId: r.provider_message_id ?? null,
2127
+ // NULL stays null rather than defaulting to 'text'. A row written before
2128
+ // migration 012 genuinely does not know what it was, and the rows that most
2129
+ // need saying so are the empty ones that a 'text' default would call fine.
2130
+ messageType: r.message_type ?? null,
2131
+ payload: r.payload ?? null
2082
2132
  };
2083
2133
  }
2084
2134
  function createSupabaseConversationsProvider(config) {
@@ -5483,6 +5533,68 @@ END $$;
5483
5533
  COMMENT ON FUNCTION public.plg_conversations_anunciar_chegada() IS
5484
5534
  'Toca a campainha no t\xF3pico \`conversas:<tenant>\` \u2014 s\xF3 o id da conversa. O conte\xFAdo \xE9 lido da tabela, com RLS (011).';
5485
5535
  `;
5536
+ var MIGRATION_012_A_RICH_MESSAGE_KEEPS_ITS_SHAPE = `-- ---------------------------------------------------------------------------
5537
+ -- 012_a_rich_message_keeps_its_shape.sql
5538
+ --
5539
+ -- \u2500\u2500 what was lost, and how \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
5540
+ --
5541
+ -- \`plg_conversation_messages\` has exactly one content column: \`body text\`. A
5542
+ -- WhatsApp message that is not text therefore arrives flattened to whatever
5543
+ -- string the ingest could make of it \u2014 and when the ingest could make none, to
5544
+ -- the empty string, which renders as an empty bubble.
5545
+ --
5546
+ -- On 2026-09-22 a lead sent his finance director's contact card. It stored as
5547
+ -- \`body = ''\`. He then sent a screenshot of the same card; that stored as
5548
+ -- \`body = ''\` too. The fact the conversation existed to obtain \u2014 a name and a
5549
+ -- phone number \u2014 was in the payload both times and in the row neither time.
5550
+ --
5551
+ -- Recovering it meant going back to the Evolution instance and reading the
5552
+ -- provider's own copy. That worked, but it is not a recovery path: Evolution
5553
+ -- prunes, the instance is local-only, and a row that needs a second system to
5554
+ -- be legible is not a record.
5555
+ --
5556
+ -- \u2500\u2500 the two columns \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\u2500
5557
+ --
5558
+ -- \`message_type\` names what the provider sent \u2014 text, contact, image, audio,
5559
+ -- document, sticker, location, reaction, unknown. The inbox needs it to choose
5560
+ -- a bubble, and \`unknown\` is the one that makes the NEXT gap visible in a query
5561
+ -- instead of in a complaint.
5562
+ --
5563
+ -- \`payload\` keeps the part that does not survive flattening: the vCard's parsed
5564
+ -- name and numbers, a media mimetype and size, a location's coordinates. Not
5565
+ -- the whole provider envelope \u2014 that carries decryption keys and a base64
5566
+ -- thumbnail, neither of which belongs in a row that every member of the tenant
5567
+ -- can read.
5568
+ --
5569
+ -- Deliberately NOT stored here: delivery status. It changes after the fact \u2014
5570
+ -- sent, delivered, read \u2014 and \`conversations-provider\`'s \`message_detail\` reads
5571
+ -- it live for that reason. \`payload\` is the shape of the message, which does
5572
+ -- not change once it arrives.
5573
+ --
5574
+ -- \u2500\u2500 why both are nullable, and nothing is backfilled \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5575
+ --
5576
+ -- Every row written before this migration has neither, and no default can
5577
+ -- invent them: \`message_type\` would have to guess, and guessing 'text' for the
5578
+ -- three empty bubbles already in the table would assert something false about
5579
+ -- exactly the rows this exists to fix. A reader that treats NULL as "written
5580
+ -- before 012" is correct; a reader that treats '' as text is the bug.
5581
+ --
5582
+ -- Table-level \`GRANT ALL ... TO authenticated\` already covers new columns, and
5583
+ -- the four tenant policies are row-scoped, so neither needs restating.
5584
+ --
5585
+ -- Idempotent and replay-safe.
5586
+ -- ---------------------------------------------------------------------------
5587
+
5588
+ ALTER TABLE public.plg_conversation_messages
5589
+ ADD COLUMN IF NOT EXISTS message_type text,
5590
+ ADD COLUMN IF NOT EXISTS payload jsonb;
5591
+
5592
+ COMMENT ON COLUMN public.plg_conversation_messages.message_type IS
5593
+ 'What the provider sent: text, contact, image, video, audio, document, sticker, location, reaction, unknown. NULL means the row predates 012 (#504).';
5594
+
5595
+ COMMENT ON COLUMN public.plg_conversation_messages.payload IS
5596
+ 'The structured remainder that \`body\` cannot hold \u2014 parsed vCards, media metadata, coordinates. Never credentials, thumbnails or decryption keys; never delivery status, which is read live (#504).';
5597
+ `;
5486
5598
  var MIGRATIONS = [
5487
5599
  { id: "000_baseline", sql: MIGRATION_000_BASELINE },
5488
5600
  { id: "001_a_caixa_sabe_quem_falou_por_ultimo", sql: MIGRATION_001_A_CAIXA_SABE_QUEM_FALOU_POR_ULTIMO },
@@ -5495,7 +5607,8 @@ var MIGRATIONS = [
5495
5607
  { id: "008_dar_por_vista_e_uma_decisao", sql: MIGRATION_008_DAR_POR_VISTA_E_UMA_DECISAO },
5496
5608
  { id: "009_a_mensagem_inteira_atravessa_o_realtime", sql: MIGRATION_009_A_MENSAGEM_INTEIRA_ATRAVESSA_O_REALTIME },
5497
5609
  { id: "010_a_chegada_e_anunciada_por_conta", sql: MIGRATION_010_A_CHEGADA_E_ANUNCIADA_POR_CONTA },
5498
- { id: "011_o_anuncio_e_uma_campainha_nao_um_recado", sql: MIGRATION_011_O_ANUNCIO_E_UMA_CAMPAINHA_NAO_UM_RECADO }
5610
+ { id: "011_o_anuncio_e_uma_campainha_nao_um_recado", sql: MIGRATION_011_O_ANUNCIO_E_UMA_CAMPAINHA_NAO_UM_RECADO },
5611
+ { id: "012_a_rich_message_keeps_its_shape", sql: MIGRATION_012_A_RICH_MESSAGE_KEEPS_ITS_SHAPE }
5499
5612
  ];
5500
5613
 
5501
5614
  // src/index.ts