@fayz-ai/plugin-conversations 0.10.0 → 0.11.1
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.
- package/README.md +3 -1
- package/dist/data/channels.d.ts +70 -0
- package/dist/data/channels.d.ts.map +1 -0
- package/dist/data/payment-preflight.d.ts +42 -0
- package/dist/data/payment-preflight.d.ts.map +1 -0
- package/dist/data/tables.d.ts +1 -0
- package/dist/data/tables.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1152 -28
- package/dist/index.js.map +1 -1
- package/dist/integrations/tyxter/connectorDef.d.ts +14 -0
- package/dist/integrations/tyxter/connectorDef.d.ts.map +1 -0
- package/dist/integrations/tyxter/data.d.ts +70 -0
- package/dist/integrations/tyxter/data.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +7 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/package.json +9 -7
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/types.ts","../src/lib/channel.ts","../src/views/shared.tsx","../src/views/ConversationList.tsx","../src/views/MessageThread.tsx","../src/views/ContactPanel.tsx","../src/views/NewConversationPanel.tsx","../src/views/InboxView.tsx","../src/views/ConversationsPage.tsx","../src/data/accents.ts","../src/data/mock.ts","../src/data/tables.ts","../src/data/supabase.ts","../src/store.ts","../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/migrations/index.ts","../src/index.ts"],"names":["React","jsx","jsxs","cn","InboxIcon","useTranslation","MessageSquare","Button","getSupabaseClientOptional"],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react'\nimport { useStore, type StoreApi } from 'zustand'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport type { EntityDef } from '@fayz-ai/core'\nimport type { ConversationsUIState } from './store'\n\n/**\n * App-tunable knobs the inbox UI reads. Kept tiny on purpose: everything here\n * describes the APP's data shape (which people are \"contacts\" in this vertical),\n * never product policy.\n */\nexport interface ResolvedConversationsConfig {\n /** `people.kind` the picker searches. */\n contactKind: string\n /** Search source for the contact picker. Defaults to the person archetype. */\n contactLookup?: EntityLookup\n /** The app's own contact entity — \"Create\" opens ITS form. Agenda's twin is `clientEntityDef`. */\n contactEntityDef?: EntityDef\n}\n\nexport const DEFAULT_CONVERSATIONS_CONFIG: ResolvedConversationsConfig = {\n contactKind: 'contact',\n}\n\nconst StoreContext = React.createContext<StoreApi<ConversationsUIState> | null>(null)\nconst ConfigContext = React.createContext<ResolvedConversationsConfig>(DEFAULT_CONVERSATIONS_CONFIG)\n\nexport function ConversationsContextProvider({\n store,\n config = DEFAULT_CONVERSATIONS_CONFIG,\n children,\n}: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n children?: React.ReactNode\n}) {\n return (\n <StoreContext.Provider value={store}>\n <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n </StoreContext.Provider>\n )\n}\n\nexport function useConversationsStore<T>(selector: (state: ConversationsUIState) => T): T {\n const store = React.useContext(StoreContext)\n if (!store) throw new Error('useConversationsStore must be used within ConversationsPage')\n return useStore(store, selector)\n}\n\nexport function useConversationsConfig(): ResolvedConversationsConfig {\n return React.useContext(ConfigContext)\n}\n","// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — domain types for the unified omni-channel\n// inbox (the GoHighLevel \"Conversations\" equivalent). Provider-agnostic.\n// ---------------------------------------------------------------------------\n\nexport type Channel = 'sms' | 'whatsapp' | 'instagram' | 'email' | 'webchat'\n\nexport type ConversationStatus = 'open' | 'snoozed' | 'closed'\n\nexport type MessageDirection = 'inbound' | 'outbound'\n\nexport interface Conversation {\n id: string\n contactName: string\n /**\n * `public.people` id when the thread is tied to a real contact record (the\n * compose modal resolves one via the shared ContactPicker). Absent for legacy\n * threads and for inbound messages from an unknown handle.\n */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel */\n contactHandle: string\n channel: Channel\n lastMessagePreview: string\n lastMessageAt: string\n unreadCount: number\n status: ConversationStatus\n assignedTo?: string\n /** Tailwind-ish accent for the avatar bubble */\n accent: string\n tags: string[]\n // Optional context shown in the contact panel.\n location?: string\n note?: string\n}\n\nexport interface Message {\n id: string\n conversationId: string\n channel: Channel\n direction: MessageDirection\n body: string\n at: string\n author: string\n}\n\nexport interface ListConversationsQuery {\n channel?: Channel | 'all'\n status?: ConversationStatus | 'all'\n search?: string\n}\n\nexport interface SendMessageInput {\n conversationId: string\n body: string\n}\n\nexport interface CreateConversationInput {\n contactName: string\n /** `public.people` id, when the contact was resolved/created by the picker. */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel. */\n contactHandle?: string\n channel: Channel\n /** Optional first outbound message; stamps preview + last_message_at. */\n firstMessage?: string\n /** Optional free-text note surfaced in the contact panel. */\n note?: string\n}\n\nexport const CHANNEL_LABELS: Record<Channel, string> = {\n sms: 'SMS',\n whatsapp: 'WhatsApp',\n instagram: 'Instagram',\n email: 'Email',\n webchat: 'Web Chat',\n}\n","import React from 'react'\nimport { MessageSquare, Phone, Instagram, Mail, Globe } from 'lucide-react'\nimport { CHANNEL_LABELS, type Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Shared channel visuals — one source of truth for the icon + accent color of\n// each channel, reused by the conversation list, thread header and contact\n// panel. Keeps the omni-channel inbox visually consistent.\n// ---------------------------------------------------------------------------\n\nexport const CHANNEL_ICON: Record<Channel, React.ComponentType<{ className?: string }>> = {\n sms: Phone,\n whatsapp: MessageSquare,\n instagram: Instagram,\n email: Mail,\n webchat: Globe,\n}\n\nexport interface ChannelAccent {\n /** Solid brand color (hex) — bubbles, dots, header chips. */\n color: string\n /** Soft tinted background classes for badges. */\n badge: string\n}\n\nexport const CHANNEL_ACCENT: Record<Channel, ChannelAccent> = {\n whatsapp: { color: '#22c55e', badge: 'bg-[#22c55e]/12 text-[#15803d] dark:text-[#4ade80]' },\n sms: { color: '#6366f1', badge: 'bg-[#6366f1]/12 text-[#4338ca] dark:text-[#a5b4fc]' },\n instagram: { color: '#ec4899', badge: 'bg-[#ec4899]/12 text-[#be185d] dark:text-[#f9a8d4]' },\n email: { color: '#0ea5e9', badge: 'bg-[#0ea5e9]/12 text-[#0369a1] dark:text-[#7dd3fc]' },\n webchat: { color: '#f59e0b', badge: 'bg-[#f59e0b]/12 text-[#b45309] dark:text-[#fcd34d]' },\n}\n\nexport { CHANNEL_LABELS }\n","import React from 'react'\nimport { cn } from '@fayz-ai/ui'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Small presentational helpers shared across the inbox panes.\n// ---------------------------------------------------------------------------\n\n/** Reactive media-query match — drives the responsive list/thread/panel layout. */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(\n () => typeof window !== 'undefined' && window.matchMedia(query).matches,\n )\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n const onChange = () => setMatches(mql.matches)\n onChange()\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [query])\n return matches\n}\n\nexport function initialsOf(name: string): string {\n return name.replace('@', '').split(/\\s+/).filter(Boolean).map((w) => w[0]).slice(0, 2).join('').toUpperCase()\n}\n\nexport function Avatar({ name, accent, size = 'md', channel }: {\n name: string\n accent: string\n size?: 'sm' | 'md' | 'lg'\n /** When set, renders a small channel glyph badge over the avatar. */\n channel?: Channel\n}) {\n const dims = size === 'lg' ? 'h-14 w-14 text-lg' : size === 'sm' ? 'h-9 w-9 text-xs' : 'h-10 w-10 text-sm'\n const Icon = channel ? CHANNEL_ICON[channel] : null\n return (\n <div className=\"relative shrink-0\">\n <div\n className={cn('flex items-center justify-center rounded-full font-semibold text-white', dims)}\n style={{ backgroundColor: accent }}\n >\n {initialsOf(name)}\n </div>\n {Icon && (\n <span\n className=\"absolute -bottom-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full ring-2 ring-card\"\n style={{ backgroundColor: CHANNEL_ACCENT[channel!].color }}\n >\n <Icon className=\"h-2.5 w-2.5 text-white\" />\n </span>\n )}\n </div>\n )\n}\n\nexport function ChannelBadge({ channel, className }: { channel: Channel; className?: string }) {\n const Icon = CHANNEL_ICON[channel]\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium',\n CHANNEL_ACCENT[channel].badge,\n className,\n )}\n >\n <Icon className=\"h-2.5 w-2.5\" />\n {CHANNEL_LABELS[channel]}\n </span>\n )\n}\n\nexport function relativeTime(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime()\n const mins = Math.round(diff / 60_000)\n if (mins < 1) return 'now'\n if (mins < 60) return `${mins}m`\n const hours = Math.round(mins / 60)\n if (hours < 24) return `${hours}h`\n const days = Math.round(hours / 24)\n if (days < 7) return `${days}d`\n return `${Math.round(days / 7)}w`\n}\n\n/** Day label for the thread date separators. */\nexport function dayLabel(iso: string): string {\n const d = new Date(iso)\n const now = new Date()\n const startOf = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime()\n const dayDiff = Math.round((startOf(now) - startOf(d)) / 86_400_000)\n if (dayDiff <= 0) return 'Today'\n if (dayDiff === 1) return 'Yesterday'\n if (dayDiff < 7) return d.toLocaleDateString([], { weekday: 'long' })\n return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: now.getFullYear() === d.getFullYear() ? undefined : 'numeric' })\n}\n","import React from 'react'\nimport { Search, Inbox as InboxIcon } from 'lucide-react'\nimport { Input, Skeleton, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport type { Channel } from '../types'\nimport { Avatar, ChannelBadge, relativeTime } from './shared'\n\nconst FILTERS: Array<Channel | 'all'> = ['all', 'whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\nexport function ConversationList({ className }: { className?: string }) {\n const t = useTranslation()\n const {\n conversations, selectedId, channelFilter, search, loading,\n select, setChannelFilter, setSearch,\n } = useConversationsStore((s) => s)\n\n return (\n <aside className={cn('w-full shrink-0 flex-col border-r border-border bg-card lg:w-[320px]', className)}>\n {/* No title: the shell header already names the page 24px away. Starting\n a conversation moved there too — it is the PAGE's action, not the list's. */}\n <div className=\"border-b border-border px-3 py-3\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t('conversations.list.search')}\n className=\"pl-8\"\n />\n </div>\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {FILTERS.map((id) => (\n <button\n key={id}\n onClick={() => setChannelFilter(id)}\n className={cn(\n 'rounded-full px-2.5 py-1 text-xs font-medium transition-colors',\n channelFilter === id\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n >\n {t(`conversations.filter.${id}`)}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {loading && conversations.length === 0 &&\n Array.from({ length: 6 }, (_, i) => (\n <div key={i} className=\"flex w-full items-start gap-3 border-b border-border/50 px-3 py-3\">\n <Skeleton className=\"h-10 w-10 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <Skeleton className=\"h-4 w-28\" />\n <Skeleton className=\"h-3 w-8\" />\n </div>\n <Skeleton className=\"mt-1.5 h-4 w-16 rounded-full\" />\n <Skeleton className=\"mt-1.5 h-3 w-3/4\" />\n </div>\n </div>\n ))}\n {!loading && conversations.length === 0 && (\n <div className=\"flex flex-col items-center gap-2 p-8 text-center text-muted-foreground\">\n <InboxIcon className=\"h-6 w-6\" />\n <p className=\"text-sm\">{t('conversations.list.empty')}</p>\n </div>\n )}\n {conversations.map((c) => {\n const active = c.id === selectedId\n const unread = c.unreadCount > 0\n return (\n <button\n key={c.id}\n onClick={() => select(c.id)}\n className={cn(\n 'flex w-full items-start gap-3 border-b border-border/50 px-3 py-3 text-left transition-colors',\n active ? 'bg-accent' : 'hover:bg-muted/50',\n )}\n >\n <Avatar name={c.contactName} accent={c.accent} channel={c.channel} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className={cn('truncate text-sm text-foreground', unread ? 'font-semibold' : 'font-medium')}>\n {c.contactName}\n </span>\n <span className={cn('shrink-0 text-[11px]', unread ? 'font-semibold text-primary' : 'text-muted-foreground')}>\n {relativeTime(c.lastMessageAt)}\n </span>\n </div>\n <div className=\"mt-1 flex items-center gap-2\">\n <ChannelBadge channel={c.channel} />\n {c.status !== 'open' && (\n <span className=\"text-[10px] uppercase tracking-wide text-muted-foreground/70\">\n {t(`conversations.status.${c.status}`)}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex items-center justify-between gap-2\">\n <span className={cn('truncate text-xs', unread ? 'text-foreground' : 'text-muted-foreground')}>\n {c.lastMessagePreview}\n </span>\n {unread && (\n <span className=\"flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground\">\n {c.unreadCount}\n </span>\n )}\n </div>\n </div>\n </button>\n )\n })}\n </div>\n </aside>\n )\n}\n","import React from 'react'\nimport { Send, Clock, Archive, PanelRight, MessageSquare, ChevronLeft } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation, Message } from '../types'\nimport { Avatar, ChannelBadge, dayLabel } from './shared'\n\n// Build a flat render list: day-separator rows interleaved with messages, and\n// each message flagged as the start of a new sender-run (WhatsApp grouping).\ntype Row =\n | { kind: 'day'; id: string; label: string }\n | { kind: 'msg'; id: string; message: Message; startsRun: boolean; endsRun: boolean }\n\nfunction buildRows(messages: Message[]): Row[] {\n const rows: Row[] = []\n let lastDay = ''\n messages.forEach((m, i) => {\n const day = new Date(m.at).toDateString()\n if (day !== lastDay) {\n rows.push({ kind: 'day', id: `day-${day}`, label: dayLabel(m.at) })\n lastDay = day\n }\n const prev = messages[i - 1]\n const next = messages[i + 1]\n const samePrev = prev && prev.direction === m.direction && new Date(prev.at).toDateString() === day\n const sameNext = next && next.direction === m.direction && new Date(next.at).toDateString() === day\n rows.push({ kind: 'msg', id: m.id, message: m, startsRun: !samePrev, endsRun: !sameNext })\n })\n return rows\n}\n\nexport function MessageThread({ selected, onTogglePanel, panelOpen, onBack, className }: {\n selected: Conversation\n onTogglePanel: () => void\n panelOpen: boolean\n onBack?: () => void\n className?: string\n}) {\n const t = useTranslation()\n const { messages, sending, send, setStatus } = useConversationsStore((s) => s)\n const [draft, setDraft] = React.useState('')\n const threadRef = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n threadRef.current?.scrollTo({ top: threadRef.current.scrollHeight, behavior: 'smooth' })\n }, [messages.length, selected.id])\n\n async function handleSend() {\n if (!draft.trim()) return\n const body = draft\n setDraft('')\n await send(body)\n }\n\n const accent = CHANNEL_ACCENT[selected.channel]\n const rows = React.useMemo(() => buildRows(messages), [messages])\n\n return (\n <section className={cn('flex min-w-0 flex-1 flex-col bg-muted/20', className)}>\n {/* Header */}\n <div className=\"flex items-center justify-between gap-2 border-b border-border bg-card px-3 py-2.5 md:px-5\">\n <div className=\"flex min-w-0 items-center gap-2 md:gap-3\">\n {onBack && (\n <Button variant=\"ghost\" size=\"icon\" className=\"-ml-1 shrink-0 lg:hidden\" onClick={onBack} aria-label={t('conversations.thread.back')}>\n <ChevronLeft className=\"h-5 w-5\" />\n </Button>\n )}\n <Avatar name={selected.contactName} accent={selected.accent} size=\"sm\" channel={selected.channel} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-foreground\">{selected.contactName}</p>\n <p className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <ChannelBadge channel={selected.channel} />\n <span className=\"truncate\">{selected.contactHandle}</span>\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setStatus('snoozed')} aria-label={t('conversations.thread.snooze')}>\n <Clock className=\"h-3.5 w-3.5 sm:mr-1\" /> <span className=\"hidden sm:inline\">{t('conversations.thread.snooze')}</span>\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setStatus('closed')} aria-label={t('conversations.thread.close')}>\n <Archive className=\"h-3.5 w-3.5 sm:mr-1\" /> <span className=\"hidden sm:inline\">{t('conversations.thread.close')}</span>\n </Button>\n <Button\n variant={panelOpen ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={onTogglePanel}\n aria-label={t('conversations.thread.details')}\n >\n <PanelRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {/* Messages */}\n <div ref={threadRef} className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4\">\n {rows.map((row) => {\n if (row.kind === 'day') {\n return (\n <div key={row.id} className=\"my-3 flex items-center justify-center\">\n <span className=\"rounded-full bg-muted px-3 py-0.5 text-[11px] font-medium text-muted-foreground\">\n {row.label}\n </span>\n </div>\n )\n }\n const m = row.message\n const outbound = m.direction === 'outbound'\n return (\n <div\n key={row.id}\n className={cn('flex', outbound ? 'justify-end' : 'justify-start', row.startsRun ? 'mt-2.5' : 'mt-0.5')}\n >\n <div\n className={cn(\n 'max-w-[68%] px-3.5 py-2 text-sm shadow-sm',\n outbound ? 'rounded-2xl text-white' : 'rounded-2xl bg-card text-foreground',\n // Tail only on the last bubble of a run, on the sender's side.\n outbound && row.endsRun && 'rounded-br-sm',\n !outbound && row.endsRun && 'rounded-bl-sm',\n )}\n style={outbound ? { backgroundColor: accent.color } : undefined}\n >\n <p className=\"whitespace-pre-wrap break-words\">{m.body}</p>\n <div className={cn('mt-0.5 text-right text-[10px]', outbound ? 'text-white/70' : 'text-muted-foreground')}>\n {new Date(m.at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n </div>\n </div>\n </div>\n )\n })}\n {rows.length === 0 && (\n <div className=\"flex h-full flex-col items-center justify-center text-muted-foreground\">\n <MessageSquare className=\"h-7 w-7\" />\n <p className=\"mt-2 text-sm\">{t('conversations.thread.empty')}</p>\n </div>\n )}\n </div>\n\n {/* Composer */}\n {/* TODO(follow-up): wire real emoji picker + attachment upload (removed the\n dead placeholder buttons rather than shipping non-functional UI). */}\n <div className=\"border-t border-border bg-card px-4 py-3\">\n <div className=\"flex items-end gap-2\">\n <textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n void handleSend()\n }\n }}\n rows={1}\n placeholder={t('conversations.thread.reply', { channel: CHANNEL_LABELS[selected.channel] })}\n className=\"max-h-32 min-h-[40px] flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary\"\n />\n <Button onClick={() => void handleSend()} disabled={sending || !draft.trim()} aria-label={t('conversations.thread.send')}>\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </section>\n )\n}\n","import React from 'react'\nimport { MapPin, User, Tag, Link2, StickyNote, X } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation } from '../types'\nimport { Avatar, ChannelBadge } from './shared'\n\nfunction Section({ icon: Icon, title, children }: {\n icon: React.ComponentType<{ className?: string }>\n title: string\n children: React.ReactNode\n}) {\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <p className=\"mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\">\n <Icon className=\"h-3 w-3\" /> {title}\n </p>\n {children}\n </div>\n )\n}\n\nexport function ContactPanel({ contact, onClose, className }: {\n contact: Conversation\n onClose?: () => void\n className?: string\n}) {\n const t = useTranslation()\n return (\n <aside className={cn('flex shrink-0 flex-col overflow-y-auto border-l border-border bg-card', className)}>\n {onClose && (\n <div className=\"flex items-center justify-between border-b border-border px-3 py-2 xl:hidden\">\n <span className=\"text-sm font-semibold text-foreground\">{t('conversations.contact.details')}</span>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label={t('conversations.contact.closeDetails')}>\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n )}\n <div className=\"flex flex-col items-center gap-2 px-4 py-5 text-center\">\n <Avatar name={contact.contactName} accent={contact.accent} size=\"lg\" channel={contact.channel} />\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{contact.contactName}</p>\n <p className=\"text-xs text-muted-foreground\">{contact.contactHandle}</p>\n </div>\n <ChannelBadge channel={contact.channel} />\n </div>\n\n <Section icon={User} title={t('conversations.contact.details')}>\n <dl className=\"space-y-1.5 text-sm\">\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.channel')}</dt>\n <dd className=\"text-foreground\">{CHANNEL_LABELS[contact.channel]}</dd>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.status')}</dt>\n <dd className=\"text-foreground\">{t(`conversations.status.${contact.status}`)}</dd>\n </div>\n {contact.assignedTo && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.assignedTo')}</dt>\n <dd className=\"text-foreground\">{contact.assignedTo}</dd>\n </div>\n )}\n {contact.location && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"flex items-center gap-1 text-muted-foreground\"><MapPin className=\"h-3 w-3\" /> {t('conversations.contact.location')}</dt>\n <dd className=\"text-foreground\">{contact.location}</dd>\n </div>\n )}\n </dl>\n </Section>\n\n {contact.tags.length > 0 && (\n <Section icon={Tag} title={t('conversations.contact.tags')}>\n <div className=\"flex flex-wrap gap-1.5\">\n {contact.tags.map((tag) => (\n <span key={tag} className=\"rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-foreground\">\n {tag}\n </span>\n ))}\n </div>\n </Section>\n )}\n\n {contact.note && (\n <Section icon={StickyNote} title={t('conversations.contact.note')}>\n <p className=\"text-sm text-foreground\">{contact.note}</p>\n </Section>\n )}\n\n <Section icon={Link2} title={t('conversations.contact.linkedRecords')}>\n <p className=\"text-xs text-muted-foreground\">{t('conversations.contact.noLinkedRecords')}</p>\n </Section>\n </aside>\n )\n}\n","import React from 'react'\nimport { Button, cn, toast } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n useLimitGuard, invalidateLimit, ContactPicker, RightRailPage,\n type ContactPickerValue,\n} from '@fayz-ai/admin'\nimport { useConversationsStore, useConversationsConfig } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\nconst CHANNELS: Channel[] = ['whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\n/**\n * Which field of a person IS the handle on a given channel. Phone channels read\n * the phone, email reads the email; Instagram and web chat have no counterpart\n * on a person record, so they always have to be typed. Deliberately NOT\n * cross-filling (an email in a WhatsApp handle looked plausible on screen and\n * was simply wrong).\n */\nfunction personHandleFor(channel: Channel, contact: ContactPickerValue | null): string {\n if (!contact) return ''\n if (channel === 'email') return contact.email ?? ''\n if (channel === 'sms' || channel === 'whatsapp') return contact.phone ?? ''\n return ''\n}\n\nconst HANDLE_LABEL_KEY: Record<Channel, string> = {\n whatsapp: 'conversations.new.handleLabel.phone',\n sms: 'conversations.new.handleLabel.phone',\n email: 'conversations.new.handleLabel.email',\n instagram: 'conversations.new.handleLabel.instagram',\n webchat: 'conversations.new.handleLabel.webchat',\n}\n\n/**\n * Starting a conversation, in the right column.\n *\n * It was a dialog over the inbox — over the very list you read to decide\n * whether a thread already exists. Beside it, that list stays legible while the\n * contact is chosen.\n */\nexport function NewConversationPanel({\n open,\n onOpenChange,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const t = useTranslation()\n const create = useConversationsStore((s) => s.create)\n const config = useConversationsConfig()\n const guardConversations = useLimitGuard('conversations_month')\n\n const [channel, setChannel] = React.useState<Channel>('whatsapp')\n const [contact, setContact] = React.useState<ContactPickerValue | null>(null)\n // Handle the user typed themselves. The one taken FROM the contact is derived\n // (see personHandleFor) and shown on the picker's chip — same as the agenda,\n // which never had a second phone field.\n const [typedHandle, setTypedHandle] = React.useState('')\n // While the picker's inline create form is open it already asks for phone and\n // email, so showing our own handle field would ask for the phone twice.\n const [creatingContact, setCreatingContact] = React.useState(false)\n const [firstMessage, setFirstMessage] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n\n // Reset the form each time the modal opens. `pickerKey` remounts the picker so\n // its internal search text resets too.\n const [pickerKey, setPickerKey] = React.useState(0)\n React.useEffect(() => {\n if (open) {\n setChannel('whatsapp')\n setContact(null)\n setTypedHandle('')\n setCreatingContact(false)\n setFirstMessage('')\n setSubmitting(false)\n setPickerKey((k) => k + 1)\n }\n }, [open])\n\n // What we'd message on this channel: the contact's own datum when they have\n // one, otherwise whatever the user typed. Recomputed on every channel switch,\n // so flipping WhatsApp → Email swaps phone for email with no stale state.\n const derivedHandle = personHandleFor(channel, contact)\n const effectiveHandle = derivedHandle || typedHandle\n const handleLabel = t(HANDLE_LABEL_KEY[channel])\n\n const canSubmit = (contact?.name.trim().length ?? 0) > 0 && !submitting\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!canSubmit) return\n setSubmitting(true)\n try {\n // Plan quantity guard (client-side, before the store call): opens the\n // global UpgradeModal and aborts when the monthly cap is reached. Inside\n // the try on purpose — it hits the network to count usage, and a rejection\n // out here used to escape unhandled, leaving the modal open with no\n // feedback at all (indistinguishable from a dead button).\n if ((await guardConversations()) === 'blocked') return\n await create({\n channel,\n contactName: contact!.name.trim(),\n contactPersonId: contact?.id,\n contactHandle: effectiveHandle.trim() || undefined,\n firstMessage: firstMessage.trim() || undefined,\n })\n invalidateLimit('conversations_month')\n onOpenChange(false)\n } catch (err) {\n // A failed insert (unresolved tenant, RLS rejection, offline…) previously\n // threw past the `finally` — `onOpenChange(false)` never ran, so the modal\n // stayed open with no feedback and the thread silently vanished. Surface\n // the failure and keep the form open so the user can retry.\n const message = err instanceof Error ? err.message : String(err)\n toast.error(t('conversations.new.createFailed'), { description: message })\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <RightRailPage\n title={t('conversations.new.title')}\n open={open}\n onClose={() => onOpenChange(false)}\n width=\"max-w-md\"\n >\n <form onSubmit={handleSubmit} className=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4\">\n {/* Channel picker */}\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.channel')}\n </label>\n <div className=\"flex flex-wrap gap-1.5\">\n {CHANNELS.map((ch) => {\n const Icon = CHANNEL_ICON[ch]\n const active = channel === ch\n return (\n <button\n key={ch}\n type=\"button\"\n onClick={() => setChannel(ch)}\n aria-pressed={active}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n active\n ? 'text-white'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n style={active ? { backgroundColor: CHANNEL_ACCENT[ch].color } : undefined}\n >\n <Icon className=\"h-3 w-3\" />\n {CHANNEL_LABELS[ch]}\n </button>\n )\n })}\n </div>\n </div>\n\n {/* Contact — shared find-or-create flow (@fayz-ai/admin), the same one\n the agenda uses to pick a client. The picker owns its label, the\n search field, the chip and the channel handle, so this composer and\n the appointment modal render the SAME thing from the same code. */}\n <ContactPicker\n key={pickerKey}\n value={contact}\n onChange={setContact}\n kind={config.contactKind}\n lookup={config.contactLookup}\n createEntityDef={config.contactEntityDef}\n allowFreeText\n onCreatingChange={setCreatingContact}\n autoFocus\n label={t('conversations.new.contactName')}\n placeholder={t('conversations.new.contactNamePlaceholder')}\n secondaryText={derivedHandle || undefined}\n handleField={{\n label: handleLabel,\n derived: derivedHandle || undefined,\n value: typedHandle,\n onChange: setTypedHandle,\n fieldLabel: t('conversations.new.handle'),\n placeholder: t('conversations.new.handlePlaceholder'),\n }}\n />\n\n {/* First message */}\n <div>\n <label htmlFor=\"conv-first-message\" className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.firstMessage')}\n </label>\n <textarea\n id=\"conv-first-message\"\n value={firstMessage}\n onChange={(e) => setFirstMessage(e.target.value)}\n rows={3}\n placeholder={t('conversations.new.firstMessagePlaceholder')}\n className=\"max-h-40 min-h-[64px] w-full resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary\"\n />\n </div>\n\n {/* No Cancel: the column header already holds the way out. */}\n <div className=\"mt-auto pt-2\">\n <Button type=\"submit\" disabled={!canSubmit} className=\"w-full\">\n {submitting ? t('conversations.new.creating') : t('conversations.new.create')}\n </Button>\n </div>\n </form>\n </RightRailPage>\n )\n}\n","import React from 'react'\nimport { MessageSquare, SquarePen } from 'lucide-react'\nimport { Button, PageHeaderActions, cn } from '@fayz-ai/ui'\nimport { PermissionGate } from '@fayz-ai/admin'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { ConversationList } from './ConversationList'\nimport { MessageThread } from './MessageThread'\nimport { ContactPanel } from './ContactPanel'\nimport { NewConversationPanel } from './NewConversationPanel'\nimport { useMediaQuery } from './shared'\n\nexport function InboxView() {\n const t = useTranslation()\n const { conversations, selectedId, deselect } = useConversationsStore((s) => s)\n // xl+ shows the contact panel inline (three panes); below that it's an\n // on-demand slide-over so the list + thread keep room to breathe.\n const isWidePanel = useMediaQuery('(min-width: 1280px)')\n const [panelOpen, setPanelOpen] = React.useState(false)\n const [newOpen, setNewOpen] = React.useState(false)\n const selected = conversations.find((c) => c.id === selectedId) ?? null\n\n // Open the panel by default only when it fits inline; collapse it otherwise.\n React.useEffect(() => {\n setPanelOpen(isWidePanel)\n }, [isWidePanel])\n\n return (\n <>\n {/* Outside the inbox row: with a header slot this portals and takes no\n space; without one it falls in as a block above the screen, rather\n than a column wedged between the list and the thread. */}\n <PermissionGate feature=\"conversations\" action=\"create\">\n <PageHeaderActions>\n <Button\n size=\"sm\"\n onClick={() => setNewOpen(true)}\n aria-label={t('conversations.list.new')}\n data-testid=\"conversations-new\"\n >\n <SquarePen className=\"h-3.5 w-3.5 sm:mr-1.5\" />\n <span className=\"hidden sm:inline\">{t('conversations.list.new')}</span>\n </Button>\n </PageHeaderActions>\n </PermissionGate>\n\n <NewConversationPanel open={newOpen} onOpenChange={setNewOpen} />\n\n <div className=\"relative flex h-full min-h-0 overflow-hidden bg-background\">\n {/* Conversation list — single-pane below lg (full width), fixed rail at lg+.\n Below lg it yields to the thread once a conversation is open. */}\n <ConversationList className={cn(selected ? 'hidden lg:flex' : 'flex')} />\n\n {/* Thread — hidden below lg until a conversation is selected. */}\n {selected ? (\n <MessageThread\n selected={selected}\n panelOpen={panelOpen}\n onTogglePanel={() => setPanelOpen((v) => !v)}\n onBack={deselect}\n className=\"flex\"\n />\n ) : (\n <section className=\"hidden min-w-0 flex-1 flex-col items-center justify-center bg-muted/20 text-muted-foreground lg:flex\">\n <MessageSquare className=\"h-9 w-9\" />\n <p className=\"mt-2 text-sm\">{t('conversations.empty.select')}</p>\n </section>\n )}\n\n {/* Contact panel — inline column at xl, slide-over overlay below xl. */}\n {selected && panelOpen && (\n <>\n <button\n className=\"absolute inset-0 z-10 bg-black/40 xl:hidden\"\n aria-label=\"Close details\"\n onClick={() => setPanelOpen(false)}\n />\n <ContactPanel\n contact={selected}\n onClose={() => setPanelOpen(false)}\n className=\"absolute inset-y-0 right-0 z-20 w-[340px] max-w-[88%] shadow-2xl xl:static xl:z-auto xl:w-[300px] xl:max-w-none xl:shadow-none\"\n />\n </>\n )}\n </div>\n </>\n )\n}\n","import React from 'react'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { ConversationsContextProvider, type ResolvedConversationsConfig } from '../context'\nimport type { ConversationsUIState } from '../store'\nimport { InboxView } from './InboxView'\n\nexport function ConversationsPage({ store, config }: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n}) {\n React.useEffect(() => {\n void store.getState().load()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return (\n <ConversationsContextProvider store={store} config={config}>\n <InboxView />\n </ConversationsContextProvider>\n )\n}\n","import type { Channel } from '../types'\n\n// Solid brand hex per channel — mirrors CHANNEL_ACCENT in ../channel.ts but\n// React-free so the data providers can stamp a sensible avatar accent on new\n// conversations without importing the icon layer.\nexport const CHANNEL_ACCENT_HEX: Record<Channel, string> = {\n whatsapp: '#22c55e',\n sms: '#6366f1',\n instagram: '#ec4899',\n email: '#0ea5e9',\n webchat: '#f59e0b',\n}\n","import type { ConversationsProvider } from './types'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n} from '../types'\n\n// Deterministic relative timestamps (no Date.now at module init for stable seeds).\nfunction minutesAgo(base: number, mins: number): string {\n return new Date(base - mins * 60_000).toISOString()\n}\n\nfunction seed(): { conversations: Conversation[]; messages: Message[] } {\n const base = new Date('2026-06-16T14:00:00Z').getTime()\n\n const conversations: Conversation[] = [\n {\n id: 'c1', contactName: 'Marina Alves', contactHandle: '+55 11 99876-1020', channel: 'whatsapp',\n lastMessagePreview: 'Perfect, can we book for Friday at 3pm?', lastMessageAt: minutesAgo(base, 4),\n unreadCount: 2, status: 'open', assignedTo: 'You', accent: '#22c55e', tags: ['Hot lead'],\n location: 'São Paulo, BR', note: 'Referred by Instagram ad — interested in full color + cut.',\n },\n {\n id: 'c2', contactName: 'Jordan Pierce', contactHandle: '+1 (415) 555-0142', channel: 'sms',\n lastMessagePreview: 'Got it — sending the deposit now.', lastMessageAt: minutesAgo(base, 22),\n unreadCount: 0, status: 'open', assignedTo: 'You', accent: '#6366f1', tags: ['Customer'],\n location: 'San Francisco, US',\n },\n {\n id: 'c3', contactName: '@thehairloft', contactHandle: 'thehairloft', channel: 'instagram',\n lastMessagePreview: 'Do you offer balayage on weekends?', lastMessageAt: minutesAgo(base, 51),\n unreadCount: 1, status: 'open', accent: '#ec4899', tags: ['New'],\n },\n {\n id: 'c4', contactName: 'David Whitman', contactHandle: 'david@whitman.co', channel: 'email',\n lastMessagePreview: 'Re: Proposal — looks great, one question on pricing…', lastMessageAt: minutesAgo(base, 95),\n unreadCount: 0, status: 'open', assignedTo: 'Sofia', accent: '#0ea5e9', tags: ['Proposal'],\n location: 'Austin, US', note: 'Evaluating the retainer tier. Decision expected this week.',\n },\n {\n id: 'c5', contactName: 'Website visitor', contactHandle: 'live chat · acme.com', channel: 'webchat',\n lastMessagePreview: 'Is anyone available to chat?', lastMessageAt: minutesAgo(base, 140),\n unreadCount: 0, status: 'snoozed', accent: '#f59e0b', tags: [],\n },\n {\n id: 'c6', contactName: 'Priya Nair', contactHandle: '+44 7700 900123', channel: 'whatsapp',\n lastMessagePreview: 'Thank you! See you next week 🙌', lastMessageAt: minutesAgo(base, 1440),\n unreadCount: 0, status: 'closed', assignedTo: 'You', accent: '#14b8a6', tags: ['Customer'],\n location: 'London, UK',\n },\n ]\n\n const messages: Message[] = [\n msg('m1', 'c1', 'whatsapp', 'inbound', 'Hi! I saw your ad — do you have availability this week?', 'Marina Alves', minutesAgo(base, 18)),\n msg('m2', 'c1', 'whatsapp', 'outbound', 'Hi Marina! Yes, we do. What service are you interested in?', 'You', minutesAgo(base, 15)),\n msg('m3', 'c1', 'whatsapp', 'inbound', 'A full color + cut.', 'Marina Alves', minutesAgo(base, 9)),\n msg('m4', 'c1', 'whatsapp', 'inbound', 'Perfect, can we book for Friday at 3pm?', 'Marina Alves', minutesAgo(base, 4)),\n\n msg('m5', 'c2', 'sms', 'outbound', 'Your appointment is confirmed for tomorrow at 10am.', 'You', minutesAgo(base, 40)),\n msg('m6', 'c2', 'sms', 'inbound', 'Got it — sending the deposit now.', 'Jordan Pierce', minutesAgo(base, 22)),\n\n msg('m7', 'c3', 'instagram', 'inbound', 'Do you offer balayage on weekends?', '@thehairloft', minutesAgo(base, 51)),\n\n msg('m8', 'c4', 'email', 'inbound', 'Re: Proposal — looks great, one question on pricing for the retainer tier.', 'David Whitman', minutesAgo(base, 95)),\n msg('m9', 'c4', 'email', 'outbound', 'Happy to walk you through it — are you free for a quick call tomorrow?', 'Sofia', minutesAgo(base, 80)),\n\n msg('m10', 'c5', 'webchat', 'inbound', 'Is anyone available to chat?', 'Website visitor', minutesAgo(base, 140)),\n\n msg('m11', 'c6', 'whatsapp', 'outbound', 'You are all set for next Tuesday. Anything else?', 'You', minutesAgo(base, 1500)),\n msg('m12', 'c6', 'whatsapp', 'inbound', 'Thank you! See you next week 🙌', 'Priya Nair', minutesAgo(base, 1440)),\n ]\n\n return { conversations, messages }\n}\n\nfunction msg(\n id: string, conversationId: string, channel: Message['channel'],\n direction: Message['direction'], body: string, author: string, at: string,\n): Message {\n return { id, conversationId, channel, direction, body, author, at }\n}\n\nexport interface MockConversationsConfig {\n /** Tenant id (value or getter) used to namespace the localStorage snapshot. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ninterface Snapshot {\n conversations: Conversation[]\n messages: Message[]\n counter: number\n}\n\n// ---------------------------------------------------------------------------\n// In-memory mock inbox with best-effort localStorage persistence, so a browser\n// reload keeps whatever the demo created/sent within the session. The store is\n// namespaced per tenant; SSR / no-window environments degrade to pure memory.\n// ---------------------------------------------------------------------------\nexport function createMockConversationsProvider(\n config?: MockConversationsConfig,\n): ConversationsProvider {\n const selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenant(): string {\n const raw = typeof config?.tenantId === 'function' ? config.tenantId() : config?.tenantId\n return raw || 'default'\n }\n\n const storageKey = () => `saas:mock:conversations:${resolveTenant()}`\n\n function hasStorage(): boolean {\n try {\n return typeof window !== 'undefined' && !!window.localStorage\n } catch {\n return false\n }\n }\n\n function load(): Snapshot {\n if (hasStorage()) {\n try {\n const raw = window.localStorage.getItem(storageKey())\n if (raw) {\n const parsed = JSON.parse(raw) as Snapshot\n if (Array.isArray(parsed.conversations) && Array.isArray(parsed.messages)) {\n return {\n conversations: parsed.conversations,\n messages: parsed.messages,\n counter: parsed.counter ?? 100,\n }\n }\n }\n } catch {\n // Corrupt snapshot — fall through to a fresh seed.\n }\n }\n const seeded = seed()\n return { conversations: seeded.conversations, messages: seeded.messages, counter: 100 }\n }\n\n const state = load()\n\n function persist(): void {\n if (!hasStorage()) return\n try {\n window.localStorage.setItem(\n storageKey(),\n JSON.stringify({\n conversations: state.conversations,\n messages: state.messages,\n counter: state.counter,\n } satisfies Snapshot),\n )\n } catch {\n // Quota / privacy mode — keep working in memory only.\n }\n }\n\n // Persist the initial seed so a first reload is already stable.\n persist()\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let list = [...state.conversations]\n if (query?.channel && query.channel !== 'all') list = list.filter((c) => c.channel === query.channel)\n if (query?.status && query.status !== 'all') list = list.filter((c) => c.status === query.status)\n if (query?.search) {\n const q = query.search.toLowerCase()\n list = list.filter(\n (c) => c.contactName.toLowerCase().includes(q) || c.lastMessagePreview.toLowerCase().includes(q),\n )\n }\n return list.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt))\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n return state.messages\n .filter((m) => m.conversationId === conversationId)\n .sort((a, b) => a.at.localeCompare(b.at))\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n const id = `c${++state.counter}`\n const conversation: Conversation = {\n id,\n contactName: input.contactName.trim(),\n contactPersonId: input.contactPersonId,\n contactHandle: input.contactHandle?.trim() ?? '',\n channel: input.channel,\n lastMessagePreview: firstMessage ?? '',\n lastMessageAt: now,\n unreadCount: 0,\n status: 'open',\n assignedTo: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || undefined,\n }\n state.conversations.unshift(conversation)\n if (firstMessage) {\n state.messages.push({\n id: `m${++state.counter}`,\n conversationId: id,\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n })\n }\n persist()\n return conversation\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const conv = state.conversations.find((c) => c.id === input.conversationId)\n const created: Message = {\n id: `m${++state.counter}`,\n conversationId: input.conversationId,\n channel: conv?.channel ?? 'sms',\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at: new Date().toISOString(),\n }\n state.messages.push(created)\n if (conv) {\n conv.lastMessagePreview = input.body\n conv.lastMessageAt = created.at\n conv.unreadCount = 0\n if (conv.status === 'closed') conv.status = 'open'\n }\n persist()\n return created\n },\n\n async markRead(conversationId: string): Promise<void> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (conv && conv.unreadCount !== 0) {\n conv.unreadCount = 0\n persist()\n }\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (!conv) throw new Error('Conversation not found')\n conv.status = status\n persist()\n return conv\n },\n }\n}\n","// Physical table names for the conversations plugin (plg_conversations* prefix).\n// Import T and reference T.<key> in the data provider so a rename lands in\n// exactly one place.\nexport const T = {\n conversations: 'plg_conversations',\n messages: 'plg_conversation_messages',\n} as const\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport type { ConversationsProvider } from './types'\nimport { T } from './tables'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// Supabase-backed conversations provider — reads/writes the project's\n// `conversations` + `conversation_messages` tables (tenant-scoped via RLS +\n// an explicit tenant filter from the active-org context). Mirrors the inline\n// client-casting style of @fayz-ai/core's createSupabaseProvider; the global\n// client is `unknown`-typed so each call narrows the shape it needs.\n//\n// Real channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver inbound\n// rows into these tables out-of-band; this provider is the read/compose surface.\n// ---------------------------------------------------------------------------\n\nexport interface SupabaseConversationsConfig {\n /** Supabase client; defaults to the global one registered by createFayzApp. */\n supabaseClient?: unknown\n /** Active tenant id (value or getter) used to scope reads and stamp writes. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapConversation(r: Row): Conversation {\n return {\n id: String(r.id),\n contactName: (r.contact_name as string) ?? '',\n contactPersonId: (r.contact_person_id as string | null) ?? undefined,\n contactHandle: (r.contact_handle as string) ?? '',\n channel: (r.channel as Conversation['channel']) ?? 'sms',\n lastMessagePreview: (r.last_message_preview as string) ?? '',\n lastMessageAt: (r.last_message_at as string) ?? '',\n unreadCount: Number(r.unread_count ?? 0),\n status: (r.status as ConversationStatus) ?? 'open',\n assignedTo: (r.assigned_to as string | null) ?? undefined,\n accent: (r.accent as string) ?? '#6366f1',\n tags: (r.tags as string[] | null) ?? [],\n location: (r.location as string | null) ?? undefined,\n note: (r.note as string | null) ?? undefined,\n }\n}\n\nfunction mapMessage(r: Row): Message {\n return {\n id: String(r.id),\n conversationId: String(r.conversation_id),\n channel: (r.channel as Message['channel']) ?? 'sms',\n direction: (r.direction as Message['direction']) ?? 'inbound',\n body: (r.body as string) ?? '',\n at: (r.at as string) ?? '',\n author: (r.author as string) ?? '',\n }\n}\n\nexport function createSupabaseConversationsProvider(\n config?: SupabaseConversationsConfig,\n): ConversationsProvider {\n const selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenantId(): string | undefined {\n if (!config?.tenantId) return undefined\n return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId\n }\n\n function client(): { from: (t: string) => Row } {\n const supabase = (config?.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n return supabase\n }\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let q = (client().from(T.conversations) as { select: (s: string) => Row }).select('*')\n\n const tenantId = resolveTenantId()\n if (tenantId) q = (q as { eq: (c: string, v: string) => Row }).eq('tenant_id', tenantId)\n if (query?.channel && query.channel !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('channel', query.channel)\n }\n if (query?.status && query.status !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('status', query.status)\n }\n if (query?.search) {\n const term = `%${query.search}%`\n q = (q as { or: (c: string) => Row }).or(\n `contact_name.ilike.${term},last_message_preview.ilike.${term}`,\n )\n }\n q = (q as { order: (c: string, o: unknown) => Row }).order('last_message_at', {\n ascending: false,\n })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapConversation)\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n const selected = (\n client().from(T.messages) as { select: (s: string) => Row }\n ).select('*')\n const filtered = (selected as { eq: (c: string, v: string) => Row }).eq(\n 'conversation_id',\n conversationId,\n )\n const ordered = (filtered as { order: (c: string, o: unknown) => Row }).order('at', {\n ascending: true,\n })\n const { data, error } = (await ordered) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapMessage)\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const tenantId = resolveTenantId()\n // Insert with a null tenant_id fails the plg_conversations RLS WITH CHECK\n // (tenant_id ∈ user_tenant_ids()) with an opaque Postgres error. When the\n // active tenant hasn't resolved yet (bootstrap race), fail fast with an\n // actionable message the compose modal can surface via toast.\n if (!tenantId) {\n throw new Error('[plugin-conversations] Active tenant not resolved — cannot create conversation. Try again in a moment.')\n }\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n\n const convRow: Row = {\n contact_name: input.contactName.trim(),\n // Only written when the picker resolved a real person — an app that\n // hasn't run migration 002 yet would reject an unknown column, and\n // omitting it keeps the free-text path working there.\n ...(input.contactPersonId ? { contact_person_id: input.contactPersonId } : {}),\n contact_handle: input.contactHandle?.trim() || null,\n channel: input.channel,\n last_message_preview: firstMessage || null,\n last_message_at: now,\n unread_count: 0,\n status: 'open',\n assigned_to: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || null,\n }\n if (tenantId) convRow.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.conversations) as { insert: (r: Row) => Row }).insert(convRow) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!created) throw new Error('Conversation not created')\n\n // Seed the thread with the first outbound message when provided.\n if (firstMessage) {\n const msgRow: Row = {\n conversation_id: String(created.id),\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n }\n if (tenantId) msgRow.tenant_id = tenantId\n await (client().from(T.messages) as { insert: (r: Row) => Promise<unknown> }).insert(msgRow)\n }\n\n return mapConversation(created)\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const tenantId = resolveTenantId()\n\n // Resolve the channel from the parent conversation so the message matches.\n const convSelected = (client().from(T.conversations) as { select: (s: string) => Row }).select(\n 'channel',\n )\n const convFiltered = (convSelected as { eq: (c: string, v: string) => Row }).eq(\n 'id',\n input.conversationId,\n )\n const { data: conv } = (await (\n convFiltered as { maybeSingle: () => Promise<{ data: Row | null }> }\n ).maybeSingle()) as { data: Row | null }\n const channel = (conv?.channel as Message['channel']) ?? 'sms'\n\n const at = new Date().toISOString()\n const row: Row = {\n conversation_id: input.conversationId,\n channel,\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at,\n }\n if (tenantId) row.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.messages) as { insert: (r: Row) => Row }).insert(row) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n\n // Roll the parent conversation forward (preview / timestamp / unread / reopen).\n await (\n (client().from(T.conversations) as {\n update: (r: Row) => Row\n }).update({\n last_message_preview: input.body,\n last_message_at: at,\n unread_count: 0,\n status: 'open',\n }) as { eq: (c: string, v: string) => Promise<unknown> }\n ).eq('id', input.conversationId)\n\n return mapMessage(created ?? row)\n },\n\n async markRead(conversationId: string): Promise<void> {\n const { error } = (await (\n (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n unread_count: 0,\n }) as { eq: (c: string, v: string) => Promise<{ error: unknown }> }\n ).eq('id', conversationId)) as { error: unknown }\n if (error) throw error\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const updated = (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n status,\n })\n const filtered = (updated as { eq: (c: string, v: string) => Row }).eq('id', conversationId)\n const selected = (filtered as { select: () => Row }).select()\n const { data, error } = (await (\n selected as { single: () => Promise<{ data: Row | null; error: unknown }> }\n ).single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!data) throw new Error('Conversation not found')\n return mapConversation(data)\n },\n }\n}\n","import { createStore, type StoreApi } from 'zustand/vanilla'\nimport type { ConversationsProvider } from './data/types'\nimport type {\n Conversation,\n Message,\n Channel,\n ConversationStatus,\n CreateConversationInput,\n} from './types'\n\nexport interface ConversationsUIState {\n conversations: Conversation[]\n messages: Message[]\n selectedId: string | null\n channelFilter: Channel | 'all'\n search: string\n loading: boolean\n sending: boolean\n\n load(): Promise<void>\n select(id: string): Promise<void>\n deselect(): void\n setChannelFilter(channel: Channel | 'all'): Promise<void>\n setSearch(search: string): Promise<void>\n create(input: CreateConversationInput): Promise<Conversation>\n send(body: string): Promise<void>\n setStatus(status: ConversationStatus): Promise<void>\n}\n\nexport function createConversationsStore(\n provider: ConversationsProvider,\n): StoreApi<ConversationsUIState> {\n return createStore<ConversationsUIState>((set, get) => ({\n conversations: [],\n messages: [],\n selectedId: null,\n channelFilter: 'all',\n search: '',\n loading: false,\n sending: false,\n\n async load() {\n set({ loading: true })\n const conversations = await provider.listConversations({\n channel: get().channelFilter,\n search: get().search || undefined,\n })\n const selectedId = get().selectedId ?? conversations[0]?.id ?? null\n set({ conversations, loading: false, selectedId })\n if (selectedId) await get().select(selectedId)\n },\n\n async select(id: string) {\n set({ selectedId: id })\n const messages = await provider.getMessages(id)\n set({ messages })\n await provider.markRead(id)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? { ...c, unreadCount: 0 } : c)),\n }))\n },\n\n deselect() {\n set({ selectedId: null, messages: [] })\n },\n\n async setChannelFilter(channel) {\n set({ channelFilter: channel })\n await get().load()\n },\n\n async setSearch(search) {\n set({ search })\n await get().load()\n },\n\n async create(input) {\n const created = await provider.createConversation(input)\n // The thread EXISTS the moment the insert returns, so resolve on that and\n // show it optimistically. Clear filters that would hide it, prepend it,\n // select it — all local.\n set((s) => ({\n channelFilter: 'all',\n search: '',\n conversations: [created, ...s.conversations.filter((c) => c.id !== created.id)],\n selectedId: created.id,\n }))\n\n // Reconciliation (authoritative list + the thread's messages) runs in the\n // BACKGROUND. It used to be awaited here, which made the caller — the\n // compose modal — hostage to two extra round-trips: on a slow pool the\n // conversation was already in the database while the modal sat open\n // looking broken, inviting a second click and a duplicate thread.\n // Failures are non-fatal: the optimistic row above already renders.\n void (async () => {\n try {\n const conversations = await provider.listConversations({})\n // Read-after-write guard: a refetch that races the insert may not\n // return the new row yet — keep the local one rather than dropping it.\n const merged = conversations.some((c) => c.id === created.id)\n ? conversations\n : [created, ...conversations]\n set({ conversations: merged })\n await get().select(created.id)\n } catch {\n /* optimistic state stands; the next load() reconciles */\n }\n })()\n\n return created\n },\n\n async send(body: string) {\n const id = get().selectedId\n if (!id || !body.trim()) return\n set({ sending: true })\n const created = await provider.sendMessage({ conversationId: id, body: body.trim() })\n set((s) => ({\n sending: false,\n messages: [...s.messages, created],\n conversations: s.conversations.map((c) =>\n c.id === id ? { ...c, lastMessagePreview: created.body, lastMessageAt: created.at } : c,\n ),\n }))\n },\n\n async setStatus(status) {\n const id = get().selectedId\n if (!id) return\n const updated = await provider.setStatus(id, status)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? updated : c)),\n }))\n },\n }))\n}\n","export const en: Record<string, string> = {\n 'conversations.title': 'Conversations',\n 'conversations.subtitle': 'Unified inbox across every channel',\n\n // Conversation list\n 'conversations.list.search': 'Search conversations',\n 'conversations.list.loading': 'Loading…',\n 'conversations.list.empty': 'No conversations',\n 'conversations.list.new': 'New conversation',\n\n // Channel filters\n 'conversations.filter.all': 'All',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'Email',\n 'conversations.filter.webchat': 'Web',\n\n // Status labels\n 'conversations.status.open': 'Open',\n 'conversations.status.snoozed': 'Snoozed',\n 'conversations.status.closed': 'Closed',\n\n // Empty state\n 'conversations.empty.select': 'Select a conversation to start chatting',\n\n // Thread\n 'conversations.thread.back': 'Back to conversations',\n 'conversations.thread.snooze': 'Snooze',\n 'conversations.thread.close': 'Close',\n 'conversations.thread.details': 'Toggle contact details',\n 'conversations.thread.empty': 'No messages yet',\n 'conversations.thread.reply': 'Reply via {{channel}}…',\n 'conversations.thread.send': 'Send',\n\n // Contact panel\n 'conversations.contact.details': 'Details',\n 'conversations.contact.closeDetails': 'Close details',\n 'conversations.contact.channel': 'Channel',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Assigned to',\n 'conversations.contact.location': 'Location',\n 'conversations.contact.tags': 'Tags',\n 'conversations.contact.note': 'Note',\n 'conversations.contact.linkedRecords': 'Linked records',\n 'conversations.contact.noLinkedRecords': 'No linked records yet.',\n\n // New-conversation modal\n 'conversations.new.title': 'New conversation',\n 'conversations.new.channel': 'Channel',\n 'conversations.new.contactName': 'Contact name',\n 'conversations.new.contactNamePlaceholder': 'e.g. Jane Doe',\n 'conversations.new.handle': 'Phone / handle / email',\n 'conversations.new.handlePlaceholder': '+1 555 000 0000',\n // The handle field only surfaces when the picked contact has nothing usable\n // for the active channel — otherwise it is derived and shown on the chip.\n 'conversations.new.addHandle': 'Add {label}',\n 'conversations.new.handleLabel.phone': 'phone',\n 'conversations.new.handleLabel.email': 'email',\n 'conversations.new.handleLabel.instagram': 'Instagram handle',\n 'conversations.new.handleLabel.webchat': 'web chat id',\n 'conversations.new.firstMessage': 'First message',\n 'conversations.new.firstMessagePlaceholder': 'Write the first message (optional)…',\n 'conversations.new.cancel': 'Cancel',\n 'conversations.new.create': 'Start conversation',\n 'conversations.new.creating': 'Starting…',\n 'conversations.new.createFailed': 'Could not create the conversation',\n}\n","export const ptBR: Record<string, string> = {\n 'conversations.title': 'Conversas',\n 'conversations.subtitle': 'Caixa de entrada unificada de todos os canais',\n\n // Lista de conversas\n 'conversations.list.search': 'Buscar conversas',\n 'conversations.list.loading': 'Carregando…',\n 'conversations.list.empty': 'Nenhuma conversa',\n 'conversations.list.new': 'Nova conversa',\n\n // Filtros de canal\n 'conversations.filter.all': 'Todas',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'E-mail',\n 'conversations.filter.webchat': 'Web',\n\n // Rótulos de status\n 'conversations.status.open': 'Aberta',\n 'conversations.status.snoozed': 'Adiada',\n 'conversations.status.closed': 'Encerrada',\n\n // Estado vazio\n 'conversations.empty.select': 'Selecione uma conversa para começar a conversar',\n\n // Thread\n 'conversations.thread.back': 'Voltar às conversas',\n 'conversations.thread.snooze': 'Adiar',\n 'conversations.thread.close': 'Encerrar',\n 'conversations.thread.details': 'Alternar detalhes do contato',\n 'conversations.thread.empty': 'Nenhuma mensagem ainda',\n 'conversations.thread.reply': 'Responder via {{channel}}…',\n 'conversations.thread.send': 'Enviar',\n\n // Painel do contato\n 'conversations.contact.details': 'Detalhes',\n 'conversations.contact.closeDetails': 'Fechar detalhes',\n 'conversations.contact.channel': 'Canal',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Responsável',\n 'conversations.contact.location': 'Localização',\n 'conversations.contact.tags': 'Etiquetas',\n 'conversations.contact.note': 'Nota',\n 'conversations.contact.linkedRecords': 'Registros vinculados',\n 'conversations.contact.noLinkedRecords': 'Nenhum registro vinculado ainda.',\n\n // Modal de nova conversa\n 'conversations.new.title': 'Nova conversa',\n 'conversations.new.channel': 'Canal',\n 'conversations.new.contactName': 'Nome do contato',\n 'conversations.new.contactNamePlaceholder': 'ex.: Maria Silva',\n 'conversations.new.handle': 'Telefone / usuário / e-mail',\n 'conversations.new.handlePlaceholder': '+55 11 99999-0000',\n // O campo de handle só aparece quando o contato escolhido não tem o dado do\n // canal ativo — caso contrário ele é derivado e mostrado no chip.\n 'conversations.new.addHandle': 'Adicionar {label}',\n 'conversations.new.handleLabel.phone': 'telefone',\n 'conversations.new.handleLabel.email': 'e-mail',\n 'conversations.new.handleLabel.instagram': '@ do Instagram',\n 'conversations.new.handleLabel.webchat': 'id do chat',\n 'conversations.new.firstMessage': 'Primeira mensagem',\n 'conversations.new.firstMessagePlaceholder': 'Escreva a primeira mensagem (opcional)…',\n 'conversations.new.cancel': 'Cancelar',\n 'conversations.new.create': 'Iniciar conversa',\n 'conversations.new.creating': 'Iniciando…',\n 'conversations.new.createFailed': 'Não foi possível criar a conversa',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const conversationsLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","// AUTO-GENERATED from 001_conversations.sql, 002_contact_person.sql — regenerate with scripts/embed-migrations.mjs\n// SQL files are the source of truth; this inline copy lets the manifest declare\n// migrations as data. Do not edit by hand — run the embed script instead.\n\nexport const MIGRATION_001_CONVERSATIONS = `-- ============================================================================\n-- plugin-conversations 001: omni-channel inbox model (SMS / WhatsApp /\n-- Instagram / Email / Web chat). Prefix: plg_conversations / plg_conversation_messages.\n-- §1 plg_conversations — one thread per contact+channel\n-- §2 plg_conversation_messages — inbound/outbound messages within a thread\n-- §3 RLS: authenticated tenant-scoped CRUD on both tables + GRANTs\n--\n-- Column names mirror exactly what supabase.ts's mapConversation / mapMessage\n-- read. Real channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver\n-- inbound rows here out-of-band; the provider is the read/compose surface.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\n-- §1 — conversations (threads)\nCREATE TABLE IF NOT EXISTS public.plg_conversations (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n contact_name text NOT NULL,\n contact_handle text,\n channel text NOT NULL\n CHECK (channel IN ('sms', 'whatsapp', 'instagram', 'email', 'webchat')),\n last_message_preview text,\n last_message_at timestamptz DEFAULT now(),\n unread_count int DEFAULT 0,\n status text DEFAULT 'open'\n CHECK (status IN ('open', 'snoozed', 'closed')),\n assigned_to text,\n accent text,\n tags text[],\n location text,\n note text,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_conversations ENABLE ROW LEVEL SECURITY;\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_tenant ON public.plg_conversations(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_tenant_recent ON public.plg_conversations(tenant_id, last_message_at DESC);\n\n-- §2 — messages\nCREATE TABLE IF NOT EXISTS public.plg_conversation_messages (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n conversation_id uuid NOT NULL REFERENCES public.plg_conversations(id) ON DELETE CASCADE,\n channel text\n CHECK (channel IS NULL OR channel IN ('sms', 'whatsapp', 'instagram', 'email', 'webchat')),\n direction text\n CHECK (direction IN ('inbound', 'outbound')),\n body text NOT NULL,\n author text,\n at timestamptz DEFAULT now()\n);\nALTER TABLE public.plg_conversation_messages ENABLE ROW LEVEL SECURITY;\nCREATE INDEX IF NOT EXISTS idx_plg_conversation_messages_thread ON public.plg_conversation_messages(conversation_id, at);\n\n-- §3 — RLS: authenticated tenant CRUD (the inbox reads/writes here)\nDROP POLICY IF EXISTS plg_conversations_select ON public.plg_conversations;\nDROP POLICY IF EXISTS plg_conversations_insert ON public.plg_conversations;\nDROP POLICY IF EXISTS plg_conversations_update ON public.plg_conversations;\nDROP POLICY IF EXISTS plg_conversations_delete ON public.plg_conversations;\nCREATE POLICY plg_conversations_select ON public.plg_conversations FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversations_insert ON public.plg_conversations FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversations_update ON public.plg_conversations FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversations_delete ON public.plg_conversations FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations TO authenticated;\n\nDROP POLICY IF EXISTS plg_conversation_messages_select ON public.plg_conversation_messages;\nDROP POLICY IF EXISTS plg_conversation_messages_insert ON public.plg_conversation_messages;\nDROP POLICY IF EXISTS plg_conversation_messages_update ON public.plg_conversation_messages;\nDROP POLICY IF EXISTS plg_conversation_messages_delete ON public.plg_conversation_messages;\nCREATE POLICY plg_conversation_messages_select ON public.plg_conversation_messages FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversation_messages_insert ON public.plg_conversation_messages FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversation_messages_update ON public.plg_conversation_messages FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversation_messages_delete ON public.plg_conversation_messages FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversation_messages TO authenticated;\n`\n\nexport const MIGRATION_002_CONTACT_PERSON = `-- ============================================================================\n-- plugin-conversations 002: link a thread to a REAL person record.\n--\n-- The compose modal used to take a free-text name + handle, so a conversation\n-- with \"Maria\" had nothing to do with the Maria in the agenda, the CRM or the\n-- financial module. The shared ContactPicker (find-or-create over\n-- public.people) now resolves a person, and this column stores that link.\n--\n-- Nullable on purpose, in both directions of time:\n-- • rows created before this migration keep working (name/handle only);\n-- • an inbound message from an unknown number still opens a thread with no\n-- person attached — the contact panel can offer \"create contact\" later.\n-- ON DELETE SET NULL: deleting a person must never take their history with it.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nALTER TABLE public.plg_conversations\n ADD COLUMN IF NOT EXISTS contact_person_id uuid REFERENCES public.people(id) ON DELETE SET NULL;\n\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_person\n ON public.plg_conversations(tenant_id, contact_person_id)\n WHERE contact_person_id IS NOT NULL;\n`\n\nexport const MIGRATIONS: Array<{ id: string; sql: string }> = [\n { id: \"001_conversations\", sql: MIGRATION_001_CONVERSATIONS },\n { id: \"002_contact_person\", sql: MIGRATION_002_CONTACT_PERSON },\n]\n","import React from 'react'\nimport type { EntityDef, PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { getActiveTenantId, getSupabaseClientOptional, registerTranslations } from '@fayz-ai/core'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport { ConversationsPage } from './views/ConversationsPage'\nimport type { ResolvedConversationsConfig } from './context'\nimport type { ConversationsProvider } from './data/types'\nimport { createMockConversationsProvider } from './data/mock'\nimport { createSupabaseConversationsProvider } from './data/supabase'\nimport { createConversationsStore } from './store'\nimport { conversationsLocales } from './locales'\nimport { MIGRATION_001_CONVERSATIONS, MIGRATION_002_CONTACT_PERSON } from './migrations'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — the GoHighLevel \"Conversations\" equivalent:\n// one omni-channel inbox (SMS / WhatsApp / Instagram / Email / Web chat).\n// Universal plugin — reusable by any vertical (beauty, resto, agency…).\n//\n// Supabase-backed (plg_conversations / plg_conversation_messages, tenant-scoped\n// via RLS) when a client is registered; a persisted mock inbox otherwise. Real\n// channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver inbound rows\n// out-of-band; this plugin is the read/compose surface.\n// ---------------------------------------------------------------------------\n\nexport interface ConversationsPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n dataProvider?: ConversationsProvider\n\n /**\n * `people.kind` for contacts created from the compose modal's picker. Each\n * vertical names its people differently (beauty 'client', agency 'lead'),\n * hence a knob rather than a hardcoded value. Default: 'contact'.\n */\n contactKind?: string\n /** Custom search source for the picker. Defaults to the person archetype lookup. */\n contactLookup?: EntityLookup\n /**\n * The app's contact entity, same definition its registry page uses. \"Create\"\n * in the picker opens THAT form. Without it the picker searches only.\n */\n contactEntityDef?: EntityDef\n}\n\nfunction createSafeProvider(): ConversationsProvider {\n // Real data when a Supabase client is registered; mock inbox otherwise.\n // Resolution is LAZY (per call): createConversationsPlugin runs at module\n // scope in the app config, BEFORE the Supabase client is registered — an\n // eager check here would capture the mock forever even on live backends.\n let real: ConversationsProvider | null = null\n let mock: ConversationsProvider | null = null\n const resolve = (): ConversationsProvider => {\n if (getSupabaseClientOptional()) {\n real ??= createSupabaseConversationsProvider({ tenantId: () => getActiveTenantId() })\n return real\n }\n mock ??= createMockConversationsProvider({ tenantId: () => getActiveTenantId() })\n return mock\n }\n return {\n listConversations: (...a) => resolve().listConversations(...a),\n getMessages: (...a) => resolve().getMessages(...a),\n sendMessage: (...a) => resolve().sendMessage(...a),\n markRead: (...a) => resolve().markRead(...a),\n setStatus: (...a) => resolve().setStatus(...a),\n createConversation: (...a) => resolve().createConversation(...a),\n }\n}\n\nexport function createConversationsPlugin(options?: ConversationsPluginOptions): PluginManifest {\n registerTranslations(conversationsLocales)\n const provider = options?.dataProvider ?? createSafeProvider()\n const store = createConversationsStore(provider)\n\n const config: ResolvedConversationsConfig = {\n contactKind: options?.contactKind ?? 'contact',\n contactLookup: options?.contactLookup,\n contactEntityDef: options?.contactEntityDef,\n }\n\n const PageComponent: React.ComponentType<unknown> = () =>\n React.createElement(ConversationsPage, { store, config })\n PageComponent.displayName = 'ConversationsPage'\n\n return {\n id: 'conversations',\n defaultAgentRole: 'frontdesk',\n name: options?.navLabel ?? 'Conversations',\n icon: 'MessageCircle',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'conversations', label: 'Conversations', group: 'Engage' }],\n // Recurring monthly quota — counts conversation threads created this month.\n declaredLimits: [\n { key: 'conversations_month', label: 'Conversations this month', table: 'plg_conversations', period: 'month' },\n ],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 1,\n label: options?.navLabel ?? 'Conversations',\n route: '/conversations',\n icon: 'MessageCircle',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/conversations',\n component: PageComponent,\n fullBleed: true,\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n widgets: [],\n events: [\n { name: 'conversations.message.received', description: 'An inbound message arrived on any channel' },\n { name: 'conversations.message.sent', description: 'An outbound message was sent' },\n ],\n aiTools: [\n {\n id: 'conversations.list-threads',\n name: 'listConversations',\n description: 'Lists open conversations across all channels, optionally filtered by channel.',\n icon: 'MessageCircle',\n mode: 'read' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n channel: {\n type: 'string' as const,\n enum: ['all', 'sms', 'whatsapp', 'instagram', 'email', 'webchat'],\n },\n },\n },\n suggestions: [\n { label: 'Show unread conversations' },\n { label: 'Any new WhatsApp messages?' },\n ],\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n id: 'conversations.send-message',\n name: 'sendMessage',\n description: 'Sends a reply in a conversation thread.',\n icon: 'Send',\n mode: 'persist' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n conversationId: { type: 'string' as const, description: 'Conversation id' },\n body: { type: 'string' as const, description: 'Message body' },\n },\n required: ['conversationId', 'body'],\n },\n permission: { feature: 'conversations', action: 'create' as const },\n },\n ],\n migrations: [\n {\n id: 'conversations-001-base-tables',\n version: '1.0.0',\n sql: MIGRATION_001_CONVERSATIONS,\n description: 'Create plg_conversations and plg_conversation_messages (tenant-scoped RLS)',\n },\n {\n id: 'conversations-002-contact-person',\n version: '1.1.0',\n sql: MIGRATION_002_CONTACT_PERSON,\n description: 'Link threads to public.people via contact_person_id (nullable, ON DELETE SET NULL)',\n },\n ],\n locales: conversationsLocales,\n }\n}\n\nexport type { ConversationsProvider } from './data/types'\nexport type { Conversation, Message, Channel, CreateConversationInput } from './types'\nexport { createMockConversationsProvider, type MockConversationsConfig } from './data/mock'\nexport {\n createSupabaseConversationsProvider,\n type SupabaseConversationsConfig,\n} from './data/supabase'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/types.ts","../src/lib/channel.ts","../src/views/shared.tsx","../src/views/ConversationList.tsx","../src/views/MessageThread.tsx","../src/views/ContactPanel.tsx","../src/views/NewConversationPanel.tsx","../src/views/InboxView.tsx","../src/views/ConversationsPage.tsx","../src/data/accents.ts","../src/data/mock.ts","../src/data/tables.ts","../src/data/supabase.ts","../src/store.ts","../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/data/channels.ts","../src/integrations/tyxter/data.ts","../src/data/payment-preflight.ts","../src/integrations/tyxter/connectorDef.tsx","../src/migrations/index.ts","../src/index.ts"],"names":["React","store","jsx","jsxs","cn","InboxIcon","useTranslation","MessageSquare","Button","client","getSupabaseClientOptional","invoke","toast","Input","getActiveTenantId"],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react'\nimport { useStore, type StoreApi } from 'zustand'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport type { EntityDef } from '@fayz-ai/core'\nimport type { ConversationsUIState } from './store'\n\n/**\n * App-tunable knobs the inbox UI reads. Kept tiny on purpose: everything here\n * describes the APP's data shape (which people are \"contacts\" in this vertical),\n * never product policy.\n */\nexport interface ResolvedConversationsConfig {\n /** `people.kind` the picker searches. */\n contactKind: string\n /** Search source for the contact picker. Defaults to the person archetype. */\n contactLookup?: EntityLookup\n /** The app's own contact entity — \"Create\" opens ITS form. Agenda's twin is `clientEntityDef`. */\n contactEntityDef?: EntityDef\n}\n\nexport const DEFAULT_CONVERSATIONS_CONFIG: ResolvedConversationsConfig = {\n contactKind: 'contact',\n}\n\nconst StoreContext = React.createContext<StoreApi<ConversationsUIState> | null>(null)\nconst ConfigContext = React.createContext<ResolvedConversationsConfig>(DEFAULT_CONVERSATIONS_CONFIG)\n\nexport function ConversationsContextProvider({\n store,\n config = DEFAULT_CONVERSATIONS_CONFIG,\n children,\n}: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n children?: React.ReactNode\n}) {\n return (\n <StoreContext.Provider value={store}>\n <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n </StoreContext.Provider>\n )\n}\n\nexport function useConversationsStore<T>(selector: (state: ConversationsUIState) => T): T {\n const store = React.useContext(StoreContext)\n if (!store) throw new Error('useConversationsStore must be used within ConversationsPage')\n return useStore(store, selector)\n}\n\nexport function useConversationsConfig(): ResolvedConversationsConfig {\n return React.useContext(ConfigContext)\n}\n","// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — domain types for the unified omni-channel\n// inbox (the GoHighLevel \"Conversations\" equivalent). Provider-agnostic.\n// ---------------------------------------------------------------------------\n\nexport type Channel = 'sms' | 'whatsapp' | 'instagram' | 'email' | 'webchat'\n\nexport type ConversationStatus = 'open' | 'snoozed' | 'closed'\n\nexport type MessageDirection = 'inbound' | 'outbound'\n\nexport interface Conversation {\n id: string\n contactName: string\n /**\n * `public.people` id when the thread is tied to a real contact record (the\n * compose modal resolves one via the shared ContactPicker). Absent for legacy\n * threads and for inbound messages from an unknown handle.\n */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel */\n contactHandle: string\n channel: Channel\n lastMessagePreview: string\n lastMessageAt: string\n unreadCount: number\n status: ConversationStatus\n assignedTo?: string\n /** Tailwind-ish accent for the avatar bubble */\n accent: string\n tags: string[]\n // Optional context shown in the contact panel.\n location?: string\n note?: string\n}\n\nexport interface Message {\n id: string\n conversationId: string\n channel: Channel\n direction: MessageDirection\n body: string\n at: string\n author: string\n}\n\nexport interface ListConversationsQuery {\n channel?: Channel | 'all'\n status?: ConversationStatus | 'all'\n search?: string\n}\n\nexport interface SendMessageInput {\n conversationId: string\n body: string\n}\n\nexport interface CreateConversationInput {\n contactName: string\n /** `public.people` id, when the contact was resolved/created by the picker. */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel. */\n contactHandle?: string\n channel: Channel\n /** Optional first outbound message; stamps preview + last_message_at. */\n firstMessage?: string\n /** Optional free-text note surfaced in the contact panel. */\n note?: string\n}\n\nexport const CHANNEL_LABELS: Record<Channel, string> = {\n sms: 'SMS',\n whatsapp: 'WhatsApp',\n instagram: 'Instagram',\n email: 'Email',\n webchat: 'Web Chat',\n}\n","import React from 'react'\nimport { MessageSquare, Phone, Instagram, Mail, Globe } from 'lucide-react'\nimport { CHANNEL_LABELS, type Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Shared channel visuals — one source of truth for the icon + accent color of\n// each channel, reused by the conversation list, thread header and contact\n// panel. Keeps the omni-channel inbox visually consistent.\n// ---------------------------------------------------------------------------\n\nexport const CHANNEL_ICON: Record<Channel, React.ComponentType<{ className?: string }>> = {\n sms: Phone,\n whatsapp: MessageSquare,\n instagram: Instagram,\n email: Mail,\n webchat: Globe,\n}\n\nexport interface ChannelAccent {\n /** Solid brand color (hex) — bubbles, dots, header chips. */\n color: string\n /** Soft tinted background classes for badges. */\n badge: string\n}\n\nexport const CHANNEL_ACCENT: Record<Channel, ChannelAccent> = {\n whatsapp: { color: '#22c55e', badge: 'bg-[#22c55e]/12 text-[#15803d] dark:text-[#4ade80]' },\n sms: { color: '#6366f1', badge: 'bg-[#6366f1]/12 text-[#4338ca] dark:text-[#a5b4fc]' },\n instagram: { color: '#ec4899', badge: 'bg-[#ec4899]/12 text-[#be185d] dark:text-[#f9a8d4]' },\n email: { color: '#0ea5e9', badge: 'bg-[#0ea5e9]/12 text-[#0369a1] dark:text-[#7dd3fc]' },\n webchat: { color: '#f59e0b', badge: 'bg-[#f59e0b]/12 text-[#b45309] dark:text-[#fcd34d]' },\n}\n\nexport { CHANNEL_LABELS }\n","import React from 'react'\nimport { cn } from '@fayz-ai/ui'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Small presentational helpers shared across the inbox panes.\n// ---------------------------------------------------------------------------\n\n/** Reactive media-query match — drives the responsive list/thread/panel layout. */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(\n () => typeof window !== 'undefined' && window.matchMedia(query).matches,\n )\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n const onChange = () => setMatches(mql.matches)\n onChange()\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [query])\n return matches\n}\n\nexport function initialsOf(name: string): string {\n return name.replace('@', '').split(/\\s+/).filter(Boolean).map((w) => w[0]).slice(0, 2).join('').toUpperCase()\n}\n\nexport function Avatar({ name, accent, size = 'md', channel }: {\n name: string\n accent: string\n size?: 'sm' | 'md' | 'lg'\n /** When set, renders a small channel glyph badge over the avatar. */\n channel?: Channel\n}) {\n const dims = size === 'lg' ? 'h-14 w-14 text-lg' : size === 'sm' ? 'h-9 w-9 text-xs' : 'h-10 w-10 text-sm'\n const Icon = channel ? CHANNEL_ICON[channel] : null\n return (\n <div className=\"relative shrink-0\">\n <div\n className={cn('flex items-center justify-center rounded-full font-semibold text-white', dims)}\n style={{ backgroundColor: accent }}\n >\n {initialsOf(name)}\n </div>\n {Icon && (\n <span\n className=\"absolute -bottom-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full ring-2 ring-card\"\n style={{ backgroundColor: CHANNEL_ACCENT[channel!].color }}\n >\n <Icon className=\"h-2.5 w-2.5 text-white\" />\n </span>\n )}\n </div>\n )\n}\n\nexport function ChannelBadge({ channel, className }: { channel: Channel; className?: string }) {\n const Icon = CHANNEL_ICON[channel]\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium',\n CHANNEL_ACCENT[channel].badge,\n className,\n )}\n >\n <Icon className=\"h-2.5 w-2.5\" />\n {CHANNEL_LABELS[channel]}\n </span>\n )\n}\n\nexport function relativeTime(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime()\n const mins = Math.round(diff / 60_000)\n if (mins < 1) return 'now'\n if (mins < 60) return `${mins}m`\n const hours = Math.round(mins / 60)\n if (hours < 24) return `${hours}h`\n const days = Math.round(hours / 24)\n if (days < 7) return `${days}d`\n return `${Math.round(days / 7)}w`\n}\n\n/** Day label for the thread date separators. */\nexport function dayLabel(iso: string): string {\n const d = new Date(iso)\n const now = new Date()\n const startOf = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime()\n const dayDiff = Math.round((startOf(now) - startOf(d)) / 86_400_000)\n if (dayDiff <= 0) return 'Today'\n if (dayDiff === 1) return 'Yesterday'\n if (dayDiff < 7) return d.toLocaleDateString([], { weekday: 'long' })\n return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: now.getFullYear() === d.getFullYear() ? undefined : 'numeric' })\n}\n","import React from 'react'\nimport { Search, Inbox as InboxIcon } from 'lucide-react'\nimport { Input, Skeleton, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport type { Channel } from '../types'\nimport { Avatar, ChannelBadge, relativeTime } from './shared'\n\nconst FILTERS: Array<Channel | 'all'> = ['all', 'whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\nexport function ConversationList({ className }: { className?: string }) {\n const t = useTranslation()\n const {\n conversations, selectedId, channelFilter, search, loading,\n select, setChannelFilter, setSearch,\n } = useConversationsStore((s) => s)\n\n return (\n <aside className={cn('w-full shrink-0 flex-col border-r border-border bg-card lg:w-[320px]', className)}>\n {/* No title: the shell header already names the page 24px away. Starting\n a conversation moved there too — it is the PAGE's action, not the list's. */}\n <div className=\"border-b border-border px-3 py-3\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t('conversations.list.search')}\n className=\"pl-8\"\n />\n </div>\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {FILTERS.map((id) => (\n <button\n key={id}\n onClick={() => setChannelFilter(id)}\n className={cn(\n 'rounded-full px-2.5 py-1 text-xs font-medium transition-colors',\n channelFilter === id\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n >\n {t(`conversations.filter.${id}`)}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {loading && conversations.length === 0 &&\n Array.from({ length: 6 }, (_, i) => (\n <div key={i} className=\"flex w-full items-start gap-3 border-b border-border/50 px-3 py-3\">\n <Skeleton className=\"h-10 w-10 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <Skeleton className=\"h-4 w-28\" />\n <Skeleton className=\"h-3 w-8\" />\n </div>\n <Skeleton className=\"mt-1.5 h-4 w-16 rounded-full\" />\n <Skeleton className=\"mt-1.5 h-3 w-3/4\" />\n </div>\n </div>\n ))}\n {!loading && conversations.length === 0 && (\n <div className=\"flex flex-col items-center gap-2 p-8 text-center text-muted-foreground\">\n <InboxIcon className=\"h-6 w-6\" />\n <p className=\"text-sm\">{t('conversations.list.empty')}</p>\n </div>\n )}\n {conversations.map((c) => {\n const active = c.id === selectedId\n const unread = c.unreadCount > 0\n return (\n <button\n key={c.id}\n onClick={() => select(c.id)}\n className={cn(\n 'flex w-full items-start gap-3 border-b border-border/50 px-3 py-3 text-left transition-colors',\n active ? 'bg-accent' : 'hover:bg-muted/50',\n )}\n >\n <Avatar name={c.contactName} accent={c.accent} channel={c.channel} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className={cn('truncate text-sm text-foreground', unread ? 'font-semibold' : 'font-medium')}>\n {c.contactName}\n </span>\n <span className={cn('shrink-0 text-[11px]', unread ? 'font-semibold text-primary' : 'text-muted-foreground')}>\n {relativeTime(c.lastMessageAt)}\n </span>\n </div>\n <div className=\"mt-1 flex items-center gap-2\">\n <ChannelBadge channel={c.channel} />\n {c.status !== 'open' && (\n <span className=\"text-[10px] uppercase tracking-wide text-muted-foreground/70\">\n {t(`conversations.status.${c.status}`)}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex items-center justify-between gap-2\">\n <span className={cn('truncate text-xs', unread ? 'text-foreground' : 'text-muted-foreground')}>\n {c.lastMessagePreview}\n </span>\n {unread && (\n <span className=\"flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground\">\n {c.unreadCount}\n </span>\n )}\n </div>\n </div>\n </button>\n )\n })}\n </div>\n </aside>\n )\n}\n","import React from 'react'\nimport { Send, Clock, Archive, PanelRight, MessageSquare, ChevronLeft } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation, Message } from '../types'\nimport { Avatar, ChannelBadge, dayLabel } from './shared'\n\n// Build a flat render list: day-separator rows interleaved with messages, and\n// each message flagged as the start of a new sender-run (WhatsApp grouping).\ntype Row =\n | { kind: 'day'; id: string; label: string }\n | { kind: 'msg'; id: string; message: Message; startsRun: boolean; endsRun: boolean }\n\nfunction buildRows(messages: Message[]): Row[] {\n const rows: Row[] = []\n let lastDay = ''\n messages.forEach((m, i) => {\n const day = new Date(m.at).toDateString()\n if (day !== lastDay) {\n rows.push({ kind: 'day', id: `day-${day}`, label: dayLabel(m.at) })\n lastDay = day\n }\n const prev = messages[i - 1]\n const next = messages[i + 1]\n const samePrev = prev && prev.direction === m.direction && new Date(prev.at).toDateString() === day\n const sameNext = next && next.direction === m.direction && new Date(next.at).toDateString() === day\n rows.push({ kind: 'msg', id: m.id, message: m, startsRun: !samePrev, endsRun: !sameNext })\n })\n return rows\n}\n\nexport function MessageThread({ selected, onTogglePanel, panelOpen, onBack, className }: {\n selected: Conversation\n onTogglePanel: () => void\n panelOpen: boolean\n onBack?: () => void\n className?: string\n}) {\n const t = useTranslation()\n const { messages, sending, send, setStatus } = useConversationsStore((s) => s)\n const [draft, setDraft] = React.useState('')\n const threadRef = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n threadRef.current?.scrollTo({ top: threadRef.current.scrollHeight, behavior: 'smooth' })\n }, [messages.length, selected.id])\n\n async function handleSend() {\n if (!draft.trim()) return\n const body = draft\n setDraft('')\n await send(body)\n }\n\n const accent = CHANNEL_ACCENT[selected.channel]\n const rows = React.useMemo(() => buildRows(messages), [messages])\n\n return (\n <section className={cn('flex min-w-0 flex-1 flex-col bg-muted/20', className)}>\n {/* Header */}\n <div className=\"flex items-center justify-between gap-2 border-b border-border bg-card px-3 py-2.5 md:px-5\">\n <div className=\"flex min-w-0 items-center gap-2 md:gap-3\">\n {onBack && (\n <Button variant=\"ghost\" size=\"icon\" className=\"-ml-1 shrink-0 lg:hidden\" onClick={onBack} aria-label={t('conversations.thread.back')}>\n <ChevronLeft className=\"h-5 w-5\" />\n </Button>\n )}\n <Avatar name={selected.contactName} accent={selected.accent} size=\"sm\" channel={selected.channel} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-foreground\">{selected.contactName}</p>\n <p className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <ChannelBadge channel={selected.channel} />\n <span className=\"truncate\">{selected.contactHandle}</span>\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setStatus('snoozed')} aria-label={t('conversations.thread.snooze')}>\n <Clock className=\"h-3.5 w-3.5 sm:mr-1\" /> <span className=\"hidden sm:inline\">{t('conversations.thread.snooze')}</span>\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setStatus('closed')} aria-label={t('conversations.thread.close')}>\n <Archive className=\"h-3.5 w-3.5 sm:mr-1\" /> <span className=\"hidden sm:inline\">{t('conversations.thread.close')}</span>\n </Button>\n <Button\n variant={panelOpen ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={onTogglePanel}\n aria-label={t('conversations.thread.details')}\n >\n <PanelRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {/* Messages */}\n <div ref={threadRef} className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4\">\n {rows.map((row) => {\n if (row.kind === 'day') {\n return (\n <div key={row.id} className=\"my-3 flex items-center justify-center\">\n <span className=\"rounded-full bg-muted px-3 py-0.5 text-[11px] font-medium text-muted-foreground\">\n {row.label}\n </span>\n </div>\n )\n }\n const m = row.message\n const outbound = m.direction === 'outbound'\n return (\n <div\n key={row.id}\n className={cn('flex', outbound ? 'justify-end' : 'justify-start', row.startsRun ? 'mt-2.5' : 'mt-0.5')}\n >\n <div\n className={cn(\n 'max-w-[68%] px-3.5 py-2 text-sm shadow-sm',\n outbound ? 'rounded-2xl text-white' : 'rounded-2xl bg-card text-foreground',\n // Tail only on the last bubble of a run, on the sender's side.\n outbound && row.endsRun && 'rounded-br-sm',\n !outbound && row.endsRun && 'rounded-bl-sm',\n )}\n style={outbound ? { backgroundColor: accent.color } : undefined}\n >\n <p className=\"whitespace-pre-wrap break-words\">{m.body}</p>\n <div className={cn('mt-0.5 text-right text-[10px]', outbound ? 'text-white/70' : 'text-muted-foreground')}>\n {new Date(m.at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n </div>\n </div>\n </div>\n )\n })}\n {rows.length === 0 && (\n <div className=\"flex h-full flex-col items-center justify-center text-muted-foreground\">\n <MessageSquare className=\"h-7 w-7\" />\n <p className=\"mt-2 text-sm\">{t('conversations.thread.empty')}</p>\n </div>\n )}\n </div>\n\n {/* Composer */}\n {/* TODO(follow-up): wire real emoji picker + attachment upload (removed the\n dead placeholder buttons rather than shipping non-functional UI). */}\n <div className=\"border-t border-border bg-card px-4 py-3\">\n <div className=\"flex items-end gap-2\">\n <textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n void handleSend()\n }\n }}\n rows={1}\n placeholder={t('conversations.thread.reply', { channel: CHANNEL_LABELS[selected.channel] })}\n className=\"max-h-32 min-h-[40px] flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary\"\n />\n <Button onClick={() => void handleSend()} disabled={sending || !draft.trim()} aria-label={t('conversations.thread.send')}>\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </section>\n )\n}\n","import React from 'react'\nimport { MapPin, User, Tag, Link2, StickyNote, X } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation } from '../types'\nimport { Avatar, ChannelBadge } from './shared'\n\nfunction Section({ icon: Icon, title, children }: {\n icon: React.ComponentType<{ className?: string }>\n title: string\n children: React.ReactNode\n}) {\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <p className=\"mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\">\n <Icon className=\"h-3 w-3\" /> {title}\n </p>\n {children}\n </div>\n )\n}\n\nexport function ContactPanel({ contact, onClose, className }: {\n contact: Conversation\n onClose?: () => void\n className?: string\n}) {\n const t = useTranslation()\n return (\n <aside className={cn('flex shrink-0 flex-col overflow-y-auto border-l border-border bg-card', className)}>\n {onClose && (\n <div className=\"flex items-center justify-between border-b border-border px-3 py-2 xl:hidden\">\n <span className=\"text-sm font-semibold text-foreground\">{t('conversations.contact.details')}</span>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label={t('conversations.contact.closeDetails')}>\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n )}\n <div className=\"flex flex-col items-center gap-2 px-4 py-5 text-center\">\n <Avatar name={contact.contactName} accent={contact.accent} size=\"lg\" channel={contact.channel} />\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{contact.contactName}</p>\n <p className=\"text-xs text-muted-foreground\">{contact.contactHandle}</p>\n </div>\n <ChannelBadge channel={contact.channel} />\n </div>\n\n <Section icon={User} title={t('conversations.contact.details')}>\n <dl className=\"space-y-1.5 text-sm\">\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.channel')}</dt>\n <dd className=\"text-foreground\">{CHANNEL_LABELS[contact.channel]}</dd>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.status')}</dt>\n <dd className=\"text-foreground\">{t(`conversations.status.${contact.status}`)}</dd>\n </div>\n {contact.assignedTo && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.assignedTo')}</dt>\n <dd className=\"text-foreground\">{contact.assignedTo}</dd>\n </div>\n )}\n {contact.location && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"flex items-center gap-1 text-muted-foreground\"><MapPin className=\"h-3 w-3\" /> {t('conversations.contact.location')}</dt>\n <dd className=\"text-foreground\">{contact.location}</dd>\n </div>\n )}\n </dl>\n </Section>\n\n {contact.tags.length > 0 && (\n <Section icon={Tag} title={t('conversations.contact.tags')}>\n <div className=\"flex flex-wrap gap-1.5\">\n {contact.tags.map((tag) => (\n <span key={tag} className=\"rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-foreground\">\n {tag}\n </span>\n ))}\n </div>\n </Section>\n )}\n\n {contact.note && (\n <Section icon={StickyNote} title={t('conversations.contact.note')}>\n <p className=\"text-sm text-foreground\">{contact.note}</p>\n </Section>\n )}\n\n <Section icon={Link2} title={t('conversations.contact.linkedRecords')}>\n <p className=\"text-xs text-muted-foreground\">{t('conversations.contact.noLinkedRecords')}</p>\n </Section>\n </aside>\n )\n}\n","import React from 'react'\nimport { Button, cn, toast } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n useLimitGuard, invalidateLimit, ContactPicker, RightRailPage,\n type ContactPickerValue,\n} from '@fayz-ai/admin'\nimport { useConversationsStore, useConversationsConfig } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\nconst CHANNELS: Channel[] = ['whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\n/**\n * Which field of a person IS the handle on a given channel. Phone channels read\n * the phone, email reads the email; Instagram and web chat have no counterpart\n * on a person record, so they always have to be typed. Deliberately NOT\n * cross-filling (an email in a WhatsApp handle looked plausible on screen and\n * was simply wrong).\n */\nfunction personHandleFor(channel: Channel, contact: ContactPickerValue | null): string {\n if (!contact) return ''\n if (channel === 'email') return contact.email ?? ''\n if (channel === 'sms' || channel === 'whatsapp') return contact.phone ?? ''\n return ''\n}\n\nconst HANDLE_LABEL_KEY: Record<Channel, string> = {\n whatsapp: 'conversations.new.handleLabel.phone',\n sms: 'conversations.new.handleLabel.phone',\n email: 'conversations.new.handleLabel.email',\n instagram: 'conversations.new.handleLabel.instagram',\n webchat: 'conversations.new.handleLabel.webchat',\n}\n\n/**\n * Starting a conversation, in the right column.\n *\n * It was a dialog over the inbox — over the very list you read to decide\n * whether a thread already exists. Beside it, that list stays legible while the\n * contact is chosen.\n */\nexport function NewConversationPanel({\n open,\n onOpenChange,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const t = useTranslation()\n const create = useConversationsStore((s) => s.create)\n const config = useConversationsConfig()\n const guardConversations = useLimitGuard('conversations_month')\n\n const [channel, setChannel] = React.useState<Channel>('whatsapp')\n const [contact, setContact] = React.useState<ContactPickerValue | null>(null)\n // Handle the user typed themselves. The one taken FROM the contact is derived\n // (see personHandleFor) and shown on the picker's chip — same as the agenda,\n // which never had a second phone field.\n const [typedHandle, setTypedHandle] = React.useState('')\n // While the picker's inline create form is open it already asks for phone and\n // email, so showing our own handle field would ask for the phone twice.\n const [creatingContact, setCreatingContact] = React.useState(false)\n const [firstMessage, setFirstMessage] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n\n // Reset the form each time the modal opens. `pickerKey` remounts the picker so\n // its internal search text resets too.\n const [pickerKey, setPickerKey] = React.useState(0)\n React.useEffect(() => {\n if (open) {\n setChannel('whatsapp')\n setContact(null)\n setTypedHandle('')\n setCreatingContact(false)\n setFirstMessage('')\n setSubmitting(false)\n setPickerKey((k) => k + 1)\n }\n }, [open])\n\n // What we'd message on this channel: the contact's own datum when they have\n // one, otherwise whatever the user typed. Recomputed on every channel switch,\n // so flipping WhatsApp → Email swaps phone for email with no stale state.\n const derivedHandle = personHandleFor(channel, contact)\n const effectiveHandle = derivedHandle || typedHandle\n const handleLabel = t(HANDLE_LABEL_KEY[channel])\n\n const canSubmit = (contact?.name.trim().length ?? 0) > 0 && !submitting\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!canSubmit) return\n setSubmitting(true)\n try {\n // Plan quantity guard (client-side, before the store call): opens the\n // global UpgradeModal and aborts when the monthly cap is reached. Inside\n // the try on purpose — it hits the network to count usage, and a rejection\n // out here used to escape unhandled, leaving the modal open with no\n // feedback at all (indistinguishable from a dead button).\n if ((await guardConversations()) === 'blocked') return\n await create({\n channel,\n contactName: contact!.name.trim(),\n contactPersonId: contact?.id,\n contactHandle: effectiveHandle.trim() || undefined,\n firstMessage: firstMessage.trim() || undefined,\n })\n invalidateLimit('conversations_month')\n onOpenChange(false)\n } catch (err) {\n // A failed insert (unresolved tenant, RLS rejection, offline…) previously\n // threw past the `finally` — `onOpenChange(false)` never ran, so the modal\n // stayed open with no feedback and the thread silently vanished. Surface\n // the failure and keep the form open so the user can retry.\n const message = err instanceof Error ? err.message : String(err)\n toast.error(t('conversations.new.createFailed'), { description: message })\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <RightRailPage\n title={t('conversations.new.title')}\n open={open}\n onClose={() => onOpenChange(false)}\n width=\"max-w-md\"\n >\n <form onSubmit={handleSubmit} className=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4\">\n {/* Channel picker */}\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.channel')}\n </label>\n <div className=\"flex flex-wrap gap-1.5\">\n {CHANNELS.map((ch) => {\n const Icon = CHANNEL_ICON[ch]\n const active = channel === ch\n return (\n <button\n key={ch}\n type=\"button\"\n onClick={() => setChannel(ch)}\n aria-pressed={active}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n active\n ? 'text-white'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n style={active ? { backgroundColor: CHANNEL_ACCENT[ch].color } : undefined}\n >\n <Icon className=\"h-3 w-3\" />\n {CHANNEL_LABELS[ch]}\n </button>\n )\n })}\n </div>\n </div>\n\n {/* Contact — shared find-or-create flow (@fayz-ai/admin), the same one\n the agenda uses to pick a client. The picker owns its label, the\n search field, the chip and the channel handle, so this composer and\n the appointment modal render the SAME thing from the same code. */}\n <ContactPicker\n key={pickerKey}\n value={contact}\n onChange={setContact}\n kind={config.contactKind}\n lookup={config.contactLookup}\n createEntityDef={config.contactEntityDef}\n allowFreeText\n onCreatingChange={setCreatingContact}\n autoFocus\n label={t('conversations.new.contactName')}\n placeholder={t('conversations.new.contactNamePlaceholder')}\n secondaryText={derivedHandle || undefined}\n handleField={{\n label: handleLabel,\n derived: derivedHandle || undefined,\n value: typedHandle,\n onChange: setTypedHandle,\n fieldLabel: t('conversations.new.handle'),\n placeholder: t('conversations.new.handlePlaceholder'),\n }}\n />\n\n {/* First message */}\n <div>\n <label htmlFor=\"conv-first-message\" className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.firstMessage')}\n </label>\n <textarea\n id=\"conv-first-message\"\n value={firstMessage}\n onChange={(e) => setFirstMessage(e.target.value)}\n rows={3}\n placeholder={t('conversations.new.firstMessagePlaceholder')}\n className=\"max-h-40 min-h-[64px] w-full resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary\"\n />\n </div>\n\n {/* No Cancel: the column header already holds the way out. */}\n <div className=\"mt-auto pt-2\">\n <Button type=\"submit\" disabled={!canSubmit} className=\"w-full\">\n {submitting ? t('conversations.new.creating') : t('conversations.new.create')}\n </Button>\n </div>\n </form>\n </RightRailPage>\n )\n}\n","import React from 'react'\nimport { MessageSquare, SquarePen } from 'lucide-react'\nimport { Button, PageHeaderActions, cn } from '@fayz-ai/ui'\nimport { PermissionGate } from '@fayz-ai/admin'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { ConversationList } from './ConversationList'\nimport { MessageThread } from './MessageThread'\nimport { ContactPanel } from './ContactPanel'\nimport { NewConversationPanel } from './NewConversationPanel'\nimport { useMediaQuery } from './shared'\n\nexport function InboxView() {\n const t = useTranslation()\n const { conversations, selectedId, deselect } = useConversationsStore((s) => s)\n // xl+ shows the contact panel inline (three panes); below that it's an\n // on-demand slide-over so the list + thread keep room to breathe.\n const isWidePanel = useMediaQuery('(min-width: 1280px)')\n const [panelOpen, setPanelOpen] = React.useState(false)\n const [newOpen, setNewOpen] = React.useState(false)\n const selected = conversations.find((c) => c.id === selectedId) ?? null\n\n // Open the panel by default only when it fits inline; collapse it otherwise.\n React.useEffect(() => {\n setPanelOpen(isWidePanel)\n }, [isWidePanel])\n\n return (\n <>\n {/* Outside the inbox row: with a header slot this portals and takes no\n space; without one it falls in as a block above the screen, rather\n than a column wedged between the list and the thread. */}\n <PermissionGate feature=\"conversations\" action=\"create\">\n <PageHeaderActions>\n <Button\n size=\"sm\"\n onClick={() => setNewOpen(true)}\n aria-label={t('conversations.list.new')}\n data-testid=\"conversations-new\"\n >\n <SquarePen className=\"h-3.5 w-3.5 sm:mr-1.5\" />\n <span className=\"hidden sm:inline\">{t('conversations.list.new')}</span>\n </Button>\n </PageHeaderActions>\n </PermissionGate>\n\n <NewConversationPanel open={newOpen} onOpenChange={setNewOpen} />\n\n <div className=\"relative flex h-full min-h-0 overflow-hidden bg-background\">\n {/* Conversation list — single-pane below lg (full width), fixed rail at lg+.\n Below lg it yields to the thread once a conversation is open. */}\n <ConversationList className={cn(selected ? 'hidden lg:flex' : 'flex')} />\n\n {/* Thread — hidden below lg until a conversation is selected. */}\n {selected ? (\n <MessageThread\n selected={selected}\n panelOpen={panelOpen}\n onTogglePanel={() => setPanelOpen((v) => !v)}\n onBack={deselect}\n className=\"flex\"\n />\n ) : (\n <section className=\"hidden min-w-0 flex-1 flex-col items-center justify-center bg-muted/20 text-muted-foreground lg:flex\">\n <MessageSquare className=\"h-9 w-9\" />\n <p className=\"mt-2 text-sm\">{t('conversations.empty.select')}</p>\n </section>\n )}\n\n {/* Contact panel — inline column at xl, slide-over overlay below xl. */}\n {selected && panelOpen && (\n <>\n <button\n className=\"absolute inset-0 z-10 bg-black/40 xl:hidden\"\n aria-label=\"Close details\"\n onClick={() => setPanelOpen(false)}\n />\n <ContactPanel\n contact={selected}\n onClose={() => setPanelOpen(false)}\n className=\"absolute inset-y-0 right-0 z-20 w-[340px] max-w-[88%] shadow-2xl xl:static xl:z-auto xl:w-[300px] xl:max-w-none xl:shadow-none\"\n />\n </>\n )}\n </div>\n </>\n )\n}\n","import React from 'react'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { ConversationsContextProvider, type ResolvedConversationsConfig } from '../context'\nimport type { ConversationsUIState } from '../store'\nimport { InboxView } from './InboxView'\n\nexport function ConversationsPage({ store, config }: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n}) {\n React.useEffect(() => {\n void store.getState().load()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return (\n <ConversationsContextProvider store={store} config={config}>\n <InboxView />\n </ConversationsContextProvider>\n )\n}\n","import type { Channel } from '../types'\n\n// Solid brand hex per channel — mirrors CHANNEL_ACCENT in ../channel.ts but\n// React-free so the data providers can stamp a sensible avatar accent on new\n// conversations without importing the icon layer.\nexport const CHANNEL_ACCENT_HEX: Record<Channel, string> = {\n whatsapp: '#22c55e',\n sms: '#6366f1',\n instagram: '#ec4899',\n email: '#0ea5e9',\n webchat: '#f59e0b',\n}\n","import type { ConversationsProvider } from './types'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n} from '../types'\n\n// Deterministic relative timestamps (no Date.now at module init for stable seeds).\nfunction minutesAgo(base: number, mins: number): string {\n return new Date(base - mins * 60_000).toISOString()\n}\n\nfunction seed(): { conversations: Conversation[]; messages: Message[] } {\n const base = new Date('2026-06-16T14:00:00Z').getTime()\n\n const conversations: Conversation[] = [\n {\n id: 'c1', contactName: 'Marina Alves', contactHandle: '+55 11 99876-1020', channel: 'whatsapp',\n lastMessagePreview: 'Perfect, can we book for Friday at 3pm?', lastMessageAt: minutesAgo(base, 4),\n unreadCount: 2, status: 'open', assignedTo: 'You', accent: '#22c55e', tags: ['Hot lead'],\n location: 'São Paulo, BR', note: 'Referred by Instagram ad — interested in full color + cut.',\n },\n {\n id: 'c2', contactName: 'Jordan Pierce', contactHandle: '+1 (415) 555-0142', channel: 'sms',\n lastMessagePreview: 'Got it — sending the deposit now.', lastMessageAt: minutesAgo(base, 22),\n unreadCount: 0, status: 'open', assignedTo: 'You', accent: '#6366f1', tags: ['Customer'],\n location: 'San Francisco, US',\n },\n {\n id: 'c3', contactName: '@thehairloft', contactHandle: 'thehairloft', channel: 'instagram',\n lastMessagePreview: 'Do you offer balayage on weekends?', lastMessageAt: minutesAgo(base, 51),\n unreadCount: 1, status: 'open', accent: '#ec4899', tags: ['New'],\n },\n {\n id: 'c4', contactName: 'David Whitman', contactHandle: 'david@whitman.co', channel: 'email',\n lastMessagePreview: 'Re: Proposal — looks great, one question on pricing…', lastMessageAt: minutesAgo(base, 95),\n unreadCount: 0, status: 'open', assignedTo: 'Sofia', accent: '#0ea5e9', tags: ['Proposal'],\n location: 'Austin, US', note: 'Evaluating the retainer tier. Decision expected this week.',\n },\n {\n id: 'c5', contactName: 'Website visitor', contactHandle: 'live chat · acme.com', channel: 'webchat',\n lastMessagePreview: 'Is anyone available to chat?', lastMessageAt: minutesAgo(base, 140),\n unreadCount: 0, status: 'snoozed', accent: '#f59e0b', tags: [],\n },\n {\n id: 'c6', contactName: 'Priya Nair', contactHandle: '+44 7700 900123', channel: 'whatsapp',\n lastMessagePreview: 'Thank you! See you next week 🙌', lastMessageAt: minutesAgo(base, 1440),\n unreadCount: 0, status: 'closed', assignedTo: 'You', accent: '#14b8a6', tags: ['Customer'],\n location: 'London, UK',\n },\n ]\n\n const messages: Message[] = [\n msg('m1', 'c1', 'whatsapp', 'inbound', 'Hi! I saw your ad — do you have availability this week?', 'Marina Alves', minutesAgo(base, 18)),\n msg('m2', 'c1', 'whatsapp', 'outbound', 'Hi Marina! Yes, we do. What service are you interested in?', 'You', minutesAgo(base, 15)),\n msg('m3', 'c1', 'whatsapp', 'inbound', 'A full color + cut.', 'Marina Alves', minutesAgo(base, 9)),\n msg('m4', 'c1', 'whatsapp', 'inbound', 'Perfect, can we book for Friday at 3pm?', 'Marina Alves', minutesAgo(base, 4)),\n\n msg('m5', 'c2', 'sms', 'outbound', 'Your appointment is confirmed for tomorrow at 10am.', 'You', minutesAgo(base, 40)),\n msg('m6', 'c2', 'sms', 'inbound', 'Got it — sending the deposit now.', 'Jordan Pierce', minutesAgo(base, 22)),\n\n msg('m7', 'c3', 'instagram', 'inbound', 'Do you offer balayage on weekends?', '@thehairloft', minutesAgo(base, 51)),\n\n msg('m8', 'c4', 'email', 'inbound', 'Re: Proposal — looks great, one question on pricing for the retainer tier.', 'David Whitman', minutesAgo(base, 95)),\n msg('m9', 'c4', 'email', 'outbound', 'Happy to walk you through it — are you free for a quick call tomorrow?', 'Sofia', minutesAgo(base, 80)),\n\n msg('m10', 'c5', 'webchat', 'inbound', 'Is anyone available to chat?', 'Website visitor', minutesAgo(base, 140)),\n\n msg('m11', 'c6', 'whatsapp', 'outbound', 'You are all set for next Tuesday. Anything else?', 'You', minutesAgo(base, 1500)),\n msg('m12', 'c6', 'whatsapp', 'inbound', 'Thank you! See you next week 🙌', 'Priya Nair', minutesAgo(base, 1440)),\n ]\n\n return { conversations, messages }\n}\n\nfunction msg(\n id: string, conversationId: string, channel: Message['channel'],\n direction: Message['direction'], body: string, author: string, at: string,\n): Message {\n return { id, conversationId, channel, direction, body, author, at }\n}\n\nexport interface MockConversationsConfig {\n /** Tenant id (value or getter) used to namespace the localStorage snapshot. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ninterface Snapshot {\n conversations: Conversation[]\n messages: Message[]\n counter: number\n}\n\n// ---------------------------------------------------------------------------\n// In-memory mock inbox with best-effort localStorage persistence, so a browser\n// reload keeps whatever the demo created/sent within the session. The store is\n// namespaced per tenant; SSR / no-window environments degrade to pure memory.\n// ---------------------------------------------------------------------------\nexport function createMockConversationsProvider(\n config?: MockConversationsConfig,\n): ConversationsProvider {\n const selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenant(): string {\n const raw = typeof config?.tenantId === 'function' ? config.tenantId() : config?.tenantId\n return raw || 'default'\n }\n\n const storageKey = () => `saas:mock:conversations:${resolveTenant()}`\n\n function hasStorage(): boolean {\n try {\n return typeof window !== 'undefined' && !!window.localStorage\n } catch {\n return false\n }\n }\n\n function load(): Snapshot {\n if (hasStorage()) {\n try {\n const raw = window.localStorage.getItem(storageKey())\n if (raw) {\n const parsed = JSON.parse(raw) as Snapshot\n if (Array.isArray(parsed.conversations) && Array.isArray(parsed.messages)) {\n return {\n conversations: parsed.conversations,\n messages: parsed.messages,\n counter: parsed.counter ?? 100,\n }\n }\n }\n } catch {\n // Corrupt snapshot — fall through to a fresh seed.\n }\n }\n const seeded = seed()\n return { conversations: seeded.conversations, messages: seeded.messages, counter: 100 }\n }\n\n const state = load()\n\n function persist(): void {\n if (!hasStorage()) return\n try {\n window.localStorage.setItem(\n storageKey(),\n JSON.stringify({\n conversations: state.conversations,\n messages: state.messages,\n counter: state.counter,\n } satisfies Snapshot),\n )\n } catch {\n // Quota / privacy mode — keep working in memory only.\n }\n }\n\n // Persist the initial seed so a first reload is already stable.\n persist()\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let list = [...state.conversations]\n if (query?.channel && query.channel !== 'all') list = list.filter((c) => c.channel === query.channel)\n if (query?.status && query.status !== 'all') list = list.filter((c) => c.status === query.status)\n if (query?.search) {\n const q = query.search.toLowerCase()\n list = list.filter(\n (c) => c.contactName.toLowerCase().includes(q) || c.lastMessagePreview.toLowerCase().includes(q),\n )\n }\n return list.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt))\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n return state.messages\n .filter((m) => m.conversationId === conversationId)\n .sort((a, b) => a.at.localeCompare(b.at))\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n const id = `c${++state.counter}`\n const conversation: Conversation = {\n id,\n contactName: input.contactName.trim(),\n contactPersonId: input.contactPersonId,\n contactHandle: input.contactHandle?.trim() ?? '',\n channel: input.channel,\n lastMessagePreview: firstMessage ?? '',\n lastMessageAt: now,\n unreadCount: 0,\n status: 'open',\n assignedTo: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || undefined,\n }\n state.conversations.unshift(conversation)\n if (firstMessage) {\n state.messages.push({\n id: `m${++state.counter}`,\n conversationId: id,\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n })\n }\n persist()\n return conversation\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const conv = state.conversations.find((c) => c.id === input.conversationId)\n const created: Message = {\n id: `m${++state.counter}`,\n conversationId: input.conversationId,\n channel: conv?.channel ?? 'sms',\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at: new Date().toISOString(),\n }\n state.messages.push(created)\n if (conv) {\n conv.lastMessagePreview = input.body\n conv.lastMessageAt = created.at\n conv.unreadCount = 0\n if (conv.status === 'closed') conv.status = 'open'\n }\n persist()\n return created\n },\n\n async markRead(conversationId: string): Promise<void> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (conv && conv.unreadCount !== 0) {\n conv.unreadCount = 0\n persist()\n }\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (!conv) throw new Error('Conversation not found')\n conv.status = status\n persist()\n return conv\n },\n }\n}\n","// Physical table names for the conversations plugin (plg_conversations* prefix).\n// Import T and reference T.<key> in the data provider so a rename lands in\n// exactly one place.\nexport const T = {\n conversations: 'plg_conversations',\n messages: 'plg_conversation_messages',\n channels: 'plg_conversations_channels',\n} as const\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport type { ConversationsProvider } from './types'\nimport { T } from './tables'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// Supabase-backed conversations provider — reads/writes the project's\n// `conversations` + `conversation_messages` tables (tenant-scoped via RLS +\n// an explicit tenant filter from the active-org context). Mirrors the inline\n// client-casting style of @fayz-ai/core's createSupabaseProvider; the global\n// client is `unknown`-typed so each call narrows the shape it needs.\n//\n// Real channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver inbound\n// rows into these tables out-of-band; this provider is the read/compose surface.\n// ---------------------------------------------------------------------------\n\nexport interface SupabaseConversationsConfig {\n /** Supabase client; defaults to the global one registered by createFayzApp. */\n supabaseClient?: unknown\n /** Active tenant id (value or getter) used to scope reads and stamp writes. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapConversation(r: Row): Conversation {\n return {\n id: String(r.id),\n contactName: (r.contact_name as string) ?? '',\n contactPersonId: (r.contact_person_id as string | null) ?? undefined,\n contactHandle: (r.contact_handle as string) ?? '',\n channel: (r.channel as Conversation['channel']) ?? 'sms',\n lastMessagePreview: (r.last_message_preview as string) ?? '',\n lastMessageAt: (r.last_message_at as string) ?? '',\n unreadCount: Number(r.unread_count ?? 0),\n status: (r.status as ConversationStatus) ?? 'open',\n assignedTo: (r.assigned_to as string | null) ?? undefined,\n accent: (r.accent as string) ?? '#6366f1',\n tags: (r.tags as string[] | null) ?? [],\n location: (r.location as string | null) ?? undefined,\n note: (r.note as string | null) ?? undefined,\n }\n}\n\nfunction mapMessage(r: Row): Message {\n return {\n id: String(r.id),\n conversationId: String(r.conversation_id),\n channel: (r.channel as Message['channel']) ?? 'sms',\n direction: (r.direction as Message['direction']) ?? 'inbound',\n body: (r.body as string) ?? '',\n at: (r.at as string) ?? '',\n author: (r.author as string) ?? '',\n }\n}\n\nexport function createSupabaseConversationsProvider(\n config?: SupabaseConversationsConfig,\n): ConversationsProvider {\n const selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenantId(): string | undefined {\n if (!config?.tenantId) return undefined\n return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId\n }\n\n function client(): { from: (t: string) => Row } {\n const supabase = (config?.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n return supabase\n }\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let q = (client().from(T.conversations) as { select: (s: string) => Row }).select('*')\n\n const tenantId = resolveTenantId()\n if (tenantId) q = (q as { eq: (c: string, v: string) => Row }).eq('tenant_id', tenantId)\n if (query?.channel && query.channel !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('channel', query.channel)\n }\n if (query?.status && query.status !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('status', query.status)\n }\n if (query?.search) {\n const term = `%${query.search}%`\n q = (q as { or: (c: string) => Row }).or(\n `contact_name.ilike.${term},last_message_preview.ilike.${term}`,\n )\n }\n q = (q as { order: (c: string, o: unknown) => Row }).order('last_message_at', {\n ascending: false,\n })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapConversation)\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n const selected = (\n client().from(T.messages) as { select: (s: string) => Row }\n ).select('*')\n const filtered = (selected as { eq: (c: string, v: string) => Row }).eq(\n 'conversation_id',\n conversationId,\n )\n const ordered = (filtered as { order: (c: string, o: unknown) => Row }).order('at', {\n ascending: true,\n })\n const { data, error } = (await ordered) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapMessage)\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const tenantId = resolveTenantId()\n // Insert with a null tenant_id fails the plg_conversations RLS WITH CHECK\n // (tenant_id ∈ user_tenant_ids()) with an opaque Postgres error. When the\n // active tenant hasn't resolved yet (bootstrap race), fail fast with an\n // actionable message the compose modal can surface via toast.\n if (!tenantId) {\n throw new Error('[plugin-conversations] Active tenant not resolved — cannot create conversation. Try again in a moment.')\n }\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n\n const convRow: Row = {\n contact_name: input.contactName.trim(),\n // Only written when the picker resolved a real person — an app that\n // hasn't run migration 002 yet would reject an unknown column, and\n // omitting it keeps the free-text path working there.\n ...(input.contactPersonId ? { contact_person_id: input.contactPersonId } : {}),\n contact_handle: input.contactHandle?.trim() || null,\n channel: input.channel,\n last_message_preview: firstMessage || null,\n last_message_at: now,\n unread_count: 0,\n status: 'open',\n assigned_to: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || null,\n }\n if (tenantId) convRow.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.conversations) as { insert: (r: Row) => Row }).insert(convRow) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!created) throw new Error('Conversation not created')\n\n // Seed the thread with the first outbound message when provided.\n if (firstMessage) {\n const msgRow: Row = {\n conversation_id: String(created.id),\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n }\n if (tenantId) msgRow.tenant_id = tenantId\n await (client().from(T.messages) as { insert: (r: Row) => Promise<unknown> }).insert(msgRow)\n }\n\n return mapConversation(created)\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const tenantId = resolveTenantId()\n\n // Resolve the channel from the parent conversation so the message matches.\n const convSelected = (client().from(T.conversations) as { select: (s: string) => Row }).select(\n 'channel',\n )\n const convFiltered = (convSelected as { eq: (c: string, v: string) => Row }).eq(\n 'id',\n input.conversationId,\n )\n const { data: conv } = (await (\n convFiltered as { maybeSingle: () => Promise<{ data: Row | null }> }\n ).maybeSingle()) as { data: Row | null }\n const channel = (conv?.channel as Message['channel']) ?? 'sms'\n\n const at = new Date().toISOString()\n const row: Row = {\n conversation_id: input.conversationId,\n channel,\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at,\n }\n if (tenantId) row.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.messages) as { insert: (r: Row) => Row }).insert(row) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n\n // Roll the parent conversation forward (preview / timestamp / unread / reopen).\n await (\n (client().from(T.conversations) as {\n update: (r: Row) => Row\n }).update({\n last_message_preview: input.body,\n last_message_at: at,\n unread_count: 0,\n status: 'open',\n }) as { eq: (c: string, v: string) => Promise<unknown> }\n ).eq('id', input.conversationId)\n\n return mapMessage(created ?? row)\n },\n\n async markRead(conversationId: string): Promise<void> {\n const { error } = (await (\n (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n unread_count: 0,\n }) as { eq: (c: string, v: string) => Promise<{ error: unknown }> }\n ).eq('id', conversationId)) as { error: unknown }\n if (error) throw error\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const updated = (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n status,\n })\n const filtered = (updated as { eq: (c: string, v: string) => Row }).eq('id', conversationId)\n const selected = (filtered as { select: () => Row }).select()\n const { data, error } = (await (\n selected as { single: () => Promise<{ data: Row | null; error: unknown }> }\n ).single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!data) throw new Error('Conversation not found')\n return mapConversation(data)\n },\n }\n}\n","import { createStore, type StoreApi } from 'zustand/vanilla'\nimport type { ConversationsProvider } from './data/types'\nimport type {\n Conversation,\n Message,\n Channel,\n ConversationStatus,\n CreateConversationInput,\n} from './types'\n\nexport interface ConversationsUIState {\n conversations: Conversation[]\n messages: Message[]\n selectedId: string | null\n channelFilter: Channel | 'all'\n search: string\n loading: boolean\n sending: boolean\n\n load(): Promise<void>\n select(id: string): Promise<void>\n deselect(): void\n setChannelFilter(channel: Channel | 'all'): Promise<void>\n setSearch(search: string): Promise<void>\n create(input: CreateConversationInput): Promise<Conversation>\n send(body: string): Promise<void>\n setStatus(status: ConversationStatus): Promise<void>\n}\n\nexport function createConversationsStore(\n provider: ConversationsProvider,\n): StoreApi<ConversationsUIState> {\n return createStore<ConversationsUIState>((set, get) => ({\n conversations: [],\n messages: [],\n selectedId: null,\n channelFilter: 'all',\n search: '',\n loading: false,\n sending: false,\n\n async load() {\n set({ loading: true })\n const conversations = await provider.listConversations({\n channel: get().channelFilter,\n search: get().search || undefined,\n })\n const selectedId = get().selectedId ?? conversations[0]?.id ?? null\n set({ conversations, loading: false, selectedId })\n if (selectedId) await get().select(selectedId)\n },\n\n async select(id: string) {\n set({ selectedId: id })\n const messages = await provider.getMessages(id)\n set({ messages })\n await provider.markRead(id)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? { ...c, unreadCount: 0 } : c)),\n }))\n },\n\n deselect() {\n set({ selectedId: null, messages: [] })\n },\n\n async setChannelFilter(channel) {\n set({ channelFilter: channel })\n await get().load()\n },\n\n async setSearch(search) {\n set({ search })\n await get().load()\n },\n\n async create(input) {\n const created = await provider.createConversation(input)\n // The thread EXISTS the moment the insert returns, so resolve on that and\n // show it optimistically. Clear filters that would hide it, prepend it,\n // select it — all local.\n set((s) => ({\n channelFilter: 'all',\n search: '',\n conversations: [created, ...s.conversations.filter((c) => c.id !== created.id)],\n selectedId: created.id,\n }))\n\n // Reconciliation (authoritative list + the thread's messages) runs in the\n // BACKGROUND. It used to be awaited here, which made the caller — the\n // compose modal — hostage to two extra round-trips: on a slow pool the\n // conversation was already in the database while the modal sat open\n // looking broken, inviting a second click and a duplicate thread.\n // Failures are non-fatal: the optimistic row above already renders.\n void (async () => {\n try {\n const conversations = await provider.listConversations({})\n // Read-after-write guard: a refetch that races the insert may not\n // return the new row yet — keep the local one rather than dropping it.\n const merged = conversations.some((c) => c.id === created.id)\n ? conversations\n : [created, ...conversations]\n set({ conversations: merged })\n await get().select(created.id)\n } catch {\n /* optimistic state stands; the next load() reconciles */\n }\n })()\n\n return created\n },\n\n async send(body: string) {\n const id = get().selectedId\n if (!id || !body.trim()) return\n set({ sending: true })\n const created = await provider.sendMessage({ conversationId: id, body: body.trim() })\n set((s) => ({\n sending: false,\n messages: [...s.messages, created],\n conversations: s.conversations.map((c) =>\n c.id === id ? { ...c, lastMessagePreview: created.body, lastMessageAt: created.at } : c,\n ),\n }))\n },\n\n async setStatus(status) {\n const id = get().selectedId\n if (!id) return\n const updated = await provider.setStatus(id, status)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? updated : c)),\n }))\n },\n }))\n}\n","export const en: Record<string, string> = {\n 'conversations.title': 'Conversations',\n 'conversations.subtitle': 'Unified inbox across every channel',\n\n // Conversation list\n 'conversations.list.search': 'Search conversations',\n 'conversations.list.loading': 'Loading…',\n 'conversations.list.empty': 'No conversations',\n 'conversations.list.new': 'New conversation',\n\n // Channel filters\n 'conversations.filter.all': 'All',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'Email',\n 'conversations.filter.webchat': 'Web',\n\n // Status labels\n 'conversations.status.open': 'Open',\n 'conversations.status.snoozed': 'Snoozed',\n 'conversations.status.closed': 'Closed',\n\n // Empty state\n 'conversations.empty.select': 'Select a conversation to start chatting',\n\n // Thread\n 'conversations.thread.back': 'Back to conversations',\n 'conversations.thread.snooze': 'Snooze',\n 'conversations.thread.close': 'Close',\n 'conversations.thread.details': 'Toggle contact details',\n 'conversations.thread.empty': 'No messages yet',\n 'conversations.thread.reply': 'Reply via {{channel}}…',\n 'conversations.thread.send': 'Send',\n\n // Contact panel\n 'conversations.contact.details': 'Details',\n 'conversations.contact.closeDetails': 'Close details',\n 'conversations.contact.channel': 'Channel',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Assigned to',\n 'conversations.contact.location': 'Location',\n 'conversations.contact.tags': 'Tags',\n 'conversations.contact.note': 'Note',\n 'conversations.contact.linkedRecords': 'Linked records',\n 'conversations.contact.noLinkedRecords': 'No linked records yet.',\n\n // New-conversation modal\n 'conversations.new.title': 'New conversation',\n 'conversations.new.channel': 'Channel',\n 'conversations.new.contactName': 'Contact name',\n 'conversations.new.contactNamePlaceholder': 'e.g. Jane Doe',\n 'conversations.new.handle': 'Phone / handle / email',\n 'conversations.new.handlePlaceholder': '+1 555 000 0000',\n // The handle field only surfaces when the picked contact has nothing usable\n // for the active channel — otherwise it is derived and shown on the chip.\n 'conversations.new.addHandle': 'Add {label}',\n 'conversations.new.handleLabel.phone': 'phone',\n 'conversations.new.handleLabel.email': 'email',\n 'conversations.new.handleLabel.instagram': 'Instagram handle',\n 'conversations.new.handleLabel.webchat': 'web chat id',\n 'conversations.new.firstMessage': 'First message',\n 'conversations.new.firstMessagePlaceholder': 'Write the first message (optional)…',\n 'conversations.new.cancel': 'Cancel',\n 'conversations.new.create': 'Start conversation',\n 'conversations.new.creating': 'Starting…',\n 'conversations.new.createFailed': 'Could not create the conversation',\n}\n","export const ptBR: Record<string, string> = {\n 'conversations.title': 'Conversas',\n 'conversations.subtitle': 'Caixa de entrada unificada de todos os canais',\n\n // Lista de conversas\n 'conversations.list.search': 'Buscar conversas',\n 'conversations.list.loading': 'Carregando…',\n 'conversations.list.empty': 'Nenhuma conversa',\n 'conversations.list.new': 'Nova conversa',\n\n // Filtros de canal\n 'conversations.filter.all': 'Todas',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'E-mail',\n 'conversations.filter.webchat': 'Web',\n\n // Rótulos de status\n 'conversations.status.open': 'Aberta',\n 'conversations.status.snoozed': 'Adiada',\n 'conversations.status.closed': 'Encerrada',\n\n // Estado vazio\n 'conversations.empty.select': 'Selecione uma conversa para começar a conversar',\n\n // Thread\n 'conversations.thread.back': 'Voltar às conversas',\n 'conversations.thread.snooze': 'Adiar',\n 'conversations.thread.close': 'Encerrar',\n 'conversations.thread.details': 'Alternar detalhes do contato',\n 'conversations.thread.empty': 'Nenhuma mensagem ainda',\n 'conversations.thread.reply': 'Responder via {{channel}}…',\n 'conversations.thread.send': 'Enviar',\n\n // Painel do contato\n 'conversations.contact.details': 'Detalhes',\n 'conversations.contact.closeDetails': 'Fechar detalhes',\n 'conversations.contact.channel': 'Canal',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Responsável',\n 'conversations.contact.location': 'Localização',\n 'conversations.contact.tags': 'Etiquetas',\n 'conversations.contact.note': 'Nota',\n 'conversations.contact.linkedRecords': 'Registros vinculados',\n 'conversations.contact.noLinkedRecords': 'Nenhum registro vinculado ainda.',\n\n // Modal de nova conversa\n 'conversations.new.title': 'Nova conversa',\n 'conversations.new.channel': 'Canal',\n 'conversations.new.contactName': 'Nome do contato',\n 'conversations.new.contactNamePlaceholder': 'ex.: Maria Silva',\n 'conversations.new.handle': 'Telefone / usuário / e-mail',\n 'conversations.new.handlePlaceholder': '+55 11 99999-0000',\n // O campo de handle só aparece quando o contato escolhido não tem o dado do\n // canal ativo — caso contrário ele é derivado e mostrado no chip.\n 'conversations.new.addHandle': 'Adicionar {label}',\n 'conversations.new.handleLabel.phone': 'telefone',\n 'conversations.new.handleLabel.email': 'e-mail',\n 'conversations.new.handleLabel.instagram': '@ do Instagram',\n 'conversations.new.handleLabel.webchat': 'id do chat',\n 'conversations.new.firstMessage': 'Primeira mensagem',\n 'conversations.new.firstMessagePlaceholder': 'Escreva a primeira mensagem (opcional)…',\n 'conversations.new.cancel': 'Cancelar',\n 'conversations.new.create': 'Iniciar conversa',\n 'conversations.new.creating': 'Iniciando…',\n 'conversations.new.createFailed': 'Não foi possível criar a conversa',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const conversationsLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","// Channel accounts — the numbers a message can leave by (migration 003).\n//\n// Read-only by construction: the table grants members SELECT and nothing else,\n// so this accessor has no write path to offer. Rows are created by the claim\n// function and advanced by the provider's webhooks.\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T } from './tables'\nimport type { Channel } from '../types'\n\n/** Whose number it is. `fallback` is the product's, shared by every tenant. */\nexport type MessagingChannelKind = 'fallback' | 'dedicated'\n\n/** The provider's own lifecycle, kept verbatim. Only `active` may carry a send. */\nexport type MessagingChannelStatus =\n | 'requested'\n | 'provisioning'\n | 'provisioned'\n | 'verifying'\n | 'active'\n | 'failed'\n | 'released'\n | 'disconnected'\n\nexport interface MessagingChannelAccount {\n id: string\n /** `null` on the product fallback row — it belongs to the software, not a tenant. */\n tenantId: string | null\n channel: Channel\n provider: string\n providerNumberId: string\n phoneE164: string | null\n kind: MessagingChannelKind\n status: MessagingChannelStatus\n displayName: string | null\n metadata: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\nexport interface ListMessagingChannelsOptions {\n /** Narrow to one provider ('tyxter'). Omitted: every provider in the pool. */\n provider?: string\n /** Narrow to one channel ('whatsapp'). */\n channel?: Channel\n /** Supabase client override; defaults to the globally registered one. */\n supabaseClient?: unknown\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapChannel(r: Row): MessagingChannelAccount {\n return {\n id: String(r.id),\n tenantId: (r.tenant_id as string | null) ?? null,\n channel: (r.channel as Channel) ?? 'whatsapp',\n provider: String(r.provider ?? ''),\n providerNumberId: String(r.provider_number_id ?? ''),\n phoneE164: (r.phone_e164 as string | null) ?? null,\n kind: (r.kind as MessagingChannelKind) ?? 'dedicated',\n status: (r.status as MessagingChannelStatus) ?? 'requested',\n displayName: (r.display_name as string | null) ?? null,\n metadata: (r.metadata as Record<string, unknown>) ?? {},\n createdAt: String(r.created_at ?? ''),\n updatedAt: String(r.updated_at ?? ''),\n }\n}\n\n/**\n * Every number this tenant can send by — its own, plus the product fallback.\n *\n * No tenant filter is applied, and that is deliberate: RLS already returns the\n * tenant's rows and the fallback row, and the fallback has no tenant to filter\n * on. Filtering here would hide exactly the row the panel most needs to show.\n */\nexport async function listMessagingChannels(\n options: ListMessagingChannelsOptions = {},\n): Promise<MessagingChannelAccount[]> {\n const supabase = (options.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n\n let q = (supabase.from(T.channels) as { select: (s: string) => Row }).select('*')\n if (options.provider) q = (q as { eq: (c: string, v: string) => Row }).eq('provider', options.provider)\n if (options.channel) q = (q as { eq: (c: string, v: string) => Row }).eq('channel', options.channel)\n // Dedicated before fallback: the tenant's own number is the answer whenever\n // there is one, and the readers of this list want it first.\n q = (q as { order: (c: string, o: unknown) => Row }).order('kind', { ascending: true })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapChannel)\n}\n\n// ── The claim's lifecycle, as a thing the UI can render ─────────────────────\n//\n// Everything below is PURE. The claim itself is an edge function call and lives\n// in ../integrations/tyxter/data.ts; what belongs here is the vocabulary of the\n// table — which states a number walks through, which of them mean \"still\n// coming\", and which single row is the tenant's own. Keeping it here rather than\n// in the panel is what lets it be tested without React.\n\n/**\n * The happy path, in order. Observed live against the provider's sandbox on\n * 2026-08-24: a provision answered `requested` and the number then walked\n * `provisioned` → `verifying` → `active` in about a second. `provisioning` is in\n * the provider's enum and was not seen at 150ms polling — kept here because the\n * enum has it and a step missing from a timeline reads as a step that failed.\n */\nexport const CHANNEL_CLAIM_TIMELINE: MessagingChannelStatus[] = [\n 'requested',\n 'provisioning',\n 'provisioned',\n 'verifying',\n 'active',\n]\n\n/**\n * The states in which the tenant already has a claim and must not start another.\n *\n * Mirrors `LIVE_CHANNEL_STATUSES` in functions/_shared/tyxter-runtime.js, which\n * is where the edge function enforces it. Two copies because an edge function\n * may not import from src/ — the vitest suite asserts they agree, so a change to\n * one that is not made to the other fails a test rather than letting the panel\n * offer a button the server will refuse.\n */\nexport const LIVE_CHANNEL_STATUSES: MessagingChannelStatus[] = [\n 'requested',\n 'provisioning',\n 'provisioned',\n 'verifying',\n 'active',\n]\n\n/** Terminal, and not a reason to stop the tenant trying again. */\nexport function isDeadChannelStatus(status: MessagingChannelStatus): boolean {\n return !LIVE_CHANNEL_STATUSES.includes(status)\n}\n\n/** On its way, but not carrying messages yet — the state the panel polls in. */\nexport function isPendingChannelStatus(status: MessagingChannelStatus): boolean {\n return LIVE_CHANNEL_STATUSES.includes(status) && status !== 'active'\n}\n\n/** The tenant's own number, live or on its way. Null when they have none. */\nexport function findDedicatedChannel(\n channels: MessagingChannelAccount[],\n tenantId?: string | null,\n): MessagingChannelAccount | null {\n return channels.find(\n (channel) => channel.kind === 'dedicated'\n && (!tenantId || channel.tenantId === tenantId)\n && !isDeadChannelStatus(channel.status),\n ) ?? null\n}\n\n/** The product's shared number, which every tenant sends by until it has its own. */\nexport function findFallbackChannel(\n channels: MessagingChannelAccount[],\n): MessagingChannelAccount | null {\n return channels.find((channel) => channel.kind === 'fallback') ?? null\n}\n\n/**\n * Can this tenant ask for a number right now?\n *\n * The same rule the edge function answers 409 on, asked before the round trip\n * so the button is disabled rather than the click refused.\n */\nexport function canClaimDedicated(\n channels: MessagingChannelAccount[],\n tenantId?: string | null,\n): boolean {\n return findDedicatedChannel(channels, tenantId) === null\n}\n","// The one place that names the Tyxter number-claim data plane.\n//\n// Kept apart from connectorDef.tsx for the reason data.ts exists on the Kiwify\n// and Stripe connectors: the credential path is the half that moves when the\n// platform broker lands, and when it does only this file should change.\nimport {\n CONNECTOR_RUNTIME_TOKEN_HEADER,\n connectorRuntimeToken,\n getSupabaseClientOptional,\n} from '@fayz-ai/core'\n\n/** The edge function that holds every phone-number call this connector makes. */\nexport const TYXTER_NUMBER_CLAIM_FUNCTION = 'tyxter-number-claim'\n\n/** The channel row, as the function hands it back. A subset of the table on purpose. */\nexport interface ClaimedChannel {\n id: string\n channel: string\n provider: string\n /** Null while the row is still a reservation with no number behind it. */\n providerNumberId: string | null\n phoneE164: string | null\n kind: string\n status: string\n displayName: string | null\n}\n\n/**\n * Where to send the human to finish the Meta registration, and how good the\n * link is.\n *\n * `source` is surfaced rather than hidden because the three are not equivalent:\n * 'session' returns the person to this very panel and names the tenant to Meta;\n * 'error_link' is the provider's own pre-targeted page; 'default' is the generic\n * dashboard, which works but assumes the browser is signed into the right\n * organization. The panel says less when the source is worse.\n */\nexport interface MetaRegistrationHandoff {\n source: 'session' | 'error_link' | 'default'\n url: string\n note: string | null\n sessionId?: string\n expiresAt?: string | null\n}\n\nexport interface ClaimResult {\n channel: ClaimedChannel\n metaRegistration: MetaRegistrationHandoff | null\n}\n\ninterface MinimalClient {\n functions: {\n invoke: (\n name: string,\n options: { body: unknown; headers?: Record<string, string> },\n ) => Promise<{ data: unknown; error: { message: string } | null }>\n }\n}\n\nfunction client(): MinimalClient {\n const supabase = getSupabaseClientOptional() as MinimalClient | null\n if (!supabase) throw new Error('Sem conexão com o banco para falar com a Tyxter.')\n return supabase\n}\n\n/**\n * One call to the data plane.\n *\n * The Supabase session rides in `Authorization` — the gateway checks it, and the\n * function re-checks membership in the tenant against it, because renting a\n * number costs money every month.\n *\n * The runtime token is BEST-EFFORT here, unlike on Kiwify. There it identifies\n * whose credential to redeem and a call without it is unanswerable; here the\n * credential is the platform's own, held once for the whole product, and the\n * function falls back to machine redemption keyed by the tenant. An app that\n * installed no token source can still claim a number, which is the right\n * outcome — refusing would be enforcing a requirement that does not exist.\n */\nasync function invoke<T>(body: Record<string, unknown>): Promise<T> {\n let headers: Record<string, string> | undefined\n try {\n headers = { [CONNECTOR_RUNTIME_TOKEN_HEADER]: await connectorRuntimeToken({ tenantId: String(body.tenantId ?? '') }) }\n } catch {\n headers = undefined\n }\n\n const { data, error } = await client().functions.invoke(TYXTER_NUMBER_CLAIM_FUNCTION, {\n body,\n ...(headers ? { headers } : {}),\n })\n if (error) throw new Error(error.message)\n return data as T\n}\n\n/**\n * A page address, cleaned up for `return_url`.\n *\n * The provider's allowlist refuses a URL carrying a fragment, credentials or a\n * non-https scheme, so the fragment goes — and this app is a hash router, which\n * means the fragment is the WHOLE route. Losing it returns the human to the\n * app's front door rather than to this panel, which is a smaller failure than a\n * session the provider refuses to create at all.\n */\nexport function returnUrlFrom(href: string | null | undefined): string | null {\n if (!href) return null\n try {\n const url = new URL(href)\n url.hash = ''\n url.username = ''\n url.password = ''\n return url.toString()\n } catch {\n return null\n }\n}\n\n/** The page the human is standing on, as a `return_url` the provider will accept. */\nexport function currentReturnUrl(): string | null {\n return typeof window === 'undefined' ? null : returnUrlFrom(window.location.href)\n}\n\n/** Rent a dedicated number for this tenant, in the given area code. */\nexport function claimDedicatedNumber(input: { tenantId: string; ddd: string }): Promise<ClaimResult> {\n return invoke<ClaimResult>({\n action: 'claim',\n tenantId: input.tenantId,\n ddd: input.ddd,\n returnUrl: currentReturnUrl(),\n })\n}\n\n/**\n * Re-read the tenant's numbers at the provider and sync the rows.\n *\n * The poll the panel runs while a claim is pending. It is not redundant with the\n * webhook: four of the five lifecycle steps emit no event at all (verified\n * live — a full provision to `active` produced zero `phone_number.*` events), so\n * without this the row would sit on \"Solicitado\" until the day the number is\n * disconnected.\n */\nexport function refreshDedicatedNumbers(tenantId: string): Promise<{ channels: ClaimedChannel[] }> {\n return invoke<{ channels: ClaimedChannel[] }>({ action: 'status', tenantId })\n}\n\n/** The link that hands the human to Meta's Embedded Signup, which no key can do headlessly. */\nexport function metaRegistrationHandoff(tenantId: string): Promise<{\n metaRegistration: MetaRegistrationHandoff | null\n channel: ClaimedChannel\n}> {\n return invoke({ action: 'handoff', tenantId, returnUrl: currentReturnUrl() })\n}\n","// \"Can this app take a Pix inside a conversation?\" — asked from the browser,\n// answered by the provider.\n//\n// It is a function call and not a table read because the answer does not live in\n// this pool. It lives at the provider, behind the PLATFORM's key, which a\n// browser must never hold — so `tyxter-payment-preflight` asks on the panel's\n// behalf and hands back only the verdict. There is nothing to cache here: a\n// merchant who has just finished the credential handoff is exactly the person\n// pressing the button again.\nimport { getActiveTenantId, getSupabaseClientOptional } from '@fayz-ai/core'\n\n/** The edge function that holds both questions. */\nexport const PAYMENT_PREFLIGHT_FUNCTION = 'tyxter-payment-preflight'\n\n/**\n * Which rail is actually behind the payments channel.\n *\n * The distinction that matters and is easy to collapse: `sandbox_default` is\n * READY — charges open, settle and fire webhooks — but the money is not real,\n * and the transparent-Pix provider options are refused. A green badge over it\n * is how a salon finds out on the day of its first real charge, so the panel\n * says \"sandbox\" beside the tick instead.\n */\nexport type PaymentActiveMode = 'sandbox_default' | 'iniciador' | 'abacate_pay'\n\nexport interface PaymentReadiness {\n /** The provider's own verdict. True for the sandbox fixture too — see `sandbox`. */\n ready: boolean\n status: string\n mode: PaymentActiveMode | null\n /** `mode === 'sandbox_default'`: ready, but not with real money. */\n sandbox: boolean\n environment: string | null\n /** Why not, when the provider says why. */\n reason: string | null\n}\n\n/** The browser handoff where the merchant enters credentials this app never sees. */\nexport interface PaymentSetupSession {\n setupUrl: string | null\n requestId: string | null\n expiresAt: string | null\n environment: string | null\n}\n\ninterface MinimalClient {\n functions: {\n invoke: (\n name: string,\n options: { body: unknown },\n ) => Promise<{ data: unknown; error: { message: string } | null }>\n }\n}\n\nasync function invoke<T>(body: Record<string, unknown>, tenantId?: string): Promise<T> {\n const supabase = getSupabaseClientOptional() as MinimalClient | null\n if (!supabase) throw new Error('Sem conexão com o banco para verificar os pagamentos.')\n const tenant = tenantId ?? getActiveTenantId()\n if (!tenant) throw new Error('Sem negócio selecionado.')\n\n const { data, error } = await supabase.functions.invoke(PAYMENT_PREFLIGHT_FUNCTION, {\n body: { ...body, tenantId: tenant },\n })\n if (!error) return data as T\n\n // supabase-js buries the function's own body in the error context. Without\n // digging it out every refusal reads as \"Edge Function returned a non-2xx\n // status code\", which is what \"not connected\", \"no key\" and a CORS preflight\n // all look like — while the function answered each with a sentence.\n let message = ''\n try {\n const payload = (await (error as { context?: { json?: () => Promise<unknown> } })\n ?.context?.json?.()) as { message?: string; error?: string } | undefined\n message = String(payload?.message ?? payload?.error ?? '').trim()\n } catch {\n /* no JSON body */\n }\n throw new Error(message || error.message)\n}\n\nexport async function readPaymentReadiness(tenantId?: string): Promise<PaymentReadiness> {\n const answer = await invoke<{ payments: PaymentReadiness }>({ action: 'status' }, tenantId)\n return answer.payments\n}\n\nexport async function openPaymentSetupSession(tenantId?: string): Promise<PaymentSetupSession> {\n return await invoke<PaymentSetupSession>({ action: 'connect' }, tenantId)\n}\n\n/**\n * Is the `pay` action safe to offer in a message template?\n *\n * The sandbox fixture counts, deliberately: a build being dogfooded on sandbox\n * credentials must be able to exercise the whole flow, and the panel is where\n * the \"sandbox\" qualifier is shown. What is refused is a channel with nothing\n * selected behind it, where a Pix button would produce a charge that never opens.\n */\nexport function canOfferPayment(readiness: PaymentReadiness | null): boolean {\n if (!readiness?.ready) return false\n return readiness.mode !== null\n}\n","// Tyxter — WhatsApp as a channel of the inbox, and the first messaging\n// connector on the standard (docs/integrations/CONNECTORS.md).\n//\n// It is the connector with NO connect form at all, and that is the whole design\n// rather than an omission. FayaLabs holds one Tyxter project per product and\n// one dashboard key with it; a tenant gets WhatsApp by agreeing to use it, not\n// by going to find a key that was never issued to them. So `fields` is empty,\n// no credential sink is involved, and connecting writes one thing: the\n// plg_connections row that says this tenant switched it on. The key is redeemed\n// server-side, per call, by the edge functions that land in WP3–WP6.\n//\n// `authKind: 'api-key'` still, and not 'oauth': Tyxter has no consent screen and\n// no app to install, so declaring OAuth would ship a button with no flow behind\n// it (the Bling failure named in the standard).\n//\n// No SQL of its own for the CONNECTION — that is a plg_connections row (029).\n// The table this plugin does add, plg_conversations_channels, is not connection\n// state: it is which phone number a message leaves by, which outlives any one\n// connector and is read by the inbox itself.\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { ExternalLink, Loader2 } from 'lucide-react'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport {\n connectionRuns,\n connectionStatus,\n createConnectionStore,\n useActiveTenantId,\n type ConnectorDefinition,\n} from '@fayz-ai/core'\nimport {\n CHANNEL_CLAIM_TIMELINE,\n canClaimDedicated,\n findDedicatedChannel,\n findFallbackChannel,\n isPendingChannelStatus,\n listMessagingChannels,\n type MessagingChannelAccount,\n type MessagingChannelStatus,\n} from '../../data/channels'\nimport {\n claimDedicatedNumber,\n metaRegistrationHandoff,\n refreshDedicatedNumbers,\n type MetaRegistrationHandoff,\n} from './data'\nimport {\n canOfferPayment,\n openPaymentSetupSession,\n readPaymentReadiness,\n type PaymentReadiness,\n} from '../../data/payment-preflight'\n\nexport const TYXTER_CONNECTOR_ID = 'tyxter'\n\n/**\n * Four seconds.\n *\n * Nobody is staring at a spinner here — the caller is an automation reacting to\n * a booking — but the budget is what stops a slow provider from holding the\n * automation executor's worker while the next twenty events queue behind it.\n * Tyxter answers `POST /v1/messages` with a 202 and does the delivery\n * asynchronously, so the call being timed is an accept, not a send.\n */\nexport const TYXTER_LATENCY_BUDGET_MS = 4000\n\n// One account per tenant: the platform's project is the same one for everybody,\n// and what the row records is consent, so there is no instance to key on.\nconst store = createConnectionStore(TYXTER_CONNECTOR_ID)\n\nconst KIND_LABEL: Record<MessagingChannelAccount['kind'], string> = {\n fallback: 'Número da plataforma',\n dedicated: 'Número dedicado',\n}\n\nconst STATUS_LABEL: Record<MessagingChannelAccount['status'], string> = {\n requested: 'Solicitado',\n provisioning: 'Em preparação',\n provisioned: 'Preparado',\n verifying: 'Em verificação',\n active: 'Ativo',\n failed: 'Falhou',\n released: 'Liberado',\n disconnected: 'Desconectado',\n}\n\n/** How long between polls while a claim is on its way. */\nconst POLL_INTERVAL_MS = 5000\n\n/**\n * The lifecycle, drawn.\n *\n * Five steps and only the last one sends. Drawn as a list rather than a single\n * badge because the wait is the thing being explained: a person who asked for a\n * phone line and sees \"Em preparação\" for two minutes needs to know how many\n * steps are left, not just which one is current.\n */\nfunction ClaimTimeline({ status }: { status: MessagingChannelStatus }) {\n if (status === 'failed' || status === 'released' || status === 'disconnected') {\n return (\n <p className=\"text-xs text-destructive\">\n {status === 'failed'\n ? 'O pedido não foi concluído. Você pode tentar de novo.'\n : `Este número saiu do ar (${STATUS_LABEL[status]}).`}\n </p>\n )\n }\n const current = CHANNEL_CLAIM_TIMELINE.indexOf(status)\n return (\n <ol className=\"space-y-1\">\n {CHANNEL_CLAIM_TIMELINE.map((step, index) => {\n const done = current > index\n const now = current === index\n return (\n <li\n key={step}\n className={`flex items-center gap-2 text-xs ${\n now ? 'font-medium text-foreground' : done ? 'text-muted-foreground' : 'text-muted-foreground/60'\n }`}\n >\n <span aria-hidden className={`h-1.5 w-1.5 rounded-full ${done || now ? 'bg-primary' : 'bg-border'}`} />\n {STATUS_LABEL[step]}\n {now && status !== 'active' && <Loader2 className=\"h-3 w-3 animate-spin\" />}\n </li>\n )\n })}\n </ol>\n )\n}\n\n/**\n * The handoff a machine cannot make.\n *\n * A rented number is not a registered one: Meta has never seen it, and WhatsApp\n * Embedded Signup is a browser window a person clicks through — the provider's\n * own connect endpoint refuses a Salvy number with `meta_registration_required`\n * and says there is no headless equivalent. So this is the honest end of the\n * flow, and it is prominent rather than tucked away, because a claim that stops\n * here without saying so looks like a claim that worked.\n */\nfunction MetaRegistrationCard({ handoff }: { handoff: MetaRegistrationHandoff }) {\n return (\n <div className=\"space-y-2 rounded-md border border-primary/40 bg-primary/5 p-3\">\n <p className=\"text-xs font-medium text-foreground\">Falta liberar o número na Meta</p>\n <p className=\"text-xs text-muted-foreground\">\n O número já é seu, mas ainda não pode enviar: a Meta exige que alguém conclua o cadastro do\n WhatsApp numa janela do navegador. É rápido, e só precisa ser feito uma vez.\n </p>\n <Button asChild size=\"sm\">\n <a href={handoff.url} target=\"_blank\" rel=\"noopener noreferrer\">\n Concluir cadastro na Meta\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </a>\n </Button>\n {handoff.source === 'default' && (\n <p className=\"text-[11px] text-muted-foreground\">\n Confira se a conta aberta no navegador é a certa antes de concluir.\n </p>\n )}\n </div>\n )\n}\n\n/**\n * Can the customer pay inside the conversation, and if not, what has to happen?\n *\n * The answer is not in this pool. It is at the provider, behind the platform's\n * key, so `tyxter-payment-preflight` asks and this renders the verdict. Three\n * states and they are deliberately not two:\n *\n * ready, real a payment connection is selected. Nothing to do.\n * ready, sandbox charges open and settle, with money that is not real.\n * Shown as ready WITH the word \"sandbox\", because a green\n * tick over a fixture is discovered on the day of the first\n * real charge.\n * not ready the handoff button, which opens a session at the provider\n * rather than linking to a dashboard the merchant then has\n * to navigate.\n *\n * The credentials are typed at the provider and never reach this app. That is\n * the accepted redundancy with the app's own AbacatePay connector, and it is\n * also the reason this panel has no form: a secret that never enters the\n * product is one the product cannot leak.\n */\nfunction TyxterPaymentsPanel() {\n const [readiness, setReadiness] = useState<PaymentReadiness | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n const [handoff, setHandoff] = useState<string | null>(null)\n const [opening, setOpening] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n void readPaymentReadiness()\n .then((value) => { if (!cancelled) setReadiness(value) })\n .catch((caught: unknown) => {\n if (!cancelled) setError(caught instanceof Error ? caught.message : 'Não deu para verificar agora.')\n })\n .finally(() => { if (!cancelled) setLoading(false) })\n return () => { cancelled = true }\n }, [])\n\n const connect = async () => {\n setOpening(true)\n setError(null)\n try {\n const session = await openPaymentSetupSession()\n setHandoff(session.setupUrl)\n // Opened by the merchant and not by us: a popup fired from an async\n // callback is the one a browser blocks, and a blocked popup reads as a\n // button that does nothing.\n } catch (caught) {\n setError(caught instanceof Error ? caught.message : 'Não deu para abrir a configuração.')\n } finally {\n setOpening(false)\n }\n }\n\n const ready = canOfferPayment(readiness)\n\n return (\n <div className=\"space-y-3 border-t pt-4\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">Pagamento no WhatsApp</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Com isso ligado, o cliente recebe o Pix na própria conversa e o agendamento fica marcado\n como pago sozinho.\n </p>\n </div>\n\n {loading && <p className=\"text-xs text-muted-foreground\">Verificando…</p>}\n\n {!loading && ready && (\n <p className=\"text-xs text-foreground\">\n <span className=\"font-medium text-emerald-600\">Pronto para cobrar</span>\n {readiness?.sandbox && (\n <span className=\"ml-2 rounded bg-amber-100 px-1.5 py-0.5 text-[11px] font-medium text-amber-800\">\n sandbox — o dinheiro não é real\n </span>\n )}\n </p>\n )}\n\n {!loading && !ready && (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n Ainda não dá para cobrar por aqui. A conta de pagamento é configurada na Tyxter, em uma\n página deles — a sua chave não passa por este app.\n {readiness?.reason ? ` (${readiness.reason})` : ''}\n </p>\n {handoff ? (\n <a\n href={handoff}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-block rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted\"\n >\n Abrir a configuração de pagamento →\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={() => { void connect() }}\n disabled={opening}\n className=\"rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted disabled:opacity-50\"\n >\n {opening ? 'Abrindo…' : 'Configurar pagamento'}\n </button>\n )}\n </div>\n )}\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n )\n}\n\n/**\n * Which numbers this tenant sends by, and the button that gets it one of its own.\n *\n * ── Why this polls ──────────────────────────────────────────────────────────\n * Verified against the provider's sandbox on 2026-08-24: provisioning a number\n * walked requested → provisioned → verifying → active in about a second and\n * emitted ZERO `phone_number.*` webhooks. The deliverable events are verified /\n * disconnected / released / quality_changed / messaging_limit_*, and none of\n * them fired. So the webhook (WP4) is not the mechanism that advances a claim —\n * asking is, and this panel asks every few seconds while the row is pending and\n * stops the moment it is not.\n */\nfunction TyxterExtraPanel() {\n const tenantId = useActiveTenantId()\n const [channels, setChannels] = useState<MessagingChannelAccount[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [ddd, setDdd] = useState('')\n const [claiming, setClaiming] = useState(false)\n const [handoff, setHandoff] = useState<MetaRegistrationHandoff | null>(null)\n const mounted = useRef(true)\n\n useEffect(() => {\n mounted.current = true\n return () => { mounted.current = false }\n }, [])\n\n const load = useCallback(async () => {\n try {\n const rows = await listMessagingChannels({ provider: TYXTER_CONNECTOR_ID, channel: 'whatsapp' })\n if (mounted.current) { setChannels(rows); setError(null) }\n } catch {\n if (mounted.current) setError('Não conseguimos ler os números agora.')\n }\n }, [])\n\n useEffect(() => { void load() }, [load])\n\n const dedicated = channels ? findDedicatedChannel(channels, tenantId) : null\n const fallback = channels ? findFallbackChannel(channels) : null\n const pending = dedicated ? isPendingChannelStatus(dedicated.status) : false\n\n // The poll, and the reason it is keyed on `pending`: a screen that keeps\n // asking after the answer arrived is a cost with no reader.\n useEffect(() => {\n if (!pending || !tenantId) return undefined\n const timer = setInterval(() => {\n void refreshDedicatedNumbers(tenantId).then(() => load()).catch(() => {})\n }, POLL_INTERVAL_MS)\n return () => clearInterval(timer)\n }, [pending, tenantId, load])\n\n // The link is asked for once the number exists and while it cannot send.\n // Not during the wait — there is nothing to register yet — and not once the\n // row carries a Meta id, because asking then costs two provider calls per\n // per panel open to be told what the row already said.\n const registered = !!dedicated?.metadata?.meta_phone_number_id\n useEffect(() => {\n if (!tenantId || !dedicated || dedicated.status !== 'active' || registered || handoff) return\n void metaRegistrationHandoff(tenantId)\n .then((answer) => { if (mounted.current) setHandoff(answer.metaRegistration) })\n .catch(() => {})\n }, [tenantId, dedicated, registered, handoff])\n\n const canClaim = !!channels && !!tenantId && canClaimDedicated(channels, tenantId)\n\n async function onClaim() {\n if (!tenantId || !/^\\d{2}$/.test(ddd)) return\n setClaiming(true)\n try {\n const result = await claimDedicatedNumber({ tenantId, ddd })\n setHandoff(result.metaRegistration)\n setDdd('')\n await load()\n toast.success('Número solicitado. Acompanhe aqui o andamento.')\n } catch (caught) {\n toast.error(caught instanceof Error ? caught.message : 'Não foi possível pedir o número.')\n } finally {\n if (mounted.current) setClaiming(false)\n }\n }\n\n return (\n <div className=\"space-y-3\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">Números</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Por onde as suas mensagens de WhatsApp saem hoje.\n </p>\n </div>\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n\n {channels && channels.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n Nenhum número configurado ainda. Assim que a plataforma liberar o número compartilhado,\n ele aparece aqui.\n </p>\n )}\n\n {channels && channels.length > 0 && (\n <ul className=\"space-y-2\">\n {channels.map((channel) => (\n <li\n key={channel.id}\n className=\"flex flex-wrap items-baseline justify-between gap-2 rounded-md border bg-muted/30 p-3 text-xs\"\n >\n <span className=\"font-medium text-foreground\">\n {channel.phoneE164 ?? channel.providerNumberId}\n </span>\n <span className=\"text-muted-foreground\">\n {KIND_LABEL[channel.kind]} · {STATUS_LABEL[channel.status]}\n </span>\n </li>\n ))}\n </ul>\n )}\n\n {dedicated && (\n <div className=\"space-y-2 rounded-md border p-3\">\n <p className=\"text-xs font-medium text-foreground\">Seu número dedicado</p>\n <ClaimTimeline status={dedicated.status} />\n {dedicated.status === 'active' && !handoff && (\n <p className=\"text-xs text-muted-foreground\">\n Ativo. As suas mensagens saem por ele, com o seu nome e sem dividir com ninguém.\n </p>\n )}\n </div>\n )}\n\n {handoff && <MetaRegistrationCard handoff={handoff} />}\n\n {canClaim && (\n <div className=\"space-y-2 border-t pt-3\">\n <p className=\"text-xs font-medium text-foreground\">Quer um número só seu?</p>\n <p className=\"text-xs text-muted-foreground\">\n {fallback\n ? 'Hoje as suas mensagens saem pelo número da plataforma, dividido com outros negócios. Um número dedicado sai só com o seu nome, libera os seus próprios modelos de mensagem e tem limite de envio próprio.'\n : 'Um número dedicado sai só com o seu nome e tem limite de envio próprio.'}\n </p>\n <div className=\"flex items-end gap-2\">\n <label className=\"text-xs text-muted-foreground\">\n DDD\n <Input\n value={ddd}\n onChange={(e) => setDdd(e.target.value.replace(/\\D/g, '').slice(0, 2))}\n inputMode=\"numeric\"\n placeholder=\"21\"\n className=\"mt-1 w-20\"\n aria-label=\"DDD do número dedicado\"\n />\n </label>\n <Button size=\"sm\" disabled={claiming || !/^\\d{2}$/.test(ddd)} onClick={() => void onClaim()}>\n {claiming && <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />}\n Solicitar número dedicado\n </Button>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n O número é alugado no mês e, depois que chegar, ainda precisa de um cadastro rápido na\n Meta para começar a enviar.\n </p>\n </div>\n )}\n\n <TyxterPaymentsPanel />\n </div>\n )\n}\n\nexport const tyxterConnectorDef: ConnectorDefinition = {\n id: TYXTER_CONNECTOR_ID,\n hostPluginId: 'conversations',\n name: 'Tyxter',\n description:\n 'WhatsApp já pronto: confirmações e lembretes saem por um número da plataforma, e a resposta do cliente cai na sua caixa de entrada.',\n icon: 'MessageCircle',\n logoUrl: 'https://assets.fayz.ai/connectors/tyxter.png',\n category: 'messaging',\n authKind: 'api-key',\n // Sending is in line with the automation that asked for it; the reply, the\n // delivery status and the template approvals arrive later and file runs.\n planes: ['request', 'sync'],\n direction: 'outbound',\n docsUrl: 'https://tyxter.com/docs',\n // A message that does not go out must never take the booking with it. The\n // opposite verdict from a payment gateway, and for the opposite reason.\n latencyBudgetMs: TYXTER_LATENCY_BUDGET_MS,\n onFailure: 'degrade',\n permissions: [\n {\n kind: 'data',\n resource: 'conversation',\n actions: ['read', 'write'],\n reason:\n 'Abrir a conversa do cliente no WhatsApp e mantê-la em dia — é onde a mensagem enviada e a resposta dele ficam juntas.',\n },\n {\n kind: 'data',\n resource: 'message',\n actions: ['read', 'write'],\n reason:\n 'Guardar cada mensagem enviada e recebida, com o status que o WhatsApp devolve (enviada, entregue, lida).',\n },\n {\n kind: 'data',\n resource: 'message_template',\n actions: ['read', 'write'],\n reason:\n 'Ler os modelos de mensagem deste app e guardar o que a Meta respondeu sobre cada um: aprovado, recusado ou pausado.',\n },\n ],\n // No form: the key is the platform's. The connect step says so on its own\n // (`connectors.authorize.nothingToFill`) rather than drawing an empty grid.\n fields: [],\n invokedAt: [\n {\n key: 'messaging.send',\n label: 'Uma automação dispara e a mensagem sai no WhatsApp do cliente',\n touches: ['conversation', 'message'],\n },\n {\n key: 'conversations.inbound',\n label: 'O cliente responde, e a resposta entra na caixa de entrada',\n plane: 'sync',\n trigger: 'webhook',\n touches: ['conversation', 'message'],\n },\n {\n key: 'messaging.template_sync',\n label: 'Os modelos de mensagem vão para aprovação da Meta e o resultado volta para cá',\n plane: 'sync',\n trigger: 'scheduled',\n touches: ['message_template'],\n },\n ],\n howItSyncs: [\n 'As mensagens saem por um número de WhatsApp da plataforma, que já vem pronto — você não cadastra chave nenhuma nem precisa de um chip só seu.',\n 'Como esse número é dividido com outros negócios, o nome do seu vai escrito no começo da mensagem, e só modelos já aprovados pela Meta podem sair por ele.',\n 'Se você pedir um número dedicado, ele passa a ser o remetente assim que ficar ativo — aí a mensagem sai só com o seu nome e nada mais é dividido.',\n 'A resposta do cliente volta para Conversas em segundos, e botões como Confirmar e Cancelar já atualizam o agendamento sozinhos.',\n ],\n ...connectionStatus(\n TYXTER_CONNECTOR_ID,\n (connection) => ({ connected: !!connection?.active, plane: 'request' }),\n { store: () => store },\n ),\n ...connectionRuns(store),\n // Nothing to receive: the hub split out a credential half that does not exist\n // for this connector, so `values` is empty and consent is the whole act.\n saveConnection: async () => {\n await store.save({ active: true })\n },\n disconnect: () => store.disconnect(),\n disconnectNote:\n 'Desconectar para de enviar e de receber mensagens no WhatsApp. As conversas que já existem continuam aqui.',\n ExtraPanel: TyxterExtraPanel,\n}\n","// AUTO-GENERATED from 001_conversations.sql, 002_contact_person.sql, 003_channels.sql, 004_message_delivery.sql, 005_webhook_events.sql, 006_optouts.sql, 007_inbound_routing.sql, 008_message_subject.sql, 009_payment_requests.sql — regenerate with scripts/embed-migrations.mjs\n// SQL files are the source of truth; this inline copy lets the manifest declare\n// migrations as data. Do not edit by hand — run the embed script instead.\n\nexport const MIGRATION_001_CONVERSATIONS = `-- ============================================================================\n-- plugin-conversations 001: omni-channel inbox model (SMS / WhatsApp /\n-- Instagram / Email / Web chat). Prefix: plg_conversations / plg_conversation_messages.\n-- §1 plg_conversations — one thread per contact+channel\n-- §2 plg_conversation_messages — inbound/outbound messages within a thread\n-- §3 RLS: authenticated tenant-scoped CRUD on both tables + GRANTs\n--\n-- Column names mirror exactly what supabase.ts's mapConversation / mapMessage\n-- read. Real channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver\n-- inbound rows here out-of-band; the provider is the read/compose surface.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\n-- §1 — conversations (threads)\nCREATE TABLE IF NOT EXISTS public.plg_conversations (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n contact_name text NOT NULL,\n contact_handle text,\n channel text NOT NULL\n CHECK (channel IN ('sms', 'whatsapp', 'instagram', 'email', 'webchat')),\n last_message_preview text,\n last_message_at timestamptz DEFAULT now(),\n unread_count int DEFAULT 0,\n status text DEFAULT 'open'\n CHECK (status IN ('open', 'snoozed', 'closed')),\n assigned_to text,\n accent text,\n tags text[],\n location text,\n note text,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_conversations ENABLE ROW LEVEL SECURITY;\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_tenant ON public.plg_conversations(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_tenant_recent ON public.plg_conversations(tenant_id, last_message_at DESC);\n\n-- §2 — messages\nCREATE TABLE IF NOT EXISTS public.plg_conversation_messages (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n conversation_id uuid NOT NULL REFERENCES public.plg_conversations(id) ON DELETE CASCADE,\n channel text\n CHECK (channel IS NULL OR channel IN ('sms', 'whatsapp', 'instagram', 'email', 'webchat')),\n direction text\n CHECK (direction IN ('inbound', 'outbound')),\n body text NOT NULL,\n author text,\n at timestamptz DEFAULT now()\n);\nALTER TABLE public.plg_conversation_messages ENABLE ROW LEVEL SECURITY;\nCREATE INDEX IF NOT EXISTS idx_plg_conversation_messages_thread ON public.plg_conversation_messages(conversation_id, at);\n\n-- §3 — RLS: authenticated tenant CRUD (the inbox reads/writes here)\nDROP POLICY IF EXISTS plg_conversations_select ON public.plg_conversations;\nDROP POLICY IF EXISTS plg_conversations_insert ON public.plg_conversations;\nDROP POLICY IF EXISTS plg_conversations_update ON public.plg_conversations;\nDROP POLICY IF EXISTS plg_conversations_delete ON public.plg_conversations;\nCREATE POLICY plg_conversations_select ON public.plg_conversations FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversations_insert ON public.plg_conversations FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversations_update ON public.plg_conversations FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversations_delete ON public.plg_conversations FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations TO authenticated;\n\nDROP POLICY IF EXISTS plg_conversation_messages_select ON public.plg_conversation_messages;\nDROP POLICY IF EXISTS plg_conversation_messages_insert ON public.plg_conversation_messages;\nDROP POLICY IF EXISTS plg_conversation_messages_update ON public.plg_conversation_messages;\nDROP POLICY IF EXISTS plg_conversation_messages_delete ON public.plg_conversation_messages;\nCREATE POLICY plg_conversation_messages_select ON public.plg_conversation_messages FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversation_messages_insert ON public.plg_conversation_messages FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversation_messages_update ON public.plg_conversation_messages FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_conversation_messages_delete ON public.plg_conversation_messages FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversation_messages TO authenticated;\n`\n\nexport const MIGRATION_002_CONTACT_PERSON = `-- ============================================================================\n-- plugin-conversations 002: link a thread to a REAL person record.\n--\n-- The compose modal used to take a free-text name + handle, so a conversation\n-- with \"Maria\" had nothing to do with the Maria in the agenda, the CRM or the\n-- financial module. The shared ContactPicker (find-or-create over\n-- public.people) now resolves a person, and this column stores that link.\n--\n-- Nullable on purpose, in both directions of time:\n-- • rows created before this migration keep working (name/handle only);\n-- • an inbound message from an unknown number still opens a thread with no\n-- person attached — the contact panel can offer \"create contact\" later.\n-- ON DELETE SET NULL: deleting a person must never take their history with it.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nALTER TABLE public.plg_conversations\n ADD COLUMN IF NOT EXISTS contact_person_id uuid REFERENCES public.people(id) ON DELETE SET NULL;\n\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_person\n ON public.plg_conversations(tenant_id, contact_person_id)\n WHERE contact_person_id IS NOT NULL;\n`\n\nexport const MIGRATION_003_CHANNELS = `-- ============================================================================\n-- plugin-conversations 003: channel accounts — WHICH number a message leaves by.\n--\n-- The inbox has always been the read/compose surface for rows a connector\n-- delivered out-of-band. This is the first table that says where those rows\n-- come FROM, and it exists because WhatsApp has two number models at once:\n--\n-- • kind 'fallback' — the product's own number, shared by every tenant.\n-- tenant_id IS NULL: it belongs to the software, not to a salon, and a\n-- tenant column with a made-up value there would make every read lie.\n-- • kind 'dedicated' — a number rented for one tenant. Once it is \\`active\\`\n-- it wins over the fallback; until then the fallback keeps carrying.\n--\n-- Provider-neutral on purpose (\\`provider\\`, \\`provider_number_id\\`): the sender\n-- model is a WhatsApp fact, not a Tyxter fact, and the second messaging\n-- provider must not need a second table.\n--\n-- Writes are SERVICE-ROLE ONLY. Every row is created by a webhook or by an\n-- edge function acting on the provider's answer — a member who could edit\n-- \\`provider_number_id\\` could point another tenant's replies at their own inbox.\n-- Members read, and the fallback row is readable by all of them because it is\n-- the number their own messages go out on.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nCREATE TABLE IF NOT EXISTS public.plg_conversations_channels (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n -- NULL = the product fallback row. Not a missing value: an owner.\n tenant_id uuid REFERENCES public.tenants(id) ON DELETE CASCADE,\n channel text NOT NULL DEFAULT 'whatsapp'\n CHECK (channel IN ('sms', 'whatsapp', 'instagram', 'email', 'webchat')),\n provider text NOT NULL,\n -- The provider's handle for the number ('pn_…'); what a webhook routes on.\n provider_number_id text NOT NULL,\n phone_e164 text,\n kind text NOT NULL DEFAULT 'dedicated'\n CHECK (kind IN ('fallback', 'dedicated')),\n -- The provider's own lifecycle, kept verbatim so a status webhook never has\n -- to be translated into a vocabulary of ours that means slightly less.\n status text NOT NULL DEFAULT 'requested'\n CHECK (status IN ('requested', 'provisioning', 'provisioned', 'verifying',\n 'active', 'failed', 'released', 'disconnected')),\n display_name text,\n metadata jsonb NOT NULL DEFAULT '{}'::jsonb,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_conversations_channels ENABLE ROW LEVEL SECURITY;\n\n-- One row per number at a provider: the webhook resolves the tenant by this\n-- pair, and a duplicate would make that resolution a coin toss.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversations_channels_number\n ON public.plg_conversations_channels(provider, provider_number_id);\n\n-- At most one fallback per (channel, provider). Sender resolution falls back to\n-- \"the product's number\", singular — two of them is an ambiguity no caller can\n-- resolve, and it would surface as messages leaving by the wrong one.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversations_channels_fallback\n ON public.plg_conversations_channels(channel, provider)\n WHERE tenant_id IS NULL AND kind = 'fallback';\n\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_channels_tenant\n ON public.plg_conversations_channels(tenant_id, channel, status)\n WHERE tenant_id IS NOT NULL;\n\n-- RLS: members read their own rows AND the product fallback (it is the number\n-- they send from, so hiding it would leave the panel with nothing to say).\n-- No INSERT/UPDATE/DELETE policy and no write GRANT: service role only.\nDROP POLICY IF EXISTS plg_conversations_channels_select ON public.plg_conversations_channels;\nCREATE POLICY plg_conversations_channels_select ON public.plg_conversations_channels\n FOR SELECT TO authenticated\n USING (tenant_id IS NULL OR tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT ON public.plg_conversations_channels TO authenticated;\n`\n\nexport const MIGRATION_004_MESSAGE_DELIVERY = `-- ============================================================================\n-- plugin-conversations 004: what the provider did with a message, and who wrote\n-- it. Four columns, ported from the legacy beautyplace inbox.\n--\n-- \\`direction\\` + \\`author\\` was enough while every row was typed by a person in\n-- this app. It stops being enough the moment a message is sent by a connector:\n--\n-- provider_message_id the provider's handle for the row. A delivery webhook\n-- names only this, so without it a status can never be applied to anything.\n-- UNIQUE (where present) so an at-least-once webhook that redelivers the\n-- same inbound message cannot open a second copy of it in the thread.\n-- delivery_status the last thing the provider said, applied MONOTONICALLY\n-- by the webhook handler — 'read' must not be overwritten by a 'delivered'\n-- that arrived late (they are unordered).\n-- sender_kind person / app / assistant / automation. The backbone of\n-- a shared inbox: legacy proved that a human and an AI writing into the same\n-- thread with no attribution is a thread nobody trusts.\n-- sender_label who exactly, in words — \"Ana\", \"Lembrete de horário\".\n--\n-- Nullable throughout: rows written before this migration are not wrong, they\n-- are simply from a time when only people wrote here.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nALTER TABLE public.plg_conversation_messages\n ADD COLUMN IF NOT EXISTS provider_message_id text,\n ADD COLUMN IF NOT EXISTS delivery_status text,\n ADD COLUMN IF NOT EXISTS sender_kind text,\n ADD COLUMN IF NOT EXISTS sender_label text;\n\n-- Added apart from the column so a re-run on a pool that already has the column\n-- still installs the constraint (ADD COLUMN IF NOT EXISTS skips its inline ones).\nDO $$\nBEGIN\n IF NOT EXISTS (\n SELECT 1 FROM pg_constraint WHERE conname = 'plg_conversation_messages_delivery_status_check'\n ) THEN\n ALTER TABLE public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_delivery_status_check\n CHECK (delivery_status IS NULL OR delivery_status IN\n ('queued', 'sent', 'delivered', 'read', 'failed', 'expired'));\n END IF;\n IF NOT EXISTS (\n SELECT 1 FROM pg_constraint WHERE conname = 'plg_conversation_messages_sender_kind_check'\n ) THEN\n ALTER TABLE public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_sender_kind_check\n CHECK (sender_kind IS NULL OR sender_kind IN ('user', 'system', 'ai', 'automation'));\n END IF;\nEND\n$$;\n\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversation_messages_provider_message\n ON public.plg_conversation_messages(provider_message_id)\n WHERE provider_message_id IS NOT NULL;\n`\n\nexport const MIGRATION_005_WEBHOOK_EVENTS = `-- ============================================================================\n-- plugin-conversations 005: the dedupe ledger — one row per event the provider\n-- ever delivered, and the reason a retry is free.\n--\n-- Tyxter delivers AT LEAST ONCE: 8 attempts over ~32.7 hours, and an attempt\n-- counts as failed on anything that is not a 2xx inside 10 seconds. So the same\n-- \\`message.received\\` arrives again whenever our answer was slow, and without\n-- this table the second delivery opens a second message in the thread and\n-- re-applies the button the customer pressed once.\n--\n-- Deliberately NOT keyed on the message: the same message produces several\n-- events (sent, delivered, read) and the same event may name no message at all\n-- (a template approval). The identity that is unique per DELIVERED FACT is the\n-- provider's event id, so that is the key.\n--\n-- The insert is the lock. \\`ON CONFLICT DO NOTHING … RETURNING id\\` returns a row\n-- for the first delivery and nothing for every later one, which is a claim\n-- taken in a single statement — a SELECT-then-INSERT would let two concurrent\n-- retries both find nothing and both apply.\n--\n-- Service-role only, and not because the rows are secret: a member who could\n-- delete one could make a webhook re-apply, and one who could insert could make\n-- a real delivery be skipped as a duplicate.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nCREATE TABLE IF NOT EXISTS public.plg_conversations_webhook_events (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n provider text NOT NULL,\n -- The provider's id for the fact ('evt_…'), NOT for the message.\n event_id text NOT NULL,\n event_type text NOT NULL,\n received_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_conversations_webhook_events ENABLE ROW LEVEL SECURITY;\n\n-- No tenant column on purpose: at claim time the tenant is not known yet — the\n-- whole point of the row is to stop the SECOND delivery before any of the work\n-- that would resolve one. Scoped by provider so two providers cannot collide on\n-- an id shape neither of them controls.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversations_webhook_events_event\n ON public.plg_conversations_webhook_events(provider, event_id);\n\n-- For the sweep that keeps this table from growing forever. Nothing prunes it\n-- yet; retention past the provider's 32.7h retry horizon buys nothing, and an\n-- index the pruner will need is cheaper to add now than to add under load.\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_webhook_events_received\n ON public.plg_conversations_webhook_events(received_at);\n\n-- RLS on with NO policy and no GRANT: service role only, in both directions.\n`\n\nexport const MIGRATION_006_OPTOUTS = `-- ============================================================================\n-- plugin-conversations 006: who told us to stop.\n--\n-- Tyxter keeps its own consent ledger and refuses a send to an opted-out\n-- contact at its edge. This table is not a copy of that for redundancy's sake —\n-- it exists so the REFUSAL IS OURS. A send that fails at the provider is a\n-- failed send: a queued row, a failed delivery status, a retry, and a line in\n-- an operator's screen saying WhatsApp is broken. A send that never leaves\n-- because this row exists is a suppression, which is a different fact and the\n-- only one a person can act on.\n--\n-- \\`tenant_id\\` is NULLABLE and the null carries meaning, the same way it does on\n-- plg_conversations_channels:\n-- • NULL — the customer opted out of the PRODUCT's shared fallback number.\n-- They cannot have consented to one salon and not another on a number all\n-- the salons share, so the suppression is product-wide.\n-- • set — the customer opted out of that tenant's dedicated number. Their\n-- other conversations are untouched.\n--\n-- Written by the webhook (contact.opted_out / contact.erased) and read by\n-- messaging-send before every send. Nothing else writes it: a member who could\n-- delete a row could resume messaging somebody who asked us to stop, which is\n-- the one mistake in this file with a legal name.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nCREATE TABLE IF NOT EXISTS public.plg_conversations_optouts (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n channel text NOT NULL DEFAULT 'whatsapp'\n CHECK (channel IN ('sms', 'whatsapp', 'instagram', 'email', 'webchat')),\n phone_e164 text NOT NULL,\n -- NULL = the product's shared number. Not a missing value: a scope.\n tenant_id uuid REFERENCES public.tenants(id) ON DELETE CASCADE,\n -- No DEFAULT: the table is provider-neutral, and a column that quietly\n -- defaults to one provider is how the second messaging provider's rows end up\n -- filed under the first one's name. Every writer names it.\n provider text NOT NULL,\n reason text,\n created_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_conversations_optouts ENABLE ROW LEVEL SECURITY;\n\n-- NULL-safe uniqueness. A plain UNIQUE(channel, phone_e164, tenant_id) does not\n-- constrain the product-wide rows at all — in SQL two NULLs are not equal, so\n-- every redelivered contact.opted_out would insert another row and the\n-- suppression read would have to be a DISTINCT. The sentinel uuid is never a\n-- real tenant id, so COALESCE gives the three columns one identity.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversations_optouts_contact\n ON public.plg_conversations_optouts(\n channel, phone_e164,\n COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::uuid));\n\n-- The read messaging-send makes on every send: \"is this number suppressed for\n-- this tenant, or product-wide?\"\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_optouts_lookup\n ON public.plg_conversations_optouts(channel, phone_e164);\n\n-- RLS: members READ the suppressions that apply to them (their own tenant's and\n-- the product-wide ones), because \"why did this customer stop getting messages\"\n-- is a question the inbox has to be able to answer. Writes are service-role\n-- only — no INSERT/UPDATE/DELETE policy, no write GRANT.\nDROP POLICY IF EXISTS plg_conversations_optouts_select ON public.plg_conversations_optouts;\nCREATE POLICY plg_conversations_optouts_select ON public.plg_conversations_optouts\n FOR SELECT TO authenticated\n USING (tenant_id IS NULL OR tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT ON public.plg_conversations_optouts TO authenticated;\n`\n\nexport const MIGRATION_007_INBOUND_ROUTING = `-- ============================================================================\n-- plugin-conversations 007: the two things the webhook needs that 004 could not\n-- know it would.\n--\n-- §1 delivery_status gains the statuses the provider actually emits.\n-- §2 plg_conversations_match_by_phone — find the thread a WhatsApp number\n-- belongs to, when the number was typed by a person and delivered by Meta.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\n-- §1 — the closed set was one delivery short --------------------------------\n--\n-- 004 wrote the vocabulary from the design. Registering a webhook endpoint at\n-- Tyxter answers with the ENUMERATED list of deliverable events, and it holds\n-- three the CHECK would have refused: \\`message.delivery_timeout\\` (the provider\n-- gave up before the recipient's device ever acknowledged), \\`message.cancelled\\`\n-- (an accepted message pulled back before it left), and \\`message.opted_out\\`\n-- (refused at the provider because the contact withdrew consent — see 006).\n--\n-- Refusing them is worse than it sounds: the handler's UPDATE fails, the\n-- webhook answers non-2xx, and Tyxter retries the same event eight times over a\n-- day and a half before giving up. The row keeps whatever it said before, which\n-- is 'sent'. An operator reads \"sent\" for a message that was never delivered.\nDO $$\nBEGIN\n IF EXISTS (\n SELECT 1 FROM pg_constraint WHERE conname = 'plg_conversation_messages_delivery_status_check'\n ) THEN\n ALTER TABLE public.plg_conversation_messages\n DROP CONSTRAINT plg_conversation_messages_delivery_status_check;\n END IF;\n ALTER TABLE public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_delivery_status_check\n CHECK (delivery_status IS NULL OR delivery_status IN\n ('queued', 'sent', 'delivered', 'read',\n 'failed', 'expired', 'delivery_timeout', 'cancelled', 'opted_out'));\nEND\n$$;\n\n-- §2 — matching a phone number to a thread ----------------------------------\n--\n-- The problem, ported from beautyplace where it cost a duplicate thread per\n-- returning customer: the receptionist typed \\`(21) 99889-9889\\`, WhatsApp\n-- delivers \\`+5521998899889\\`, and the mobile ninth digit is optional on records\n-- older than 2016. Three spellings of one person, and \\`=\\` matches none of them.\n--\n-- The rule is digit-suffix, longest first:\n-- last 10 = DDD + the 8 significant digits. Specific: a collision needs two\n-- customers in the same area code sharing 8 digits.\n-- last 8 = the subscriber digits alone. The FALLBACK, tried only when the\n-- first finds nothing, because it crosses area codes.\n--\n-- SECURITY DEFINER and CROSS-TENANT by construction, which is the part to read\n-- carefully. The product's fallback number is shared by every tenant in the\n-- pool, so an inbound message on it arrives with no tenant attached and finding\n-- one means looking at all of them. That is exactly the read that must never be\n-- reachable by a member, so EXECUTE is granted to service_role ONLY and\n-- explicitly revoked from authenticated and anon. The webhook holds the\n-- service-role key; nothing in the browser can call this.\n-- \\`p_tenant_id\\` narrows the search to one tenant, which is what a DEDICATED\n-- number wants: the tenant is already known from the channel row, and the\n-- cross-tenant search could hand back another salon's thread for the same\n-- customer. NULL searches the whole pool, which is the fallback number's case\n-- and the only one that has no other answer.\nCREATE OR REPLACE FUNCTION public.plg_conversations_match_by_phone(\n p_channel text,\n p_last10 text,\n p_last8 text,\n p_tenant_id uuid DEFAULT NULL\n) RETURNS TABLE (\n id uuid,\n tenant_id uuid,\n contact_name text,\n contact_handle text,\n contact_person_id uuid\n)\nLANGUAGE sql\nSTABLE\nSECURITY DEFINER\nSET search_path = public\nAS $fn$\n WITH candidate AS (\n SELECT c.id, c.tenant_id, c.contact_name, c.contact_handle, c.contact_person_id,\n c.last_message_at,\n regexp_replace(COALESCE(c.contact_handle, ''), '[^0-9]', '', 'g') AS digits\n FROM public.plg_conversations c\n WHERE c.channel = p_channel\n AND (p_tenant_id IS NULL OR c.tenant_id = p_tenant_id)\n )\n SELECT id, tenant_id, contact_name, contact_handle, contact_person_id\n FROM candidate\n WHERE length(digits) >= 8\n AND ((p_last10 IS NOT NULL AND right(digits, 10) = p_last10)\n OR (p_last8 IS NOT NULL AND right(digits, 8) = p_last8))\n -- The 10-digit match wins over an 8-digit one even when the 8-digit thread\n -- is newer: a wrong area code is a different person, and a newer wrong\n -- answer is still wrong.\n ORDER BY (p_last10 IS NOT NULL AND right(digits, 10) = p_last10) DESC,\n last_message_at DESC NULLS LAST\n LIMIT 1;\n$fn$;\n\nREVOKE ALL ON FUNCTION public.plg_conversations_match_by_phone(text, text, text, uuid) FROM public;\nREVOKE ALL ON FUNCTION public.plg_conversations_match_by_phone(text, text, text, uuid) FROM anon;\nREVOKE ALL ON FUNCTION public.plg_conversations_match_by_phone(text, text, text, uuid) FROM authenticated;\nGRANT EXECUTE ON FUNCTION public.plg_conversations_match_by_phone(text, text, text, uuid) TO service_role;\n\n-- The suffix is computed per row, so without this the match is a full scan of\n-- every WhatsApp thread in the pool on every inbound message. All three\n-- functions are IMMUTABLE, which is what makes the expression indexable.\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_handle_suffix\n ON public.plg_conversations(\n channel,\n right(regexp_replace(COALESCE(contact_handle, ''), '[^0-9]', '', 'g'), 8),\n last_message_at DESC);\n`\n\nexport const MIGRATION_008_MESSAGE_SUBJECT = `-- ============================================================================\n-- plugin-conversations 008: what a message was ABOUT.\n--\n-- Two columns, and they exist because of one unproved assumption in the\n-- WhatsApp design. The template send attaches a per-button payload carrying the\n-- booking id (\\`act:confirm:booking:<uuid>\\`), and the whole confirm/cancel flow\n-- rests on Meta echoing that payload back when the customer taps. Tyxter's\n-- sandbox accepts the components with a 202 and stores them verbatim — but it\n-- validates that array not at all (an index of 7 on a two-button template was\n-- accepted the same way), so the 202 proves nothing about Meta. Only a live\n-- WABA can.\n--\n-- If Meta turns out to echo the button's static TEXT instead, the reply arrives\n-- saying \"Confirmar\" and naming nothing. These columns are what it is then\n-- correlated against: the last outbound message on that conversation, and the\n-- subject recorded on it. Which is exactly the seam legacy beautyplace had to\n-- build after the fact — their keyword matcher confirmed EVERY future\n-- appointment the customer had, and the 48-hour re-enrichment subsystem existed\n-- only to guess which one was meant. Recording the subject at send time costs\n-- two nullable columns and removes the guess.\n--\n-- messaging-send (WP3) already writes them, tolerating 42703 and retrying\n-- without — so a pool that has not run this migration keeps sending, and one\n-- that has gains the anchor. This is the migration that closes that gap.\n--\n-- Nullable, and deliberately not a foreign key: the subject is polymorphic\n-- ('booking', later 'order', 'invoice'), lives in tables this plugin does not\n-- own, and a message about a deleted booking is still a message somebody sent.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nALTER TABLE public.plg_conversation_messages\n ADD COLUMN IF NOT EXISTS subject_type text,\n ADD COLUMN IF NOT EXISTS subject_id text;\n\n-- The correlation read: the most recent outbound message on this thread that\n-- was about something. Partial, because the rows that carry a subject are the\n-- minority and an index over the rest would be paid for on every insert.\nCREATE INDEX IF NOT EXISTS idx_plg_conversation_messages_subject\n ON public.plg_conversation_messages(conversation_id, at DESC)\n WHERE subject_id IS NOT NULL;\n\n-- The other direction, for \"show me everything ever said about this booking\" —\n-- the thread view a booking detail page wants, and the audit answer to \"was the\n-- customer actually told?\".\nCREATE INDEX IF NOT EXISTS idx_plg_conversation_messages_by_subject\n ON public.plg_conversation_messages(tenant_id, subject_type, subject_id)\n WHERE subject_id IS NOT NULL;\n`\n\nexport const MIGRATION_009_PAYMENT_REQUESTS = `-- ============================================================================\n-- plugin-conversations 009: the charge a customer was asked for inside a\n-- conversation, and the row that stops it being asked for twice.\n--\n-- The flow this table exists for: the customer taps \"Pagar\" on a WhatsApp\n-- confirmation, a Pix is opened at the provider, the copy-and-paste code is\n-- sent back into the same thread, and the money — or the expiry — comes back as\n-- a webhook. Four hops, at least one of them delivered more than once.\n--\n-- \\`id\\` IS the \\`external_reference\\` sent to the provider, and that is the whole\n-- reconciliation story: the provider echoes it on every payment event, so a\n-- delivery names the row without a lookup table, and a payment created by this\n-- pool can never be confused with one created by the merchant's own AbacatePay\n-- account (the redundancy the design accepted on purpose). It is also the\n-- Idempotency-Key of the create call, so a retried create replays the provider's\n-- own first answer instead of opening a second charge.\n--\n-- ── The two columns that are not data, but claims ───────────────────────────\n-- \\`link_delivered_at\\` and \\`settled_notified_at\\` are LOCKS, taken with a\n-- conditional UPDATE … WHERE … IS NULL RETURNING. They exist because of a fact\n-- verified against the sandbox on 2026-08-24: ONE Pix becoming available emits\n-- TWO webhook events with two different event ids —\n--\n-- payment.link_generated evt_2448… occurred_at 16:52:49.640Z\n-- payment.approval_available evt_750b… occurred_at 16:52:49.639Z\n--\n-- — carrying identical \\`data\\`. The dedupe ledger (005) cannot help: they are\n-- genuinely two events. Without a claim on the ROW the customer receives the\n-- same Pix code twice, one second apart, which reads as a double charge.\n--\n-- ── Money moves forward only ────────────────────────────────────────────────\n-- \\`status\\` is applied monotonically by the same rule the delivery statuses use,\n-- because webhooks are unordered: a \\`link_generated\\` arriving after a \\`paid\\`\n-- must not walk a settled charge back to \"waiting\". The CHECK carries the\n-- provider's own vocabulary verbatim, including \\`approval_requested\\` and\n-- \\`approved\\`, which the design's list did not have and the OpenAPI does.\n--\n-- Writes are SERVICE-ROLE ONLY. A member who could update \\`status\\` could mark a\n-- charge paid; one who could update \\`amount_cents\\` could change what a customer\n-- is asked for after the fact. Members read — \"was this booking paid, and how\n-- much for\" is a question the inbox and the booking page both have to answer.\n-- Idempotent + safe to re-run.\n-- ============================================================================\n\nCREATE TABLE IF NOT EXISTS public.plg_conversations_payment_requests (\n -- Ours, and the provider's \\`external_reference\\`, and the create's\n -- Idempotency-Key. One identity, so there is nothing to keep in step.\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n conversation_id uuid REFERENCES public.plg_conversations(id) ON DELETE SET NULL,\n -- The outbound message carrying the Pix code, once it has been sent. NULL\n -- until then, and that is exactly how \"the code has not gone out yet\" is read.\n message_id uuid REFERENCES public.plg_conversation_messages(id) ON DELETE SET NULL,\n -- Polymorphic and deliberately not a foreign key, for the same reason 008's\n -- subject columns are not: 'booking' today, 'order' and 'invoice' later, in\n -- tables this plugin does not own.\n subject_type text,\n subject_id text,\n -- Integers, because the provider takes \\`amount_brl_centavos\\` and a float that\n -- rounds to 4499 is a customer charged one centavo less than the salon reads.\n amount_cents integer NOT NULL CHECK (amount_cents > 0),\n currency text NOT NULL DEFAULT 'BRL',\n description text,\n -- Provider-neutral, like every other column in this plugin: the second\n -- payment rail must not need a second table.\n provider text NOT NULL,\n status text NOT NULL DEFAULT 'created'\n CHECK (status IN ('created', 'link_generated', 'approval_requested', 'approved',\n 'paid', 'failed', 'expired', 'cancelled')),\n provider_payment_id text,\n payment_link_url text,\n -- Kept because the customer may lose the message and ask for it again, and\n -- re-reading a code we already have beats opening a second charge.\n pix_copy_paste text,\n -- The claims. See the header.\n link_delivered_at timestamptz,\n settled_notified_at timestamptz,\n settled_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_conversations_payment_requests ENABLE ROW LEVEL SECURITY;\n\n-- The webhook's lookup: an event names the payment, and this finds the row.\n-- UNIQUE where present — two rows claiming one provider payment would make the\n-- settlement a coin toss.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversations_payment_requests_provider\n ON public.plg_conversations_payment_requests(provider, provider_payment_id)\n WHERE provider_payment_id IS NOT NULL;\n\n-- At most ONE live charge per subject. A customer tapping \"Pagar\" twice sends\n-- two \\`message.received\\` events with two different event ids, so the dedupe\n-- ledger lets both through — this is what stops the second one opening a second\n-- Pix for the same appointment. Settled rows are excluded: a booking legitimately\n-- gets a new charge after one expired.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_conversations_payment_requests_open_subject\n ON public.plg_conversations_payment_requests(tenant_id, subject_type, subject_id)\n WHERE subject_id IS NOT NULL\n AND status IN ('created', 'link_generated', 'approval_requested', 'approved');\n\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_payment_requests_tenant\n ON public.plg_conversations_payment_requests(tenant_id, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_payment_requests_subject\n ON public.plg_conversations_payment_requests(tenant_id, subject_type, subject_id)\n WHERE subject_id IS NOT NULL;\n\n-- RLS: members read their own tenant's charges. No INSERT/UPDATE/DELETE policy\n-- and no write GRANT — every write here is made by the webhook with the\n-- service-role key, acting on a signed delivery.\nDROP POLICY IF EXISTS plg_conversations_payment_requests_select\n ON public.plg_conversations_payment_requests;\nCREATE POLICY plg_conversations_payment_requests_select\n ON public.plg_conversations_payment_requests\n FOR SELECT TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT ON public.plg_conversations_payment_requests TO authenticated;\n`\n\nexport const MIGRATIONS: Array<{ id: string; sql: string }> = [\n { id: \"001_conversations\", sql: MIGRATION_001_CONVERSATIONS },\n { id: \"002_contact_person\", sql: MIGRATION_002_CONTACT_PERSON },\n { id: \"003_channels\", sql: MIGRATION_003_CHANNELS },\n { id: \"004_message_delivery\", sql: MIGRATION_004_MESSAGE_DELIVERY },\n { id: \"005_webhook_events\", sql: MIGRATION_005_WEBHOOK_EVENTS },\n { id: \"006_optouts\", sql: MIGRATION_006_OPTOUTS },\n { id: \"007_inbound_routing\", sql: MIGRATION_007_INBOUND_ROUTING },\n { id: \"008_message_subject\", sql: MIGRATION_008_MESSAGE_SUBJECT },\n { id: \"009_payment_requests\", sql: MIGRATION_009_PAYMENT_REQUESTS },\n]\n","import React from 'react'\nimport type { EntityDef, PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { getActiveTenantId, getSupabaseClientOptional, registerTranslations } from '@fayz-ai/core'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport { ConversationsPage } from './views/ConversationsPage'\nimport type { ResolvedConversationsConfig } from './context'\nimport type { ConversationsProvider } from './data/types'\nimport { createMockConversationsProvider } from './data/mock'\nimport { createSupabaseConversationsProvider } from './data/supabase'\nimport { createConversationsStore } from './store'\nimport { conversationsLocales } from './locales'\nimport { tyxterConnectorDef } from './integrations/tyxter/connectorDef'\nimport {\n MIGRATION_001_CONVERSATIONS,\n MIGRATION_002_CONTACT_PERSON,\n MIGRATION_003_CHANNELS,\n MIGRATION_004_MESSAGE_DELIVERY,\n MIGRATION_005_WEBHOOK_EVENTS,\n MIGRATION_006_OPTOUTS,\n MIGRATION_007_INBOUND_ROUTING,\n MIGRATION_008_MESSAGE_SUBJECT,\n MIGRATION_009_PAYMENT_REQUESTS,\n} from './migrations'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — the GoHighLevel \"Conversations\" equivalent:\n// one omni-channel inbox (SMS / WhatsApp / Instagram / Email / Web chat).\n// Universal plugin — reusable by any vertical (beauty, resto, agency…).\n//\n// Supabase-backed (plg_conversations / plg_conversation_messages, tenant-scoped\n// via RLS) when a client is registered; a persisted mock inbox otherwise.\n// WhatsApp arrives through the Tyxter connector declared below; the other\n// channels still deliver inbound rows out-of-band, and this plugin stays the\n// read/compose surface for all of them.\n// ---------------------------------------------------------------------------\n\nexport interface ConversationsPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n dataProvider?: ConversationsProvider\n\n /**\n * `people.kind` for contacts created from the compose modal's picker. Each\n * vertical names its people differently (beauty 'client', agency 'lead'),\n * hence a knob rather than a hardcoded value. Default: 'contact'.\n */\n contactKind?: string\n /** Custom search source for the picker. Defaults to the person archetype lookup. */\n contactLookup?: EntityLookup\n /**\n * The app's contact entity, same definition its registry page uses. \"Create\"\n * in the picker opens THAT form. Without it the picker searches only.\n */\n contactEntityDef?: EntityDef\n}\n\nfunction createSafeProvider(): ConversationsProvider {\n // Real data when a Supabase client is registered; mock inbox otherwise.\n // Resolution is LAZY (per call): createConversationsPlugin runs at module\n // scope in the app config, BEFORE the Supabase client is registered — an\n // eager check here would capture the mock forever even on live backends.\n let real: ConversationsProvider | null = null\n let mock: ConversationsProvider | null = null\n const resolve = (): ConversationsProvider => {\n if (getSupabaseClientOptional()) {\n real ??= createSupabaseConversationsProvider({ tenantId: () => getActiveTenantId() })\n return real\n }\n mock ??= createMockConversationsProvider({ tenantId: () => getActiveTenantId() })\n return mock\n }\n return {\n listConversations: (...a) => resolve().listConversations(...a),\n getMessages: (...a) => resolve().getMessages(...a),\n sendMessage: (...a) => resolve().sendMessage(...a),\n markRead: (...a) => resolve().markRead(...a),\n setStatus: (...a) => resolve().setStatus(...a),\n createConversation: (...a) => resolve().createConversation(...a),\n }\n}\n\nexport function createConversationsPlugin(options?: ConversationsPluginOptions): PluginManifest {\n registerTranslations(conversationsLocales)\n const provider = options?.dataProvider ?? createSafeProvider()\n const store = createConversationsStore(provider)\n\n const config: ResolvedConversationsConfig = {\n contactKind: options?.contactKind ?? 'contact',\n contactLookup: options?.contactLookup,\n contactEntityDef: options?.contactEntityDef,\n }\n\n const PageComponent: React.ComponentType<unknown> = () =>\n React.createElement(ConversationsPage, { store, config })\n PageComponent.displayName = 'ConversationsPage'\n\n return {\n id: 'conversations',\n defaultAgentRole: 'frontdesk',\n name: options?.navLabel ?? 'Conversations',\n icon: 'MessageCircle',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'conversations', label: 'Conversations', group: 'Engage' }],\n // Recurring monthly quota — counts conversation threads created this month.\n declaredLimits: [\n { key: 'conversations_month', label: 'Conversations this month', table: 'plg_conversations', period: 'month' },\n ],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 1,\n label: options?.navLabel ?? 'Conversations',\n route: '/conversations',\n icon: 'MessageCircle',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/conversations',\n component: PageComponent,\n fullBleed: true,\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n widgets: [],\n events: [\n { name: 'conversations.message.received', description: 'An inbound message arrived on any channel' },\n { name: 'conversations.message.sent', description: 'An outbound message was sent' },\n {\n // Emitted server-side by plg_emit_event, from the tyxter-webhook's\n // payment handler — the browser never sees this write, which is the\n // whole reason packages/db/migrations/027 exists.\n name: 'conversations.payment_received',\n title: 'Pagamento recebido na conversa',\n description: 'O cliente pagou uma cobrança enviada dentro da conversa',\n trigger: 'Quando o provedor confirma o pagamento de uma cobrança aberta no chat',\n icon: 'BadgeDollarSign',\n fields: [\n { name: 'amount', label: 'Valor', type: 'money', example: 'R$ 120,00' },\n { name: 'amount_cents', label: 'Valor em centavos', type: 'number', example: '12000' },\n { name: 'currency', label: 'Moeda', type: 'string', example: 'BRL' },\n { name: 'description', label: 'Descrição da cobrança', type: 'string', example: 'Corte + Escova' },\n { name: 'channel', label: 'Canal', type: 'string', example: 'whatsapp' },\n { name: 'provider', label: 'Provedor', type: 'string', example: 'tyxter' },\n // The subject is the BOOKING, not the charge: an automation reacting\n // to this needs the thing that was paid for, and the charge id is the\n // audit trail beside it.\n { name: 'subject_type', label: 'Tipo do registro', type: 'string', example: 'booking' },\n { name: 'subject_id', label: 'Registro', type: 'id', example: '9f1c…' },\n { name: 'payment_request_id', label: 'Cobrança', type: 'id', example: '3ab2…' },\n ],\n },\n ],\n aiTools: [\n {\n id: 'conversations.list-threads',\n name: 'listConversations',\n description: 'Lists open conversations across all channels, optionally filtered by channel.',\n icon: 'MessageCircle',\n mode: 'read' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n channel: {\n type: 'string' as const,\n enum: ['all', 'sms', 'whatsapp', 'instagram', 'email', 'webchat'],\n },\n },\n },\n suggestions: [\n { label: 'Show unread conversations' },\n { label: 'Any new WhatsApp messages?' },\n ],\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n id: 'conversations.send-message',\n name: 'sendMessage',\n description: 'Sends a reply in a conversation thread.',\n icon: 'Send',\n mode: 'persist' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n conversationId: { type: 'string' as const, description: 'Conversation id' },\n body: { type: 'string' as const, description: 'Message body' },\n },\n required: ['conversationId', 'body'],\n },\n permission: { feature: 'conversations', action: 'create' as const },\n },\n ],\n migrations: [\n {\n id: 'conversations-001-base-tables',\n version: '1.0.0',\n sql: MIGRATION_001_CONVERSATIONS,\n description: 'Create plg_conversations and plg_conversation_messages (tenant-scoped RLS)',\n },\n {\n id: 'conversations-002-contact-person',\n version: '1.1.0',\n sql: MIGRATION_002_CONTACT_PERSON,\n description: 'Link threads to public.people via contact_person_id (nullable, ON DELETE SET NULL)',\n },\n {\n id: 'conversations-003-channels',\n version: '1.2.0',\n sql: MIGRATION_003_CHANNELS,\n description:\n 'Create plg_conversations_channels — which number a message leaves by (product fallback vs tenant dedicated); member read, service-role writes',\n },\n {\n id: 'conversations-004-message-delivery',\n version: '1.2.0',\n sql: MIGRATION_004_MESSAGE_DELIVERY,\n description:\n 'Message provider_message_id + delivery_status + sender attribution (sender_kind / sender_label)',\n },\n {\n id: 'conversations-005-webhook-events',\n version: '1.3.0',\n sql: MIGRATION_005_WEBHOOK_EVENTS,\n description:\n 'Create plg_conversations_webhook_events — the dedupe ledger that makes an at-least-once webhook safe to apply',\n },\n {\n id: 'conversations-006-optouts',\n version: '1.3.0',\n sql: MIGRATION_006_OPTOUTS,\n description:\n 'Create plg_conversations_optouts — consent suppression read by messaging-send before every send',\n },\n {\n id: 'conversations-007-inbound-routing',\n version: '1.3.0',\n sql: MIGRATION_007_INBOUND_ROUTING,\n description:\n 'Widen delivery_status to the statuses Tyxter emits + plg_conversations_match_by_phone (BR digit-suffix thread matching, service-role only)',\n },\n {\n id: 'conversations-008-message-subject',\n version: '1.3.0',\n sql: MIGRATION_008_MESSAGE_SUBJECT,\n description:\n 'Message subject_type / subject_id — the correlation anchor a button reply falls back to when the provider echoes the button text instead of our payload',\n },\n {\n id: 'conversations-009-payment-requests',\n version: '1.4.0',\n sql: MIGRATION_009_PAYMENT_REQUESTS,\n description:\n 'Create plg_conversations_payment_requests — the charge opened inside a conversation, with the row-level claims that make one Pix arrive once and a settlement notify once',\n },\n ],\n functions: [\n {\n slug: 'tyxter-webhook',\n // PUBLIC, and the only function in this plugin that is. The caller is\n // Tyxter's infrastructure, which has no Supabase session and never\n // will — so the gateway check is off and the HMAC signature over\n // `${timestamp}.${rawBody}` IS the authentication.\n ingress: 'none',\n verifyJwt: false,\n // NAMES only. TYXTER_WEBHOOK_SECRET is the endpoint signing secret,\n // returned exactly ONCE by POST /v1/webhook-endpoints and stored as a\n // pool secret by the provisioning script — it is per-endpoint, which is\n // why it can live in the pool while the API key cannot.\n secrets: [\n 'TYXTER_WEBHOOK_SECRET',\n 'TYXTER_API_BASE_URL',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n {\n slug: 'tyxter-template-sync',\n // Two callers, neither of them the outside world: an operator with a\n // Supabase JWT, and an ops script with TYXTER_SYNC_SECRET. The second\n // cannot present a JWT, so the gateway check has to be off and the\n // function authenticates itself.\n ingress: 'none',\n verifyJwt: false,\n // NAMES only. The Tyxter key is NOT here and could not be: a Supabase\n // secret is per-POOL and the key belongs to the platform's project — it\n // is redeemed per run from the vault, which is what the last three are for.\n secrets: [\n 'TYXTER_API_BASE_URL',\n 'TYXTER_SYNC_SECRET',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n {\n slug: 'tyxter-payment-preflight',\n // verify_jwt TRUE — the only function in this wave with a human caller\n // and nothing else. An operator looking at the connector panel has a\n // Supabase session; there is no clock and no ops script here, so the\n // gateway's own check is left on and membership is checked on top of it.\n ingress: 'none',\n verifyJwt: true,\n // NAMES only. The browser cannot ask Tyxter anything — the key is the\n // platform's — which is the whole reason this function exists.\n secrets: [\n 'TYXTER_API_BASE_URL',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n ],\n // WhatsApp through Tyxter. Declared here because this plugin IS the inbox\n // the messages land in — the connector configures inside Conversas.\n connectors: [tyxterConnectorDef],\n locales: conversationsLocales,\n }\n}\n\nexport type { ConversationsProvider } from './data/types'\nexport type { Conversation, Message, Channel, CreateConversationInput } from './types'\nexport { createMockConversationsProvider, type MockConversationsConfig } from './data/mock'\nexport {\n createSupabaseConversationsProvider,\n type SupabaseConversationsConfig,\n} from './data/supabase'\n// The automations template editor gates its `pay` action on this, so it has to\n// leave the plugin — see PaymentReadiness for why `ready` alone is not the test.\nexport {\n readPaymentReadiness,\n openPaymentSetupSession,\n canOfferPayment,\n PAYMENT_PREFLIGHT_FUNCTION,\n type PaymentReadiness,\n type PaymentActiveMode,\n type PaymentSetupSession,\n} from './data/payment-preflight'\nexport {\n listMessagingChannels,\n type MessagingChannelAccount,\n type MessagingChannelKind,\n type MessagingChannelStatus,\n type ListMessagingChannelsOptions,\n} from './data/channels'\n// Exported so a bespoke admin can render the connector without mounting the\n// whole plugin — the same reason plugin-shop exports its definitions.\nexport {\n tyxterConnectorDef,\n TYXTER_CONNECTOR_ID,\n TYXTER_LATENCY_BUDGET_MS,\n} from './integrations/tyxter/connectorDef'\n"]}
|