@burdenoff/microfe-botmanor 2026.927.1 → 2026.927.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceRequired.js","names":[],"sources":["../../../src/botlit/components/WorkspaceRequired.tsx"],"sourcesContent":["/**\n * Workspace Required Gate\n * @module botlit/components\n */\n\nimport type { FC } from 'react';\nimport { Bot, MessagesSquare, Sparkles } from 'lucide-react';\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { joinPath } from '../utils/navigation';\n\n/**\n * Shown when BotManor is mounted without an active workspace.\n *\n * Every BotManor query is workspace-scoped and every write is rejected without\n * `x-workspace-id`, so the module genuinely cannot run — but a single muted\n * sentence in a tall empty region reads as a broken page. This uses the shared\n * guidance pattern (purpose → illustrated zero state → next steps) so the user\n * is told what BotManor is for and what to do next, and the steps land on real\n * routes the moment a workspace becomes active.\n */\nexport interface WorkspaceRequiredProps {\n /** Absolute path BotManor is mounted at in the host shell, e.g. `/
|
|
1
|
+
{"version":3,"file":"WorkspaceRequired.js","names":[],"sources":["../../../src/botlit/components/WorkspaceRequired.tsx"],"sourcesContent":["/**\n * Workspace Required Gate\n * @module botlit/components\n */\n\nimport type { FC } from 'react';\nimport { Bot, MessagesSquare, Sparkles } from 'lucide-react';\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { joinPath } from '../utils/navigation';\n\n/**\n * Shown when BotManor is mounted without an active workspace.\n *\n * Every BotManor query is workspace-scoped and every write is rejected without\n * `x-workspace-id`, so the module genuinely cannot run — but a single muted\n * sentence in a tall empty region reads as a broken page. This uses the shared\n * guidance pattern (purpose → illustrated zero state → next steps) so the user\n * is told what BotManor is for and what to do next, and the steps land on real\n * routes the moment a workspace becomes active.\n */\nexport interface WorkspaceRequiredProps {\n /** Absolute path BotManor is mounted at in the host shell, e.g. `/botmanor`. */\n basePath: string;\n /** Host shell navigation. Absent in standalone/dev mounts. */\n navigate?: (path: string) => void;\n}\n\nexport const WorkspaceRequired: FC<WorkspaceRequiredProps> = ({ basePath, navigate }) => {\n const t = useTr();\n const go = (relativePath: string) => {\n navigate?.(joinPath(basePath, relativePath));\n };\n\n return (\n <div className=\"mx-auto flex h-full min-h-[60vh] w-full max-w-2xl flex-col justify-center gap-6 px-4 py-10\">\n {/* The 'story': plain-language purpose of this module for a first-time user. */}\n <PagePurpose icon={<Bot className=\"size-4\" aria-hidden=\"true\" />}>\n {t(\n 'workspaceRequired.purpose',\n 'BotManor is where you build and run AI agents — you give an agent a prompt, a model, the knowledge it may read and the tools it may call, then publish it to a channel such as web chat, Slack or an API so your team and customers can talk to it.'\n )}\n </PagePurpose>\n\n {/* The single emphasis zone for this screen: the get-started callout. */}\n <EmphasisPanel className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={t('workspaceRequired.emptyTitle', 'Pick a workspace to start building')}\n description={t(\n 'workspaceRequired.emptyDescription',\n 'Agents, knowledge bases and channels live inside a workspace so they stay scoped to the right team. Select or create an active workspace from the shell to load your BotManor workspace.'\n )}\n />\n </EmphasisPanel>\n\n {/* Dismissable, reopenable next-step guidance wired to real routes. */}\n <NextSteps\n storageKey=\"botlit-get-started\"\n title={t('workspaceRequired.nextStepsTitle', 'Once your workspace is active')}\n steps={[\n {\n id: 'open-agents',\n label: t('workspaceRequired.openAgents', 'Open your agents'),\n description: t(\n 'workspaceRequired.openAgentsDescription',\n 'Browse, search and manage every agent in this workspace.'\n ),\n icon: <Bot className=\"size-4\" aria-hidden=\"true\" />,\n onClick: () => go('agents'),\n },\n {\n id: 'build-agent',\n label: t('workspaceRequired.buildAgent', 'Build an agent'),\n description: t(\n 'workspaceRequired.buildAgentDescription',\n 'Pick a model, write the prompt and wire up the tools it may call.'\n ),\n icon: <Sparkles className=\"size-4\" aria-hidden=\"true\" />,\n onClick: () => go('agents/new'),\n },\n {\n id: 'connect-channel',\n label: t('workspaceRequired.connectChannel', 'Connect a channel'),\n description: t(\n 'workspaceRequired.connectChannelDescription',\n 'Publish an agent to web chat, Slack or an API so people can reach it.'\n ),\n icon: <MessagesSquare className=\"size-4\" aria-hidden=\"true\" />,\n onClick: () => go('channels'),\n },\n ]}\n />\n </div>\n );\n};\n"],"mappings":";;;;;;AAiCA,IAAa,KAAiD,EAAE,aAAU,kBAAe;CACvF,IAAM,IAAI,GAAO,EACX,KAAM,MAAyB;AACnC,MAAW,EAAS,GAAU,EAAa,CAAC;;AAG9C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD;KAAK,WAAU;KAAS,eAAY;KAAS,CAAA;cAC7D,EACC,6BACA,sPACD;IACW,CAAA;GAGd,kBAAC,GAAD;IAAe,WAAU;cACvB,kBAAC,GAAD;KACE,cAAa;KACb,OAAO,EAAE,gCAAgC,qCAAqC;KAC9E,aAAa,EACX,sCACA,2LACD;KACD,CAAA;IACY,CAAA;GAGhB,kBAAC,GAAD;IACE,YAAW;IACX,OAAO,EAAE,oCAAoC,gCAAgC;IAC7E,OAAO;KACL;MACE,IAAI;MACJ,OAAO,EAAE,gCAAgC,mBAAmB;MAC5D,aAAa,EACX,2CACA,2DACD;MACD,MAAM,kBAAC,GAAD;OAAK,WAAU;OAAS,eAAY;OAAS,CAAA;MACnD,eAAe,EAAG,SAAS;MAC5B;KACD;MACE,IAAI;MACJ,OAAO,EAAE,gCAAgC,iBAAiB;MAC1D,aAAa,EACX,2CACA,oEACD;MACD,MAAM,kBAAC,GAAD;OAAU,WAAU;OAAS,eAAY;OAAS,CAAA;MACxD,eAAe,EAAG,aAAa;MAChC;KACD;MACE,IAAI;MACJ,OAAO,EAAE,oCAAoC,oBAAoB;MACjE,aAAa,EACX,+CACA,wEACD;MACD,MAAM,kBAAC,GAAD;OAAgB,WAAU;OAAS,eAAY;OAAS,CAAA;MAC9D,eAAe,EAAG,WAAW;MAC9B;KACF;IACD,CAAA;GACE"}
|
|
@@ -324,7 +324,7 @@ var q = ({ routeId: e, connectorId: t, channelId: n, platform: r, onRemoved: i,
|
|
|
324
324
|
let { basePath: m } = e(), [h] = g(), [_, v] = T(!1), y = c.platform, b = K(y), x = G(c.adapterType), S = _, C = w((e) => {
|
|
325
325
|
let t = U[y];
|
|
326
326
|
if (!t) return;
|
|
327
|
-
let n = window.location.origin + window.location.pathname + window.location.search, r = `${m ? m.replace(/\/botlit(\/.*)?$/, "/integrations") : "/integrations"}/connections/add?${new URLSearchParams({
|
|
327
|
+
let n = window.location.origin + window.location.pathname + window.location.search, r = `${m ? m.replace(/\/(botmanor|botlit)(\/.*)?$/, "/integrations") : "/integrations"}/connections/add?${new URLSearchParams({
|
|
328
328
|
providerKey: t,
|
|
329
329
|
returnTo: n,
|
|
330
330
|
channelId: e
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BotChannelsTab.js","names":[],"sources":["../../../src/botlit/pages/BotChannelsTab.tsx"],"sourcesContent":["/**\n * BotChannelsTab\n * @module botlit/pages\n *\n * Shown inside BotDetailPage → \"Channels\" tab.\n *\n * Flow:\n * 1. Fetches all ACTIVE COMMUNICATION channels from global-channel-svc via useGlobalChannels()\n * 2. Filters to show only channelType = COMMUNICATION (bi-directional platforms)\n * 3. OAuth platforms (SLACK, TEAMS, DISCORD):\n * - No bot route → \"Set up channel\" button\n * → creates ChannelRoute + ChannelRoute, then opens integrations in new tab\n * - Route exists → PlatformRouteCard (manual link flow + connected state + toggle / disconnect)\n * - Manual link → User pastes integrationConnectionId → clicks \"Link Connection\"\n * → calls linkIntegrationConnection mutation → toast success\n * 4. WhatsApp: pairing code flow (coming soon / handled by wspace-integrations CUSTOM auth)\n * 5. NOTIFICATION channels (EMAIL/SMS): hidden (not shown for bots)\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useState, useCallback } from 'react';\nimport {\n Slack,\n Mail,\n MessageSquare,\n Radio,\n CheckCircle2,\n Bell,\n BellOff,\n Loader2,\n Trash2,\n Unlink,\n MessageCircle,\n Users,\n Plus,\n} from 'lucide-react';\nimport { toast } from 'sonner';\nimport { Card, CardHeader, CardTitle, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { Input } from '../components/Input';\nimport { ChannelSelectorSheet } from '../components/ChannelSelectorSheet';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n isNative,\n nativeCopyText,\n nativeNotify,\n nativeOpenUrl,\n nativeToast,\n} from '../../utils/nativeBridge';\nimport {\n useBotChannelRoutes,\n useChannelConnector,\n useCreateChannelRoute,\n useDeleteChannelRoute,\n} from '../hooks';\nimport {\n useMyUserPlatformConnection,\n useLinkIntegrationConnection,\n useToggleSlackNotifications,\n useRevokeSlackConnection,\n useGlobalChannels,\n} from '../hooks/useUserPlatformConnection';\nimport type { GlobalChannelData } from '../hooks/useUserPlatformConnection';\nimport type { ChannelRouteRecord } from '../hooks/useChannels';\n// `listChannelRoutes` is published — routes carry the real record shape.\ntype ChannelRoute = ChannelRouteRecord;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platform → provider key mapping (must match keys seeded in wspace-integrations-svc)\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PLATFORM_PROVIDER_KEY: Record<string, string> = {\n SLACK: 'slack-user',\n TEAMS: 'teams-user',\n DISCORD: 'discord-user',\n WHATSAPP: 'whatsapp-user',\n};\n\n/** Platforms that use the OAuth flow via microfe-integrations */\nconst OAUTH_PLATFORMS = new Set(['SLACK', 'TEAMS', 'DISCORD']);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Human-readable label for an AdapterType enum value */\nfunction getAdapterLabel(adapterType: string | null | undefined): string | null {\n if (!adapterType) return null;\n const labels: Record<string, string> = {\n SLACK_BOT: 'Slack Bot',\n SLACK_WEBHOOK: 'Slack Webhook',\n AWS_SES: 'AWS SES',\n AZURE_ACS: 'Azure ACS',\n SENDGRID: 'SendGrid',\n MAILGUN: 'Mailgun',\n MAILJET: 'Mailjet',\n POSTMARK: 'Postmark',\n RESEND: 'Resend',\n SMTP: 'SMTP',\n TWILIO_SMS: 'Twilio SMS',\n AWS_SNS: 'AWS SNS',\n SINCH: 'Sinch',\n VONAGE: 'Vonage',\n PLIVO: 'Plivo',\n DISCORD_BOT: 'Discord Bot',\n DISCORD_WEBHOOK: 'Discord Webhook',\n TEAMS_BOT: 'Teams Bot',\n TEAMS_WEBHOOK: 'Teams Webhook',\n FCM: 'Firebase Cloud Messaging',\n APNS: 'Apple Push (APNs)',\n WEBHOOK_HTTP: 'HTTP Webhook',\n };\n return (\n labels[adapterType] ??\n adapterType\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase())\n );\n}\n\n/** Platform display config — icon element, badge colours, accent colour */\ninterface PlatformMeta {\n label: string;\n badgeClass: string;\n icon: ReactNode;\n accentBg: string;\n accentText: string;\n}\n\nfunction getPlatformMeta(platform: string): PlatformMeta {\n switch (platform) {\n case 'SLACK':\n return {\n label: 'Slack',\n badgeClass: 'bg-pink-100 text-pink-700',\n icon: <Slack size={18} />,\n accentBg: 'bg-[#611f69]/10',\n accentText: 'text-[#611f69]',\n };\n case 'TEAMS':\n return {\n label: 'Teams',\n badgeClass: 'bg-indigo-100 text-indigo-700',\n icon: <Users size={18} />,\n accentBg: 'bg-indigo-500/10',\n accentText: 'text-indigo-600',\n };\n case 'DISCORD':\n return {\n label: 'Discord',\n badgeClass: 'bg-violet-100 text-violet-700',\n icon: <MessageCircle size={18} />,\n accentBg: 'bg-violet-500/10',\n accentText: 'text-violet-600',\n };\n case 'WHATSAPP':\n return {\n label: 'WhatsApp',\n badgeClass: 'bg-green-100 text-green-700',\n icon: <MessageSquare size={18} />,\n accentBg: 'bg-green-500/10',\n accentText: 'text-green-700',\n };\n case 'EMAIL':\n return {\n label: 'Email',\n badgeClass: 'bg-blue-100 text-blue-700',\n icon: <Mail size={18} />,\n accentBg: 'bg-blue-500/10',\n accentText: 'text-blue-600',\n };\n case 'SMS':\n return {\n label: 'SMS',\n badgeClass: 'bg-green-100 text-green-700',\n icon: <MessageSquare size={18} />,\n accentBg: 'bg-green-500/10',\n accentText: 'text-green-600',\n };\n default:\n return {\n label: platform,\n badgeClass: 'bg-gray-100 text-gray-700',\n icon: <Radio size={18} />,\n accentBg: 'bg-gray-500/10',\n accentText: 'text-gray-600',\n };\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PlatformRouteCard — one connected OAuth platform route + personal connection UI\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface PlatformRouteCardProps {\n routeId: string;\n connectorId: string;\n /** The global channel ID (credentialsRefId) stored on the connector */\n channelId: string;\n platform: string;\n onRemoved: () => void;\n /** Navigate to microfe-integrations to reconnect */\n onReconnect: () => void;\n}\n\nconst PlatformRouteCard: FC<PlatformRouteCardProps> = ({\n routeId,\n connectorId,\n channelId,\n platform,\n onRemoved,\n onReconnect,\n}) => {\n const { data: connector, loading: connectorLoading } = useChannelConnector(connectorId);\n // The channel ID for the UPC lookup is stored on connector.credentialsRefId\n const upcChannelId = connector?.credentialsRefId ?? channelId;\n\n const {\n data: platformConnection,\n loading: connectionLoading,\n refetch: refetchConnection,\n } = useMyUserPlatformConnection(upcChannelId);\n\n const { toggle: toggleNotifications, loading: togglingNotifications } =\n useToggleSlackNotifications();\n const { revoke: revokeConnection, loading: revoking } = useRevokeSlackConnection();\n const { link: linkIntegration } = useLinkIntegrationConnection();\n const [deleteRoute] = useDeleteChannelRoute();\n\n // Manual linking state\n const [integrationConnId, setIntegrationConnId] = useState('');\n const [linking, setLinking] = useState(false);\n\n const meta = getPlatformMeta(platform);\n\n const handleLinkConnection = async () => {\n if (!integrationConnId.trim()) {\n toast.error('Please enter an integration connection ID');\n return;\n }\n setLinking(true);\n try {\n const conn = await linkIntegration(integrationConnId.trim(), upcChannelId);\n if (conn) {\n toast.success(\n `${conn.externalDisplayName ?? conn.externalUsername ?? 'Account'} connected! The bot can now send you direct messages.`\n );\n await refetchConnection();\n setIntegrationConnId('');\n } else {\n toast.error('Failed to link connection. Please verify the ID and try again.');\n }\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to link connection');\n } finally {\n setLinking(false);\n }\n };\n\n const handleRemove = async () => {\n if (!confirm(`Remove this ${meta.label} channel from the bot?`)) return;\n try {\n await deleteRoute(routeId);\n onRemoved();\n toast.success(`${meta.label} channel removed`);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to remove');\n if (isNative()) void nativeNotify('error');\n }\n };\n\n if (connectorLoading) {\n return <div className=\"h-32 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />;\n }\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4 space-y-3\">\n {/* Route header */}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary font-medium uppercase tracking-wide\">\n Connected workspace\n </span>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleRemove}\n className=\"h-7 w-7 text-text-secondary hover:text-status-error-text\"\n title=\"Remove channel\"\n >\n <Trash2 size={13} />\n </Button>\n </div>\n\n {connectionLoading ? (\n <div className=\"flex items-center gap-2 py-2 text-text-secondary\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span className=\"text-xs\">Checking {meta.label} connection…</span>\n </div>\n ) : platformConnection?.dmAllowed ? (\n /* ── Personal account connected ── */\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3 p-3 rounded-lg bg-bg-surface border border-border-subtle\">\n <CheckCircle2 size={16} className=\"text-green-600 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-sm\">\n {platformConnection.externalDisplayName ||\n platformConnection.externalUsername ||\n platformConnection.externalUserId}\n </p>\n {platformConnection.externalUsername && (\n <p className=\"text-xs text-text-secondary\">\n @{platformConnection.externalUsername}\n </p>\n )}\n {platformConnection.connectedAt && (\n <p className=\"text-xs text-text-secondary\">\n Connected {new Date(platformConnection.connectedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n {platformConnection.isTokenExpired && (\n <Badge variant=\"outline\" className=\"text-xs text-amber-600 border-amber-400\">\n Token expired\n </Badge>\n )}\n </div>\n\n {/* Integration Connection ID (read-only) */}\n {platformConnection.integrationConnectionId && (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium text-text-secondary\">\n Integration Connection ID\n </label>\n <Input\n value={platformConnection.integrationConnectionId}\n readOnly\n className=\"text-xs font-mono bg-bg-sunken/50 cursor-default\"\n onClick={(e) => {\n e.currentTarget.select();\n void nativeCopyText(platformConnection.integrationConnectionId!);\n toast.success('Connection ID copied to clipboard');\n if (isNative()) void nativeToast('Copied');\n }}\n title=\"Click to copy\"\n />\n </div>\n )}\n\n {/* Notification toggle */}\n <div className=\"flex items-center justify-between p-3 border border-border-subtle rounded-lg bg-bg-surface\">\n <div className=\"flex items-center gap-2\">\n {platformConnection.notificationsEnabled ? (\n <Bell size={14} className=\"text-primary\" />\n ) : (\n <BellOff size={14} className=\"text-text-secondary\" />\n )}\n <div>\n <p className=\"text-sm font-medium\">DM Notifications</p>\n <p className=\"text-xs text-text-secondary\">\n {platformConnection.notificationsEnabled\n ? `Bot will DM you on ${meta.label}`\n : 'DM notifications paused'}\n </p>\n </div>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n isLoading={togglingNotifications}\n onClick={async () => {\n const updated = await toggleNotifications(\n platformConnection.id,\n !platformConnection.notificationsEnabled\n );\n if (updated) await refetchConnection();\n }}\n >\n {platformConnection.notificationsEnabled ? 'Disable' : 'Enable'}\n </Button>\n </div>\n\n <div className=\"flex gap-2 justify-end pt-1\">\n {platformConnection.isTokenExpired && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={meta.icon as React.ReactElement}\n onClick={onReconnect}\n >\n Reconnect\n </Button>\n )}\n <Button\n variant=\"destructive\"\n size=\"sm\"\n leftIcon={<Unlink size={13} />}\n isLoading={revoking}\n onClick={async () => {\n const ok = await revokeConnection(platformConnection.id);\n if (ok) await refetchConnection();\n }}\n >\n Disconnect\n </Button>\n </div>\n </div>\n ) : (\n /* ── Personal account not connected - Manual linking flow ── */\n <div className=\"space-y-3 py-2\">\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-bg-surface border border-border-subtle\">\n <div className=\"p-2 rounded-full bg-bg-sunken shrink-0\">\n <span className=\"text-text-secondary\">{meta.icon}</span>\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-sm\">No personal account connected</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Connect your {meta.label} account to receive bot DMs\n </p>\n </div>\n </div>\n\n <div className=\"p-3 rounded-lg bg-bg-sunken/50 space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary\">Connect your account:</p>\n <ol className=\"list-decimal list-inside space-y-1 text-xs text-text-secondary\">\n <li>Click \"Open Integrations\" below (opens in new tab)</li>\n <li>Connect your {meta.label} account via OAuth</li>\n <li>Copy the Connection ID from the integrations page</li>\n <li>Paste it below and click \"Link Connection\"</li>\n </ol>\n </div>\n\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={meta.icon as React.ReactElement}\n onClick={onReconnect}\n className=\"w-full\"\n >\n Open Integrations →\n </Button>\n\n <div className=\"flex gap-2\">\n <Input\n placeholder=\"Paste integration connection ID...\"\n value={integrationConnId}\n onChange={(e) => setIntegrationConnId(e.target.value)}\n className=\"flex-1 text-sm\"\n />\n <Button\n size=\"sm\"\n onClick={handleLinkConnection}\n isLoading={linking}\n disabled={!integrationConnId.trim() || linking}\n >\n Link Connection\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuthGlobalChannelCard\n// Generic card for OAuth platforms (Slack, Teams, Discord).\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface OAuthGlobalChannelCardProps {\n channel: GlobalChannelData;\n routes: ChannelRoute[];\n botId: string;\n botName: string;\n onRoutesChanged: () => void;\n}\n\nconst OAuthGlobalChannelCard: FC<OAuthGlobalChannelCardProps> = ({\n channel,\n routes,\n botId,\n botName,\n onRoutesChanged,\n}) => {\n const { basePath } = useBotlit();\n const [createRoute] = useCreateChannelRoute();\n const [connecting, setConnecting] = useState(false);\n\n const platform = channel.platform as string;\n const meta = getPlatformMeta(platform);\n const adapterLabel = getAdapterLabel(channel.adapterType as string | null);\n const isCreating = connecting;\n\n /**\n * Navigate to microfe-integrations wizard pre-configured for this platform.\n * The integrations wizard will OAuth the user, then redirect back here with\n * ?integrationConnectionId=<id>&channelId=<id> appended to the returnTo URL.\n *\n * returnTo MUST be an absolute URL so the backend's new URL(returnTo) doesn't throw.\n */\n const navigateToIntegrations = useCallback(\n (currentChannelId: string) => {\n const providerKey = PLATFORM_PROVIDER_KEY[platform];\n if (!providerKey) return;\n\n // Absolute URL so the backend oauth-callback can do new URL(returnTo) without error.\n const returnTo = window.location.origin + window.location.pathname + window.location.search;\n\n // Integrations MFE base path — shell mounts it at /integrations by convention.\n // Derive it by replacing the /botlit segment of basePath.\n const integrationsBase = basePath\n ? basePath.replace(/\\/botlit(\\/.*)?$/, '/integrations')\n : '/integrations';\n\n const params = new URLSearchParams({\n providerKey,\n returnTo,\n channelId: currentChannelId,\n });\n\n const targetPath = `${integrationsBase}/connections/add?${params.toString()}`;\n\n // Always open in new tab for manual copy-paste workflow\n if (isNative() && /^https?:\\/\\//.test(targetPath)) {\n void nativeOpenUrl(targetPath);\n } else {\n window.open(targetPath, '_blank');\n }\n },\n [platform, basePath]\n );\n\n const handleConnect = useCallback(async () => {\n setConnecting(true);\n try {\n // Route the global channel at this bot. `workspaceId` is supplied by the\n // hook from the BotManor context — never pass an empty string here.\n await createRoute({\n connectorId: channel.id, // Global channel id doubles as the connector reference\n appId: botId,\n locationId: '*',\n metadata: { locationName: 'All channels & DMs', appName: botName },\n });\n\n await onRoutesChanged();\n\n // Navigate to microfe-integrations for OAuth\n navigateToIntegrations(channel.id);\n // page will navigate away — don't setConnecting(false)\n } catch (err) {\n toast.error(err instanceof Error ? err.message : `Failed to connect ${meta.label}`);\n setConnecting(false);\n }\n }, [\n botId,\n botName,\n channel.id,\n createRoute,\n meta.label,\n navigateToIntegrations,\n onRoutesChanged,\n ]);\n\n const handleReconnect = useCallback(() => {\n navigateToIntegrations(channel.id);\n }, [channel.id, navigateToIntegrations]);\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between pb-3\">\n <div className=\"flex items-center gap-2.5\">\n <div className={`p-1.5 rounded-md ${meta.accentBg}`}>\n <span className={meta.accentText}>{meta.icon}</span>\n </div>\n <div>\n <CardTitle className=\"text-base\">{channel.name}</CardTitle>\n {adapterLabel && <p className=\"text-xs text-text-secondary mt-0.5\">{adapterLabel}</p>}\n </div>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <span className={`px-2 py-0.5 text-xs font-semibold rounded-full ${meta.badgeClass}`}>\n {meta.label}\n </span>\n {routes.length > 0 && (\n <Badge\n variant=\"outline\"\n className=\"text-xs text-green-600 border-green-400 bg-green-50\"\n >\n Active\n </Badge>\n )}\n </div>\n </CardHeader>\n\n <CardContent>\n {routes.length > 0 ? (\n <div className=\"space-y-3\">\n {routes.map((route: ChannelRoute) => (\n <PlatformRouteCard\n key={route.id}\n routeId={route.id}\n connectorId={route.connectorId}\n channelId={channel.id}\n platform={platform}\n onRemoved={onRoutesChanged}\n onReconnect={handleReconnect}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center gap-4 py-8 text-center\">\n <div className=\"p-4 rounded-full bg-bg-sunken\">\n <span className=\"text-text-secondary\">{meta.icon}</span>\n </div>\n <div>\n <p className=\"font-medium text-sm\">Not connected to this bot</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n Enable {meta.label} for this bot. Users will connect their personal accounts\n separately.\n </p>\n </div>\n <Button\n leftIcon={meta.icon as React.ReactElement}\n onClick={handleConnect}\n isLoading={isCreating}\n >\n Set up {meta.label} channel\n </Button>\n </div>\n )}\n </CardContent>\n </Card>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ComingSoonChannelCard\n// EMAIL and SMS channels shown in the UI — connect functionality coming later\n// NOTE: This component is currently unused - we now only show OAuth platforms\n// ─────────────────────────────────────────────────────────────────────────────\n\n// Commented out - not needed for OAuth-only flow\n// interface ComingSoonChannelCardProps {\n// channel: GlobalChannelData;\n// }\n//\n// const ComingSoonChannelCard: FC<ComingSoonChannelCardProps> = ({ channel }) => {\n// const meta = getPlatformMeta(channel.platform as string);\n// const adapterLabel = getAdapterLabel(channel.adapterType as string | null);\n//\n// return (\n// <Card className=\"opacity-80\">\n// <CardHeader className=\"flex flex-row items-center justify-between pb-3\">\n// <div className=\"flex items-center gap-2.5\">\n// <div className={`p-1.5 rounded-md ${meta.accentBg}`}>\n// <span className={meta.accentText}>{meta.icon}</span>\n// </div>\n// <div>\n// <CardTitle className=\"text-base\">{channel.name}</CardTitle>\n// {adapterLabel && <p className=\"text-xs text-text-secondary mt-0.5\">{adapterLabel}</p>}\n// </div>\n// </div>\n// <div className=\"flex items-center gap-1.5\">\n// <span className={`px-2 py-0.5 text-xs font-semibold rounded-full ${meta.badgeClass}`}>\n// {meta.label}\n// </span>\n// <Badge variant=\"outline\" className=\"text-xs text-text-secondary\">\n// <Clock size={10} className=\"mr-1\" />\n// Coming soon\n// </Badge>\n// </div>\n// </CardHeader>\n// <CardContent>\n// <p className=\"text-sm text-text-secondary text-center py-4\">\n// Bot integration for <strong>{meta.label}</strong> channels is coming soon. This channel is\n// available for notification delivery — two-way bot communication will be enabled in a\n// future update.\n// </p>\n// </CardContent>\n// </Card>\n// );\n// };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ConfiguredChannelCard - Wrapper that fetches connector and matches channel\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface ConfiguredChannelCardProps {\n route: ChannelRoute;\n oauthChannels: GlobalChannelData[];\n botId: string;\n botName: string;\n onRoutesChanged: () => void;\n}\n\nconst ConfiguredChannelCard: FC<ConfiguredChannelCardProps> = ({\n route,\n oauthChannels,\n botId,\n botName,\n onRoutesChanged,\n}) => {\n // Fetch the connector to get credentialsRefId\n const { data: connector } = useChannelConnector(route.connectorId);\n\n // Find the matching channel by credentialsRefId\n const channel = oauthChannels.find((ch) => ch.id === connector?.credentialsRefId);\n\n // If connector is still loading or channel not found, don't render\n if (!connector || !channel) return null;\n\n // Only show OAuth channels\n if (!OAUTH_PLATFORMS.has(channel.platform as string)) return null;\n\n return (\n <OAuthGlobalChannelCard\n channel={channel}\n routes={[route]}\n botId={botId}\n botName={botName}\n onRoutesChanged={onRoutesChanged}\n />\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// BotChannelsTab - Main component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BotChannelsTabProps {\n botId: string;\n botName: string;\n}\n\nexport const BotChannelsTab: FC<BotChannelsTabProps> = ({ botId, botName }) => {\n const {\n data: globalChannels,\n loading: channelsLoading,\n error: channelsError,\n } = useGlobalChannels();\n\n const {\n data: routes,\n loading: routesLoading,\n refetch: refetchRoutes,\n } = useBotChannelRoutes(botId);\n\n // State for channel selector sheet\n const [showChannelSelector, setShowChannelSelector] = useState(false);\n const [selectedChannelForSetup, setSelectedChannelForSetup] = useState<string | null>(null);\n\n const handleRoutesChanged = useCallback(async () => {\n await refetchRoutes();\n }, [refetchRoutes]);\n\n // Filter to show only OAuth platforms (Slack, Teams, Discord)\n const oauthChannels = globalChannels.filter((ch) => OAUTH_PLATFORMS.has(ch.platform as string));\n\n // Calculate which platforms are already configured for this bot\n const configuredPlatforms = new Set(\n routes\n .map((_route: ChannelRoute) => {\n // Find the channel for this route to get its platform\n const channel = oauthChannels.find((_ch) => {\n // Match by checking if any route uses this channel\n return routes.some((r: ChannelRoute) => r.connectorId);\n });\n return channel?.platform as string;\n })\n .filter(Boolean)\n );\n\n // Get available channels (OAuth platforms not yet configured)\n const availableChannels = oauthChannels.filter(\n (ch) => !configuredPlatforms.has(ch.platform as string)\n );\n\n // Platform icons mapping for ChannelSelectorSheet\n const platformIcons: Record<string, ReactNode> = {\n SLACK: <Slack size={18} />,\n TEAMS: <Users size={18} />,\n DISCORD: <MessageCircle size={18} />,\n };\n\n // Platform metadata mapping for ChannelSelectorSheet\n const platformMetaMap: Record<string, PlatformMeta> = {};\n oauthChannels.forEach((ch) => {\n const platform = ch.platform as string;\n platformMetaMap[platform] = getPlatformMeta(platform);\n });\n\n const handleSelectChannel = useCallback((channelId: string, _platform: string) => {\n setSelectedChannelForSetup(channelId);\n }, []);\n\n const isLoading = channelsLoading || routesLoading;\n\n if (isLoading) {\n return (\n <div className=\"space-y-4\">\n <div className=\"h-10 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg w-52\" />\n <div className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n <div className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </div>\n );\n }\n\n // Empty state: no channels configured yet\n const hasConfiguredChannels = routes.length > 0;\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div>\n <h3 className=\"font-semibold text-base\">Channel Connections</h3>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Communication platforms available for this bot\n </p>\n </div>\n\n {/* Channel error banner */}\n {channelsError && (\n <div className=\"flex items-start gap-2 rounded-lg border border-status-error-border/40 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\">\n <span>Failed to load channels: {channelsError.message}</span>\n </div>\n )}\n\n {/* Empty state: no OAuth channels available from backend */}\n {!channelsError && oauthChannels.length === 0 && (\n <Card>\n <CardContent className=\"py-12\">\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No channels available\"\n description=\"No active communication channels have been configured yet. Contact your administrator to set up channels.\"\n />\n </CardContent>\n </Card>\n )}\n\n {/* Empty state: channels available but none configured yet */}\n {!channelsError &&\n oauthChannels.length > 0 &&\n !hasConfiguredChannels &&\n !selectedChannelForSetup && (\n <Card>\n <CardContent className=\"py-12\">\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No channels connected\"\n description=\"Connect a communication channel to enable bot conversations on platforms like Slack, Teams, or Discord.\"\n action={\n <Button\n leftIcon={<Plus size={16} />}\n onClick={() => setShowChannelSelector(true)}\n >\n Connect Channel\n </Button>\n }\n />\n </CardContent>\n </Card>\n )}\n\n {/* Configured channels + channel in setup */}\n {oauthChannels.length > 0 && (hasConfiguredChannels || selectedChannelForSetup) && (\n <div className=\"space-y-4\">\n {/* Show configured channels */}\n {routes.map((route: ChannelRoute) => {\n return (\n <ConfiguredChannelCard\n key={route.id}\n route={route}\n oauthChannels={oauthChannels}\n botId={botId}\n botName={botName}\n onRoutesChanged={handleRoutesChanged}\n />\n );\n })}\n\n {/* Show channel currently being set up */}\n {selectedChannelForSetup && (\n <>\n {oauthChannels\n .filter((ch) => ch.id === selectedChannelForSetup)\n .map((channel) => (\n <OAuthGlobalChannelCard\n key={channel.id}\n channel={channel}\n routes={routes.filter((_r: ChannelRoute) => {\n // Only show routes for this specific channel\n return false; // Initially no routes exist\n })}\n botId={botId}\n botName={botName}\n onRoutesChanged={async () => {\n await handleRoutesChanged();\n setSelectedChannelForSetup(null);\n }}\n />\n ))}\n </>\n )}\n </div>\n )}\n\n {/* Channel Selector Sheet */}\n <ChannelSelectorSheet\n open={showChannelSelector}\n onClose={() => setShowChannelSelector(false)}\n onSelectChannel={handleSelectChannel}\n availableChannels={availableChannels}\n platformIcons={platformIcons}\n platformMeta={platformMetaMap}\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BotChannelsTab.js","names":[],"sources":["../../../src/botlit/pages/BotChannelsTab.tsx"],"sourcesContent":["/**\n * BotChannelsTab\n * @module botlit/pages\n *\n * Shown inside BotDetailPage → \"Channels\" tab.\n *\n * Flow:\n * 1. Fetches all ACTIVE COMMUNICATION channels from global-channel-svc via useGlobalChannels()\n * 2. Filters to show only channelType = COMMUNICATION (bi-directional platforms)\n * 3. OAuth platforms (SLACK, TEAMS, DISCORD):\n * - No bot route → \"Set up channel\" button\n * → creates ChannelRoute + ChannelRoute, then opens integrations in new tab\n * - Route exists → PlatformRouteCard (manual link flow + connected state + toggle / disconnect)\n * - Manual link → User pastes integrationConnectionId → clicks \"Link Connection\"\n * → calls linkIntegrationConnection mutation → toast success\n * 4. WhatsApp: pairing code flow (coming soon / handled by wspace-integrations CUSTOM auth)\n * 5. NOTIFICATION channels (EMAIL/SMS): hidden (not shown for bots)\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useState, useCallback } from 'react';\nimport {\n Slack,\n Mail,\n MessageSquare,\n Radio,\n CheckCircle2,\n Bell,\n BellOff,\n Loader2,\n Trash2,\n Unlink,\n MessageCircle,\n Users,\n Plus,\n} from 'lucide-react';\nimport { toast } from 'sonner';\nimport { Card, CardHeader, CardTitle, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { Input } from '../components/Input';\nimport { ChannelSelectorSheet } from '../components/ChannelSelectorSheet';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n isNative,\n nativeCopyText,\n nativeNotify,\n nativeOpenUrl,\n nativeToast,\n} from '../../utils/nativeBridge';\nimport {\n useBotChannelRoutes,\n useChannelConnector,\n useCreateChannelRoute,\n useDeleteChannelRoute,\n} from '../hooks';\nimport {\n useMyUserPlatformConnection,\n useLinkIntegrationConnection,\n useToggleSlackNotifications,\n useRevokeSlackConnection,\n useGlobalChannels,\n} from '../hooks/useUserPlatformConnection';\nimport type { GlobalChannelData } from '../hooks/useUserPlatformConnection';\nimport type { ChannelRouteRecord } from '../hooks/useChannels';\n// `listChannelRoutes` is published — routes carry the real record shape.\ntype ChannelRoute = ChannelRouteRecord;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Platform → provider key mapping (must match keys seeded in wspace-integrations-svc)\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PLATFORM_PROVIDER_KEY: Record<string, string> = {\n SLACK: 'slack-user',\n TEAMS: 'teams-user',\n DISCORD: 'discord-user',\n WHATSAPP: 'whatsapp-user',\n};\n\n/** Platforms that use the OAuth flow via microfe-integrations */\nconst OAUTH_PLATFORMS = new Set(['SLACK', 'TEAMS', 'DISCORD']);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Human-readable label for an AdapterType enum value */\nfunction getAdapterLabel(adapterType: string | null | undefined): string | null {\n if (!adapterType) return null;\n const labels: Record<string, string> = {\n SLACK_BOT: 'Slack Bot',\n SLACK_WEBHOOK: 'Slack Webhook',\n AWS_SES: 'AWS SES',\n AZURE_ACS: 'Azure ACS',\n SENDGRID: 'SendGrid',\n MAILGUN: 'Mailgun',\n MAILJET: 'Mailjet',\n POSTMARK: 'Postmark',\n RESEND: 'Resend',\n SMTP: 'SMTP',\n TWILIO_SMS: 'Twilio SMS',\n AWS_SNS: 'AWS SNS',\n SINCH: 'Sinch',\n VONAGE: 'Vonage',\n PLIVO: 'Plivo',\n DISCORD_BOT: 'Discord Bot',\n DISCORD_WEBHOOK: 'Discord Webhook',\n TEAMS_BOT: 'Teams Bot',\n TEAMS_WEBHOOK: 'Teams Webhook',\n FCM: 'Firebase Cloud Messaging',\n APNS: 'Apple Push (APNs)',\n WEBHOOK_HTTP: 'HTTP Webhook',\n };\n return (\n labels[adapterType] ??\n adapterType\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase())\n );\n}\n\n/** Platform display config — icon element, badge colours, accent colour */\ninterface PlatformMeta {\n label: string;\n badgeClass: string;\n icon: ReactNode;\n accentBg: string;\n accentText: string;\n}\n\nfunction getPlatformMeta(platform: string): PlatformMeta {\n switch (platform) {\n case 'SLACK':\n return {\n label: 'Slack',\n badgeClass: 'bg-pink-100 text-pink-700',\n icon: <Slack size={18} />,\n accentBg: 'bg-[#611f69]/10',\n accentText: 'text-[#611f69]',\n };\n case 'TEAMS':\n return {\n label: 'Teams',\n badgeClass: 'bg-indigo-100 text-indigo-700',\n icon: <Users size={18} />,\n accentBg: 'bg-indigo-500/10',\n accentText: 'text-indigo-600',\n };\n case 'DISCORD':\n return {\n label: 'Discord',\n badgeClass: 'bg-violet-100 text-violet-700',\n icon: <MessageCircle size={18} />,\n accentBg: 'bg-violet-500/10',\n accentText: 'text-violet-600',\n };\n case 'WHATSAPP':\n return {\n label: 'WhatsApp',\n badgeClass: 'bg-green-100 text-green-700',\n icon: <MessageSquare size={18} />,\n accentBg: 'bg-green-500/10',\n accentText: 'text-green-700',\n };\n case 'EMAIL':\n return {\n label: 'Email',\n badgeClass: 'bg-blue-100 text-blue-700',\n icon: <Mail size={18} />,\n accentBg: 'bg-blue-500/10',\n accentText: 'text-blue-600',\n };\n case 'SMS':\n return {\n label: 'SMS',\n badgeClass: 'bg-green-100 text-green-700',\n icon: <MessageSquare size={18} />,\n accentBg: 'bg-green-500/10',\n accentText: 'text-green-600',\n };\n default:\n return {\n label: platform,\n badgeClass: 'bg-gray-100 text-gray-700',\n icon: <Radio size={18} />,\n accentBg: 'bg-gray-500/10',\n accentText: 'text-gray-600',\n };\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PlatformRouteCard — one connected OAuth platform route + personal connection UI\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface PlatformRouteCardProps {\n routeId: string;\n connectorId: string;\n /** The global channel ID (credentialsRefId) stored on the connector */\n channelId: string;\n platform: string;\n onRemoved: () => void;\n /** Navigate to microfe-integrations to reconnect */\n onReconnect: () => void;\n}\n\nconst PlatformRouteCard: FC<PlatformRouteCardProps> = ({\n routeId,\n connectorId,\n channelId,\n platform,\n onRemoved,\n onReconnect,\n}) => {\n const { data: connector, loading: connectorLoading } = useChannelConnector(connectorId);\n // The channel ID for the UPC lookup is stored on connector.credentialsRefId\n const upcChannelId = connector?.credentialsRefId ?? channelId;\n\n const {\n data: platformConnection,\n loading: connectionLoading,\n refetch: refetchConnection,\n } = useMyUserPlatformConnection(upcChannelId);\n\n const { toggle: toggleNotifications, loading: togglingNotifications } =\n useToggleSlackNotifications();\n const { revoke: revokeConnection, loading: revoking } = useRevokeSlackConnection();\n const { link: linkIntegration } = useLinkIntegrationConnection();\n const [deleteRoute] = useDeleteChannelRoute();\n\n // Manual linking state\n const [integrationConnId, setIntegrationConnId] = useState('');\n const [linking, setLinking] = useState(false);\n\n const meta = getPlatformMeta(platform);\n\n const handleLinkConnection = async () => {\n if (!integrationConnId.trim()) {\n toast.error('Please enter an integration connection ID');\n return;\n }\n setLinking(true);\n try {\n const conn = await linkIntegration(integrationConnId.trim(), upcChannelId);\n if (conn) {\n toast.success(\n `${conn.externalDisplayName ?? conn.externalUsername ?? 'Account'} connected! The bot can now send you direct messages.`\n );\n await refetchConnection();\n setIntegrationConnId('');\n } else {\n toast.error('Failed to link connection. Please verify the ID and try again.');\n }\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to link connection');\n } finally {\n setLinking(false);\n }\n };\n\n const handleRemove = async () => {\n if (!confirm(`Remove this ${meta.label} channel from the bot?`)) return;\n try {\n await deleteRoute(routeId);\n onRemoved();\n toast.success(`${meta.label} channel removed`);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to remove');\n if (isNative()) void nativeNotify('error');\n }\n };\n\n if (connectorLoading) {\n return <div className=\"h-32 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />;\n }\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4 space-y-3\">\n {/* Route header */}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary font-medium uppercase tracking-wide\">\n Connected workspace\n </span>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleRemove}\n className=\"h-7 w-7 text-text-secondary hover:text-status-error-text\"\n title=\"Remove channel\"\n >\n <Trash2 size={13} />\n </Button>\n </div>\n\n {connectionLoading ? (\n <div className=\"flex items-center gap-2 py-2 text-text-secondary\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span className=\"text-xs\">Checking {meta.label} connection…</span>\n </div>\n ) : platformConnection?.dmAllowed ? (\n /* ── Personal account connected ── */\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3 p-3 rounded-lg bg-bg-surface border border-border-subtle\">\n <CheckCircle2 size={16} className=\"text-green-600 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-sm\">\n {platformConnection.externalDisplayName ||\n platformConnection.externalUsername ||\n platformConnection.externalUserId}\n </p>\n {platformConnection.externalUsername && (\n <p className=\"text-xs text-text-secondary\">\n @{platformConnection.externalUsername}\n </p>\n )}\n {platformConnection.connectedAt && (\n <p className=\"text-xs text-text-secondary\">\n Connected {new Date(platformConnection.connectedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n {platformConnection.isTokenExpired && (\n <Badge variant=\"outline\" className=\"text-xs text-amber-600 border-amber-400\">\n Token expired\n </Badge>\n )}\n </div>\n\n {/* Integration Connection ID (read-only) */}\n {platformConnection.integrationConnectionId && (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium text-text-secondary\">\n Integration Connection ID\n </label>\n <Input\n value={platformConnection.integrationConnectionId}\n readOnly\n className=\"text-xs font-mono bg-bg-sunken/50 cursor-default\"\n onClick={(e) => {\n e.currentTarget.select();\n void nativeCopyText(platformConnection.integrationConnectionId!);\n toast.success('Connection ID copied to clipboard');\n if (isNative()) void nativeToast('Copied');\n }}\n title=\"Click to copy\"\n />\n </div>\n )}\n\n {/* Notification toggle */}\n <div className=\"flex items-center justify-between p-3 border border-border-subtle rounded-lg bg-bg-surface\">\n <div className=\"flex items-center gap-2\">\n {platformConnection.notificationsEnabled ? (\n <Bell size={14} className=\"text-primary\" />\n ) : (\n <BellOff size={14} className=\"text-text-secondary\" />\n )}\n <div>\n <p className=\"text-sm font-medium\">DM Notifications</p>\n <p className=\"text-xs text-text-secondary\">\n {platformConnection.notificationsEnabled\n ? `Bot will DM you on ${meta.label}`\n : 'DM notifications paused'}\n </p>\n </div>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n isLoading={togglingNotifications}\n onClick={async () => {\n const updated = await toggleNotifications(\n platformConnection.id,\n !platformConnection.notificationsEnabled\n );\n if (updated) await refetchConnection();\n }}\n >\n {platformConnection.notificationsEnabled ? 'Disable' : 'Enable'}\n </Button>\n </div>\n\n <div className=\"flex gap-2 justify-end pt-1\">\n {platformConnection.isTokenExpired && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={meta.icon as React.ReactElement}\n onClick={onReconnect}\n >\n Reconnect\n </Button>\n )}\n <Button\n variant=\"destructive\"\n size=\"sm\"\n leftIcon={<Unlink size={13} />}\n isLoading={revoking}\n onClick={async () => {\n const ok = await revokeConnection(platformConnection.id);\n if (ok) await refetchConnection();\n }}\n >\n Disconnect\n </Button>\n </div>\n </div>\n ) : (\n /* ── Personal account not connected - Manual linking flow ── */\n <div className=\"space-y-3 py-2\">\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-bg-surface border border-border-subtle\">\n <div className=\"p-2 rounded-full bg-bg-sunken shrink-0\">\n <span className=\"text-text-secondary\">{meta.icon}</span>\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-sm\">No personal account connected</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Connect your {meta.label} account to receive bot DMs\n </p>\n </div>\n </div>\n\n <div className=\"p-3 rounded-lg bg-bg-sunken/50 space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary\">Connect your account:</p>\n <ol className=\"list-decimal list-inside space-y-1 text-xs text-text-secondary\">\n <li>Click \"Open Integrations\" below (opens in new tab)</li>\n <li>Connect your {meta.label} account via OAuth</li>\n <li>Copy the Connection ID from the integrations page</li>\n <li>Paste it below and click \"Link Connection\"</li>\n </ol>\n </div>\n\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={meta.icon as React.ReactElement}\n onClick={onReconnect}\n className=\"w-full\"\n >\n Open Integrations →\n </Button>\n\n <div className=\"flex gap-2\">\n <Input\n placeholder=\"Paste integration connection ID...\"\n value={integrationConnId}\n onChange={(e) => setIntegrationConnId(e.target.value)}\n className=\"flex-1 text-sm\"\n />\n <Button\n size=\"sm\"\n onClick={handleLinkConnection}\n isLoading={linking}\n disabled={!integrationConnId.trim() || linking}\n >\n Link Connection\n </Button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuthGlobalChannelCard\n// Generic card for OAuth platforms (Slack, Teams, Discord).\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface OAuthGlobalChannelCardProps {\n channel: GlobalChannelData;\n routes: ChannelRoute[];\n botId: string;\n botName: string;\n onRoutesChanged: () => void;\n}\n\nconst OAuthGlobalChannelCard: FC<OAuthGlobalChannelCardProps> = ({\n channel,\n routes,\n botId,\n botName,\n onRoutesChanged,\n}) => {\n const { basePath } = useBotlit();\n const [createRoute] = useCreateChannelRoute();\n const [connecting, setConnecting] = useState(false);\n\n const platform = channel.platform as string;\n const meta = getPlatformMeta(platform);\n const adapterLabel = getAdapterLabel(channel.adapterType as string | null);\n const isCreating = connecting;\n\n /**\n * Navigate to microfe-integrations wizard pre-configured for this platform.\n * The integrations wizard will OAuth the user, then redirect back here with\n * ?integrationConnectionId=<id>&channelId=<id> appended to the returnTo URL.\n *\n * returnTo MUST be an absolute URL so the backend's new URL(returnTo) doesn't throw.\n */\n const navigateToIntegrations = useCallback(\n (currentChannelId: string) => {\n const providerKey = PLATFORM_PROVIDER_KEY[platform];\n if (!providerKey) return;\n\n // Absolute URL so the backend oauth-callback can do new URL(returnTo) without error.\n const returnTo = window.location.origin + window.location.pathname + window.location.search;\n\n // Integrations MFE base path — shell mounts it at /integrations by convention.\n // Derive it by replacing the product segment of basePath (/botmanor, or\n // /botlit on shells that still mount the pre-rename route).\n const integrationsBase = basePath\n ? basePath.replace(/\\/(botmanor|botlit)(\\/.*)?$/, '/integrations')\n : '/integrations';\n\n const params = new URLSearchParams({\n providerKey,\n returnTo,\n channelId: currentChannelId,\n });\n\n const targetPath = `${integrationsBase}/connections/add?${params.toString()}`;\n\n // Always open in new tab for manual copy-paste workflow\n if (isNative() && /^https?:\\/\\//.test(targetPath)) {\n void nativeOpenUrl(targetPath);\n } else {\n window.open(targetPath, '_blank');\n }\n },\n [platform, basePath]\n );\n\n const handleConnect = useCallback(async () => {\n setConnecting(true);\n try {\n // Route the global channel at this bot. `workspaceId` is supplied by the\n // hook from the BotManor context — never pass an empty string here.\n await createRoute({\n connectorId: channel.id, // Global channel id doubles as the connector reference\n appId: botId,\n locationId: '*',\n metadata: { locationName: 'All channels & DMs', appName: botName },\n });\n\n await onRoutesChanged();\n\n // Navigate to microfe-integrations for OAuth\n navigateToIntegrations(channel.id);\n // page will navigate away — don't setConnecting(false)\n } catch (err) {\n toast.error(err instanceof Error ? err.message : `Failed to connect ${meta.label}`);\n setConnecting(false);\n }\n }, [\n botId,\n botName,\n channel.id,\n createRoute,\n meta.label,\n navigateToIntegrations,\n onRoutesChanged,\n ]);\n\n const handleReconnect = useCallback(() => {\n navigateToIntegrations(channel.id);\n }, [channel.id, navigateToIntegrations]);\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between pb-3\">\n <div className=\"flex items-center gap-2.5\">\n <div className={`p-1.5 rounded-md ${meta.accentBg}`}>\n <span className={meta.accentText}>{meta.icon}</span>\n </div>\n <div>\n <CardTitle className=\"text-base\">{channel.name}</CardTitle>\n {adapterLabel && <p className=\"text-xs text-text-secondary mt-0.5\">{adapterLabel}</p>}\n </div>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <span className={`px-2 py-0.5 text-xs font-semibold rounded-full ${meta.badgeClass}`}>\n {meta.label}\n </span>\n {routes.length > 0 && (\n <Badge\n variant=\"outline\"\n className=\"text-xs text-green-600 border-green-400 bg-green-50\"\n >\n Active\n </Badge>\n )}\n </div>\n </CardHeader>\n\n <CardContent>\n {routes.length > 0 ? (\n <div className=\"space-y-3\">\n {routes.map((route: ChannelRoute) => (\n <PlatformRouteCard\n key={route.id}\n routeId={route.id}\n connectorId={route.connectorId}\n channelId={channel.id}\n platform={platform}\n onRemoved={onRoutesChanged}\n onReconnect={handleReconnect}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center gap-4 py-8 text-center\">\n <div className=\"p-4 rounded-full bg-bg-sunken\">\n <span className=\"text-text-secondary\">{meta.icon}</span>\n </div>\n <div>\n <p className=\"font-medium text-sm\">Not connected to this bot</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n Enable {meta.label} for this bot. Users will connect their personal accounts\n separately.\n </p>\n </div>\n <Button\n leftIcon={meta.icon as React.ReactElement}\n onClick={handleConnect}\n isLoading={isCreating}\n >\n Set up {meta.label} channel\n </Button>\n </div>\n )}\n </CardContent>\n </Card>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ComingSoonChannelCard\n// EMAIL and SMS channels shown in the UI — connect functionality coming later\n// NOTE: This component is currently unused - we now only show OAuth platforms\n// ─────────────────────────────────────────────────────────────────────────────\n\n// Commented out - not needed for OAuth-only flow\n// interface ComingSoonChannelCardProps {\n// channel: GlobalChannelData;\n// }\n//\n// const ComingSoonChannelCard: FC<ComingSoonChannelCardProps> = ({ channel }) => {\n// const meta = getPlatformMeta(channel.platform as string);\n// const adapterLabel = getAdapterLabel(channel.adapterType as string | null);\n//\n// return (\n// <Card className=\"opacity-80\">\n// <CardHeader className=\"flex flex-row items-center justify-between pb-3\">\n// <div className=\"flex items-center gap-2.5\">\n// <div className={`p-1.5 rounded-md ${meta.accentBg}`}>\n// <span className={meta.accentText}>{meta.icon}</span>\n// </div>\n// <div>\n// <CardTitle className=\"text-base\">{channel.name}</CardTitle>\n// {adapterLabel && <p className=\"text-xs text-text-secondary mt-0.5\">{adapterLabel}</p>}\n// </div>\n// </div>\n// <div className=\"flex items-center gap-1.5\">\n// <span className={`px-2 py-0.5 text-xs font-semibold rounded-full ${meta.badgeClass}`}>\n// {meta.label}\n// </span>\n// <Badge variant=\"outline\" className=\"text-xs text-text-secondary\">\n// <Clock size={10} className=\"mr-1\" />\n// Coming soon\n// </Badge>\n// </div>\n// </CardHeader>\n// <CardContent>\n// <p className=\"text-sm text-text-secondary text-center py-4\">\n// Bot integration for <strong>{meta.label}</strong> channels is coming soon. This channel is\n// available for notification delivery — two-way bot communication will be enabled in a\n// future update.\n// </p>\n// </CardContent>\n// </Card>\n// );\n// };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ConfiguredChannelCard - Wrapper that fetches connector and matches channel\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface ConfiguredChannelCardProps {\n route: ChannelRoute;\n oauthChannels: GlobalChannelData[];\n botId: string;\n botName: string;\n onRoutesChanged: () => void;\n}\n\nconst ConfiguredChannelCard: FC<ConfiguredChannelCardProps> = ({\n route,\n oauthChannels,\n botId,\n botName,\n onRoutesChanged,\n}) => {\n // Fetch the connector to get credentialsRefId\n const { data: connector } = useChannelConnector(route.connectorId);\n\n // Find the matching channel by credentialsRefId\n const channel = oauthChannels.find((ch) => ch.id === connector?.credentialsRefId);\n\n // If connector is still loading or channel not found, don't render\n if (!connector || !channel) return null;\n\n // Only show OAuth channels\n if (!OAUTH_PLATFORMS.has(channel.platform as string)) return null;\n\n return (\n <OAuthGlobalChannelCard\n channel={channel}\n routes={[route]}\n botId={botId}\n botName={botName}\n onRoutesChanged={onRoutesChanged}\n />\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// BotChannelsTab - Main component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BotChannelsTabProps {\n botId: string;\n botName: string;\n}\n\nexport const BotChannelsTab: FC<BotChannelsTabProps> = ({ botId, botName }) => {\n const {\n data: globalChannels,\n loading: channelsLoading,\n error: channelsError,\n } = useGlobalChannels();\n\n const {\n data: routes,\n loading: routesLoading,\n refetch: refetchRoutes,\n } = useBotChannelRoutes(botId);\n\n // State for channel selector sheet\n const [showChannelSelector, setShowChannelSelector] = useState(false);\n const [selectedChannelForSetup, setSelectedChannelForSetup] = useState<string | null>(null);\n\n const handleRoutesChanged = useCallback(async () => {\n await refetchRoutes();\n }, [refetchRoutes]);\n\n // Filter to show only OAuth platforms (Slack, Teams, Discord)\n const oauthChannels = globalChannels.filter((ch) => OAUTH_PLATFORMS.has(ch.platform as string));\n\n // Calculate which platforms are already configured for this bot\n const configuredPlatforms = new Set(\n routes\n .map((_route: ChannelRoute) => {\n // Find the channel for this route to get its platform\n const channel = oauthChannels.find((_ch) => {\n // Match by checking if any route uses this channel\n return routes.some((r: ChannelRoute) => r.connectorId);\n });\n return channel?.platform as string;\n })\n .filter(Boolean)\n );\n\n // Get available channels (OAuth platforms not yet configured)\n const availableChannels = oauthChannels.filter(\n (ch) => !configuredPlatforms.has(ch.platform as string)\n );\n\n // Platform icons mapping for ChannelSelectorSheet\n const platformIcons: Record<string, ReactNode> = {\n SLACK: <Slack size={18} />,\n TEAMS: <Users size={18} />,\n DISCORD: <MessageCircle size={18} />,\n };\n\n // Platform metadata mapping for ChannelSelectorSheet\n const platformMetaMap: Record<string, PlatformMeta> = {};\n oauthChannels.forEach((ch) => {\n const platform = ch.platform as string;\n platformMetaMap[platform] = getPlatformMeta(platform);\n });\n\n const handleSelectChannel = useCallback((channelId: string, _platform: string) => {\n setSelectedChannelForSetup(channelId);\n }, []);\n\n const isLoading = channelsLoading || routesLoading;\n\n if (isLoading) {\n return (\n <div className=\"space-y-4\">\n <div className=\"h-10 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg w-52\" />\n <div className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n <div className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </div>\n );\n }\n\n // Empty state: no channels configured yet\n const hasConfiguredChannels = routes.length > 0;\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div>\n <h3 className=\"font-semibold text-base\">Channel Connections</h3>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Communication platforms available for this bot\n </p>\n </div>\n\n {/* Channel error banner */}\n {channelsError && (\n <div className=\"flex items-start gap-2 rounded-lg border border-status-error-border/40 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\">\n <span>Failed to load channels: {channelsError.message}</span>\n </div>\n )}\n\n {/* Empty state: no OAuth channels available from backend */}\n {!channelsError && oauthChannels.length === 0 && (\n <Card>\n <CardContent className=\"py-12\">\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No channels available\"\n description=\"No active communication channels have been configured yet. Contact your administrator to set up channels.\"\n />\n </CardContent>\n </Card>\n )}\n\n {/* Empty state: channels available but none configured yet */}\n {!channelsError &&\n oauthChannels.length > 0 &&\n !hasConfiguredChannels &&\n !selectedChannelForSetup && (\n <Card>\n <CardContent className=\"py-12\">\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No channels connected\"\n description=\"Connect a communication channel to enable bot conversations on platforms like Slack, Teams, or Discord.\"\n action={\n <Button\n leftIcon={<Plus size={16} />}\n onClick={() => setShowChannelSelector(true)}\n >\n Connect Channel\n </Button>\n }\n />\n </CardContent>\n </Card>\n )}\n\n {/* Configured channels + channel in setup */}\n {oauthChannels.length > 0 && (hasConfiguredChannels || selectedChannelForSetup) && (\n <div className=\"space-y-4\">\n {/* Show configured channels */}\n {routes.map((route: ChannelRoute) => {\n return (\n <ConfiguredChannelCard\n key={route.id}\n route={route}\n oauthChannels={oauthChannels}\n botId={botId}\n botName={botName}\n onRoutesChanged={handleRoutesChanged}\n />\n );\n })}\n\n {/* Show channel currently being set up */}\n {selectedChannelForSetup && (\n <>\n {oauthChannels\n .filter((ch) => ch.id === selectedChannelForSetup)\n .map((channel) => (\n <OAuthGlobalChannelCard\n key={channel.id}\n channel={channel}\n routes={routes.filter((_r: ChannelRoute) => {\n // Only show routes for this specific channel\n return false; // Initially no routes exist\n })}\n botId={botId}\n botName={botName}\n onRoutesChanged={async () => {\n await handleRoutesChanged();\n setSelectedChannelForSetup(null);\n }}\n />\n ))}\n </>\n )}\n </div>\n )}\n\n {/* Channel Selector Sheet */}\n <ChannelSelectorSheet\n open={showChannelSelector}\n onClose={() => setShowChannelSelector(false)}\n onSelectChannel={handleSelectChannel}\n availableChannels={availableChannels}\n platformIcons={platformIcons}\n platformMeta={platformMetaMap}\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED