@burdenoff/microfe-botmanor 2026.819.1 → 2026.925.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.
Files changed (72) hide show
  1. package/dist/botlit/BotlitRoutes.js.map +1 -1
  2. package/dist/botlit/BotmanorRoot.js.map +1 -1
  3. package/dist/botlit/BotmanorRootInner.js +4 -4
  4. package/dist/botlit/BotmanorRootInner.js.map +1 -1
  5. package/dist/botlit/components/ErrorState.js +33 -29
  6. package/dist/botlit/components/ErrorState.js.map +1 -1
  7. package/dist/botlit/components/RouteLoader.js +52 -48
  8. package/dist/botlit/components/RouteLoader.js.map +1 -1
  9. package/dist/botlit/components/VersionHistoryPanel.js +74 -77
  10. package/dist/botlit/components/VersionHistoryPanel.js.map +1 -1
  11. package/dist/botlit/components/WorkspaceRequired.js +31 -30
  12. package/dist/botlit/components/WorkspaceRequired.js.map +1 -1
  13. package/dist/botlit/hooks/internal/workspaceGateway.js.map +1 -1
  14. package/dist/botlit/hooks/useConversations.js.map +1 -1
  15. package/dist/botlit/hooks/useLLMProviders.js.map +1 -1
  16. package/dist/botlit/hooks/useMCPServers.js +60 -49
  17. package/dist/botlit/hooks/useMCPServers.js.map +1 -1
  18. package/dist/botlit/pages/A2AAgentDetailPage.js +1 -1
  19. package/dist/botlit/pages/A2AAgentDetailPage.js.map +1 -1
  20. package/dist/botlit/pages/A2ARegistryPage.js +116 -124
  21. package/dist/botlit/pages/A2ARegistryPage.js.map +1 -1
  22. package/dist/botlit/pages/AgentDetailPage.js +169 -172
  23. package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
  24. package/dist/botlit/pages/AgentsListPage.js +96 -90
  25. package/dist/botlit/pages/AgentsListPage.js.map +1 -1
  26. package/dist/botlit/pages/BotChannelsTab.js.map +1 -1
  27. package/dist/botlit/pages/BotDetailPage.js +228 -231
  28. package/dist/botlit/pages/BotDetailPage.js.map +1 -1
  29. package/dist/botlit/pages/BotWorkflowTab.js +84 -87
  30. package/dist/botlit/pages/BotWorkflowTab.js.map +1 -1
  31. package/dist/botlit/pages/BotsListPage.js +144 -147
  32. package/dist/botlit/pages/BotsListPage.js.map +1 -1
  33. package/dist/botlit/pages/BucketsPage.js +3 -3
  34. package/dist/botlit/pages/BucketsPage.js.map +1 -1
  35. package/dist/botlit/pages/ChannelDetailPage.js +1 -1
  36. package/dist/botlit/pages/ChannelDetailPage.js.map +1 -1
  37. package/dist/botlit/pages/ChannelsPage.js +156 -149
  38. package/dist/botlit/pages/ChannelsPage.js.map +1 -1
  39. package/dist/botlit/pages/ChatPage.js +126 -129
  40. package/dist/botlit/pages/ChatPage.js.map +1 -1
  41. package/dist/botlit/pages/DashboardPage.js +111 -114
  42. package/dist/botlit/pages/DashboardPage.js.map +1 -1
  43. package/dist/botlit/pages/ExecutionsPage.js +163 -159
  44. package/dist/botlit/pages/ExecutionsPage.js.map +1 -1
  45. package/dist/botlit/pages/InstalledPackagesPage.js +3 -3
  46. package/dist/botlit/pages/InstalledPackagesPage.js.map +1 -1
  47. package/dist/botlit/pages/KnowledgeBasesPage.js +84 -78
  48. package/dist/botlit/pages/KnowledgeBasesPage.js.map +1 -1
  49. package/dist/botlit/pages/MCPServerDetailPage.js +295 -238
  50. package/dist/botlit/pages/MCPServerDetailPage.js.map +1 -1
  51. package/dist/botlit/pages/MCPServersPage.js +159 -154
  52. package/dist/botlit/pages/MCPServersPage.js.map +1 -1
  53. package/dist/botlit/pages/PoliciesPage.js +119 -114
  54. package/dist/botlit/pages/PoliciesPage.js.map +1 -1
  55. package/dist/botlit/pages/RetrievalProfilesPage.js +102 -109
  56. package/dist/botlit/pages/RetrievalProfilesPage.js.map +1 -1
  57. package/dist/botlit/pages/SettingsPage.js +144 -159
  58. package/dist/botlit/pages/SettingsPage.js.map +1 -1
  59. package/dist/botlit/pages/SkillsPage.js +122 -105
  60. package/dist/botlit/pages/SkillsPage.js.map +1 -1
  61. package/dist/botlit/pages/StorePage.js +176 -175
  62. package/dist/botlit/pages/StorePage.js.map +1 -1
  63. package/dist/botlit/providers/BotlitProvider.js.map +1 -1
  64. package/dist/botlit/store/botlitStore.js +1 -1
  65. package/dist/botlit/store/botlitStore.js.map +1 -1
  66. package/dist/botlit/utils/navigation.js.map +1 -1
  67. package/dist/generated/wspace-operations.js +45 -29
  68. package/dist/generated/wspace-operations.js.map +1 -1
  69. package/dist/generated/wspace-types.js.map +1 -1
  70. package/dist/shared/hooks/useTr.js +14 -0
  71. package/dist/shared/hooks/useTr.js.map +1 -0
  72. package/package.json +33 -9
@@ -1 +1 @@
1
- {"version":3,"file":"AgentsListPage.js","names":[],"sources":["../../../src/botlit/pages/AgentsListPage.tsx"],"sourcesContent":["/**\n * Agents List Page\n * @module botlit/pages\n */\n\nimport type { ChangeEvent, FC } from 'react';\nimport { useState } from 'react';\nimport { useParams, Link } from 'react-router';\nimport {\n Bot,\n Plus,\n Search,\n Grid3X3,\n List,\n MoreVertical,\n MessageSquare,\n Trash2,\n Settings,\n Play,\n} from 'lucide-react';\nimport { toast } from 'sonner';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input } from '../components/Input';\nimport { StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport { useAgents, useDeleteAgent, type AgentRecord } from '../hooks/useAgents';\nimport { useBotlitStore } from '../store/botlitStore';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { formatRelativeTime, formatNumber } from '../utils/formatters';\nimport { cn } from '../utils/cn';\n\nexport const AgentsListPage: FC = () => {\n const { botId } = useParams();\n const { navigateTo, getPath } = useBotlit();\n const { data: agents, loading, error, refetch } = useAgents();\n const [deleteAgent] = useDeleteAgent();\n const { viewMode, setViewMode, agentFilter, updateAgentFilter } = useBotlitStore();\n const [menuOpen, setMenuOpen] = useState<string | null>(null);\n\n const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {\n updateAgentFilter({ search: e.target.value });\n };\n\n const handleDelete = async (agent: { id: string; name: string }) => {\n const confirmed = await nativeConfirm({\n title: 'Delete agent?',\n message: `Are you sure you want to delete \"${agent.name}\"?`,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok =\n confirmed === null ? confirm(`Are you sure you want to delete \"${agent.name}\"?`) : confirmed;\n if (ok) {\n nativeImpact('medium');\n try {\n await deleteAgent(agent.id);\n toast.success(`Agent \"${agent.name}\" deleted successfully`);\n if (isNative()) void nativeNotify('success');\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'Failed to delete agent';\n toast.error(message);\n if (isNative()) void nativeNotify('error');\n }\n }\n setMenuOpen(null);\n };\n\n const filteredAgents = agents.filter((agent) => {\n if (agentFilter.search) {\n const search = agentFilter.search.toLowerCase();\n return (\n agent.name.toLowerCase().includes(search) ||\n (agent.description?.toLowerCase().includes(search) ?? false)\n );\n }\n return true;\n });\n\n return (\n <PageLayout\n title={botId ? 'Bot Agents' : 'All Agents'}\n description={botId ? 'Manage agents for this bot' : 'View and manage all your AI agents'}\n actions={\n <Button\n leftIcon={<Plus size={16} />}\n onClick={() => navigateTo(botId ? `bots/${botId}/agents/new` : 'agents/new')}\n >\n Create Agent\n </Button>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Bot size={18} />}>\n Agents are the reasoning workers behind your bots — each one bundles a model, a system\n prompt, tools and knowledge into a callable AI. Build an agent here to handle a specific\n job (answer support questions, summarise documents, run a workflow), then test it in chat\n and connect it to a bot or channel.\n </PagePurpose>\n\n {/* Summary */}\n {agents.length > 0 ? (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">Total agents</p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {agents.length}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">Active</p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {agents.filter((agent) => agent.status === 'ACTIVE').length}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"col-span-2 p-4 sm:col-span-1\">\n <p className=\"text-sm text-text-secondary\">Total executions</p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {formatNumber(agents.reduce((sum, agent) => sum + (agent.executionCount ?? 0), 0))}\n </p>\n </GlassCard>\n </div>\n ) : null}\n\n {/* Toolbar */}\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"relative flex-1 max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder=\"Search agents...\"\n value={agentFilter.search || ''}\n onChange={handleSearch}\n className=\"pl-9\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={() => setViewMode('grid')}\n >\n <Grid3X3 size={18} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={() => setViewMode('list')}\n >\n <List size={18} />\n </Button>\n </div>\n </div>\n\n {/* Content */}\n {loading ? (\n <div\n className={cn(\n 'gap-4',\n viewMode === 'grid'\n ? 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3'\n : 'flex flex-col'\n )}\n >\n {[1, 2, 3, 4, 5, 6].map((i) => (\n <div\n key={i}\n className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\"\n />\n ))}\n </div>\n ) : error ? (\n <ErrorState resource=\"agents\" error={error} onRetry={() => void refetch()} />\n ) : filteredAgents.length > 0 ? (\n viewMode === 'grid' ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredAgents.map((agent: AgentRecord) => (\n <Card\n key={agent.id}\n className=\"group hover:shadow-md transition-shadow cursor-pointer relative\"\n onClick={() => navigateTo(`agents/${agent.id}`)}\n >\n <CardContent className=\"pt-6\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <span className=\"text-4xl\">🤖</span>\n <div>\n <h3 className=\"font-semibold text-text-primary\">{agent.name}</h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <StatusBadge status={agent.status} />\n </div>\n </div>\n </div>\n <div className=\"relative\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={(e) => {\n e.stopPropagation();\n setMenuOpen(menuOpen === agent.id ? null : agent.id);\n }}\n >\n <MoreVertical size={16} />\n </Button>\n {menuOpen === agent.id && (\n <div className=\"absolute right-0 top-full mt-1 w-48 bg-popover border border-border-subtle rounded-md shadow-lg z-10\">\n <button\n className=\"w-full px-4 py-2 text-left text-sm hover:bg-accent flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`agents/${agent.id}/chat`);\n setMenuOpen(null);\n }}\n >\n <Play size={16} />\n Test Agent\n </button>\n <button\n className=\"w-full px-4 py-2 text-left text-sm hover:bg-accent flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`agents/${agent.id}/edit`);\n setMenuOpen(null);\n }}\n >\n <Settings size={16} />\n Edit Agent\n </button>\n <button\n className=\"w-full px-4 py-2 text-left text-sm hover:bg-accent text-status-error-text flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(agent);\n }}\n >\n <Trash2 size={16} />\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">\n {agent.description}\n </p>\n <div className=\"mt-4 flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <MessageSquare size={14} />\n {formatNumber(agent.executionCount)} executions\n </span>\n </div>\n <div className=\"mt-3 pt-3 border-t border-border-subtle flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">\n Created {formatRelativeTime(agent.createdAt)}\n </span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n leftIcon={<Play size={14} />}\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`agents/${agent.id}/chat`);\n }}\n >\n Test\n </Button>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n ) : (\n <div className=\"space-y-2\">\n {filteredAgents.map((agent: AgentRecord) => (\n <Card\n key={agent.id}\n className=\"hover:shadow-md transition-shadow cursor-pointer\"\n onClick={() => navigateTo(`agents/${agent.id}`)}\n >\n <CardContent className=\"py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <span className=\"text-3xl\">🤖</span>\n <div>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{agent.name}</h3>\n <StatusBadge status={agent.status} />\n </div>\n <p className=\"text-sm text-text-secondary line-clamp-1\">\n {agent.description}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4 text-sm text-text-secondary\">\n <span>{formatNumber(agent.executionCount)} executions</span>\n <span>{formatRelativeTime(agent.createdAt)}</span>\n <Button variant=\"outline\" size=\"sm\" leftIcon={<Play size={14} />}>\n <Link to={getPath(`agents/${agent.id}/chat`)}>Test</Link>\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )\n ) : (\n <EmptyState\n illustration=\"empty-agents\"\n title=\"No agents yet\"\n description=\"Create your first AI agent to start building intelligent automations.\"\n action={\n <Button\n leftIcon={<Plus size={16} />}\n onClick={() => navigateTo(botId ? `bots/${botId}/agents/new` : 'agents/new')}\n >\n Create Agent\n </Button>\n }\n />\n )}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentsListPage.js","names":[],"sources":["../../../src/botlit/pages/AgentsListPage.tsx"],"sourcesContent":["/**\n * Agents List Page\n * @module botlit/pages\n */\n\nimport type { ChangeEvent, FC } from 'react';\nimport { useState } from 'react';\nimport { useParams, Link } from 'react-router';\nimport {\n Bot,\n Plus,\n Search,\n Grid3X3,\n List,\n MoreVertical,\n MessageSquare,\n Trash2,\n Settings,\n Play,\n} from 'lucide-react';\nimport { toast } from 'sonner';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input } from '../components/Input';\nimport { StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport { useAgents, useDeleteAgent, type AgentRecord } from '../hooks/useAgents';\nimport { useBotlitStore } from '../store/botlitStore';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { formatRelativeTime, formatNumber } from '../utils/formatters';\nimport { cn } from '../utils/cn';\n\nexport const AgentsListPage: FC = () => {\n const t = useTr();\n const { botId } = useParams();\n const { navigateTo, getPath } = useBotlit();\n const { data: agents, loading, error, refetch } = useAgents();\n const [deleteAgent] = useDeleteAgent();\n const { viewMode, setViewMode, agentFilter, updateAgentFilter } = useBotlitStore();\n const [menuOpen, setMenuOpen] = useState<string | null>(null);\n\n const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {\n updateAgentFilter({ search: e.target.value });\n };\n\n const handleDelete = async (agent: { id: string; name: string }) => {\n const confirmed = await nativeConfirm({\n title: t('botlit.agentsList.delete.title', 'Delete agent?'),\n message: t(\n 'botlit.agentsList.delete.message',\n 'Are you sure you want to delete \"{{name}}\"?',\n {\n name: agent.name,\n }\n ),\n okButtonTitle: t('botlit.agentsList.delete.okButton', 'Delete'),\n cancelButtonTitle: t('botlit.agentsList.delete.cancelButton', 'Cancel'),\n });\n const ok =\n confirmed === null\n ? confirm(\n t('botlit.agentsList.delete.message', 'Are you sure you want to delete \"{{name}}\"?', {\n name: agent.name,\n })\n )\n : confirmed;\n if (ok) {\n nativeImpact('medium');\n try {\n await deleteAgent(agent.id);\n toast.success(\n t('botlit.agentsList.delete.success', 'Agent \"{{name}}\" deleted successfully', {\n name: agent.name,\n })\n );\n if (isNative()) void nativeNotify('success');\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.agentsList.delete.error', 'Failed to delete agent');\n toast.error(message);\n if (isNative()) void nativeNotify('error');\n }\n }\n setMenuOpen(null);\n };\n\n const filteredAgents = agents.filter((agent) => {\n if (agentFilter.search) {\n const search = agentFilter.search.toLowerCase();\n return (\n agent.name.toLowerCase().includes(search) ||\n (agent.description?.toLowerCase().includes(search) ?? false)\n );\n }\n return true;\n });\n\n return (\n <PageLayout\n title={\n botId\n ? t('botlit.agentsList.titleBot', 'Bot Agents')\n : t('botlit.agentsList.title', 'All Agents')\n }\n description={\n botId\n ? t('botlit.agentsList.descriptionBot', 'Manage agents for this bot')\n : t('botlit.agentsList.description', 'View and manage all your AI agents')\n }\n actions={\n <Button\n leftIcon={<Plus size={16} />}\n onClick={() => navigateTo(botId ? `bots/${botId}/agents/new` : 'agents/new')}\n >\n {t('botlit.agentsList.actions.createAgent', 'Create Agent')}\n </Button>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Bot size={18} />}>\n {t(\n 'botlit.agentsList.purpose',\n 'Agents are the reasoning workers behind your bots — each one bundles a model, a system prompt, tools and knowledge into a callable AI. Build an agent here to handle a specific job (answer support questions, summarise documents, run a workflow), then test it in chat and connect it to a bot or channel.'\n )}\n </PagePurpose>\n\n {/* Summary */}\n {agents.length > 0 ? (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.agentsList.summary.total', 'Total agents')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {agents.length}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.agentsList.summary.active', 'Active')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {agents.filter((agent) => agent.status === 'ACTIVE').length}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"col-span-2 p-4 sm:col-span-1\">\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.agentsList.summary.executions', 'Total executions')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {formatNumber(agents.reduce((sum, agent) => sum + (agent.executionCount ?? 0), 0))}\n </p>\n </GlassCard>\n </div>\n ) : null}\n\n {/* Toolbar */}\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"relative flex-1 max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder={t('botlit.agentsList.searchPlaceholder', 'Search agents...')}\n value={agentFilter.search || ''}\n onChange={handleSearch}\n className=\"pl-9\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={() => setViewMode('grid')}\n >\n <Grid3X3 size={18} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={() => setViewMode('list')}\n >\n <List size={18} />\n </Button>\n </div>\n </div>\n\n {/* Content */}\n {loading ? (\n <div\n className={cn(\n 'gap-4',\n viewMode === 'grid'\n ? 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3'\n : 'flex flex-col'\n )}\n >\n {[1, 2, 3, 4, 5, 6].map((i) => (\n <div\n key={i}\n className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\"\n />\n ))}\n </div>\n ) : error ? (\n <ErrorState resource=\"agents\" error={error} onRetry={() => void refetch()} />\n ) : filteredAgents.length > 0 ? (\n viewMode === 'grid' ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredAgents.map((agent: AgentRecord) => (\n <Card\n key={agent.id}\n className=\"group hover:shadow-md transition-shadow cursor-pointer relative\"\n onClick={() => navigateTo(`agents/${agent.id}`)}\n >\n <CardContent className=\"pt-6\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <span className=\"text-4xl\">🤖</span>\n <div>\n <h3 className=\"font-semibold text-text-primary\">{agent.name}</h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <StatusBadge status={agent.status} />\n </div>\n </div>\n </div>\n <div className=\"relative\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={(e) => {\n e.stopPropagation();\n setMenuOpen(menuOpen === agent.id ? null : agent.id);\n }}\n >\n <MoreVertical size={16} />\n </Button>\n {menuOpen === agent.id && (\n <div className=\"absolute right-0 top-full mt-1 w-48 bg-popover border border-border-subtle rounded-md shadow-lg z-10\">\n <button\n className=\"w-full px-4 py-2 text-left text-sm hover:bg-accent flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`agents/${agent.id}/chat`);\n setMenuOpen(null);\n }}\n >\n <Play size={16} />\n {t('botlit.agentsList.menu.testAgent', 'Test Agent')}\n </button>\n <button\n className=\"w-full px-4 py-2 text-left text-sm hover:bg-accent flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`agents/${agent.id}/edit`);\n setMenuOpen(null);\n }}\n >\n <Settings size={16} />\n {t('botlit.agentsList.menu.editAgent', 'Edit Agent')}\n </button>\n <button\n className=\"w-full px-4 py-2 text-left text-sm hover:bg-accent text-status-error-text flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(agent);\n }}\n >\n <Trash2 size={16} />\n {t('botlit.agentsList.menu.delete', 'Delete')}\n </button>\n </div>\n )}\n </div>\n </div>\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">\n {agent.description}\n </p>\n <div className=\"mt-4 flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <MessageSquare size={14} />\n {formatNumber(agent.executionCount)}{' '}\n {t('botlit.agentsList.card.executions', 'executions')}\n </span>\n </div>\n <div className=\"mt-3 pt-3 border-t border-border-subtle flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">\n {t('botlit.agentsList.card.created', 'Created')}{' '}\n {formatRelativeTime(agent.createdAt)}\n </span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n leftIcon={<Play size={14} />}\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`agents/${agent.id}/chat`);\n }}\n >\n {t('botlit.agentsList.card.test', 'Test')}\n </Button>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n ) : (\n <div className=\"space-y-2\">\n {filteredAgents.map((agent: AgentRecord) => (\n <Card\n key={agent.id}\n className=\"hover:shadow-md transition-shadow cursor-pointer\"\n onClick={() => navigateTo(`agents/${agent.id}`)}\n >\n <CardContent className=\"py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <span className=\"text-3xl\">🤖</span>\n <div>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{agent.name}</h3>\n <StatusBadge status={agent.status} />\n </div>\n <p className=\"text-sm text-text-secondary line-clamp-1\">\n {agent.description}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4 text-sm text-text-secondary\">\n <span>{formatNumber(agent.executionCount)} executions</span>\n <span>{formatRelativeTime(agent.createdAt)}</span>\n <Button variant=\"outline\" size=\"sm\" leftIcon={<Play size={14} />}>\n <Link to={getPath(`agents/${agent.id}/chat`)}>Test</Link>\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )\n ) : (\n <EmptyState\n illustration=\"empty-agents\"\n title={t('botlit.agentsList.empty.title', 'No agents yet')}\n description={t(\n 'botlit.agentsList.empty.description',\n 'Create your first AI agent to start building intelligent automations.'\n )}\n action={\n <Button\n leftIcon={<Plus size={16} />}\n onClick={() => navigateTo(botId ? `bots/${botId}/agents/new` : 'agents/new')}\n >\n {t('botlit.agentsList.actions.createAgent', 'Create Agent')}\n </Button>\n }\n />\n )}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqCA,IAAa,UAA2B;CACtC,IAAM,IAAI,GAAO,EACX,EAAE,aAAU,GAAW,EACvB,EAAE,eAAY,eAAY,GAAW,EACrC,EAAE,MAAM,GAAQ,YAAS,UAAO,eAAY,GAAW,EACvD,CAAC,KAAe,GAAgB,EAChC,EAAE,aAAU,gBAAa,gBAAa,yBAAsB,GAAgB,EAC5E,CAAC,GAAU,KAAe,EAAwB,KAAK,EAEvD,KAAgB,MAAqC;AACzD,IAAkB,EAAE,QAAQ,EAAE,OAAO,OAAO,CAAC;IAGzC,KAAe,OAAO,MAAwC;EAClE,IAAM,IAAY,MAAM,EAAc;GACpC,OAAO,EAAE,kCAAkC,gBAAgB;GAC3D,SAAS,EACP,oCACA,iDACA,EACE,MAAM,EAAM,MACb,CACF;GACD,eAAe,EAAE,qCAAqC,SAAS;GAC/D,mBAAmB,EAAE,yCAAyC,SAAS;GACxE,CAAC;AASF,MAPE,MAAc,OACV,QACE,EAAE,oCAAoC,iDAA+C,EACnF,MAAM,EAAM,MACb,CAAC,CACH,GACD,GACE;AACN,KAAa,SAAS;AACtB,OAAI;AAOF,IANA,MAAM,EAAY,EAAM,GAAG,EAC3B,EAAM,QACJ,EAAE,oCAAoC,2CAAyC,EAC7E,MAAM,EAAM,MACb,CAAC,CACH,EACG,GAAU,IAAO,EAAa,UAAU;YACrC,GAAc;IACrB,IAAM,IACJ,aAAe,QACX,EAAI,UACJ,EAAE,kCAAkC,yBAAyB;AAEnE,IADA,EAAM,MAAM,EAAQ,EAChB,GAAU,IAAO,EAAa,QAAQ;;;AAG9C,IAAY,KAAK;IAGb,IAAiB,EAAO,QAAQ,MAAU;AAC9C,MAAI,EAAY,QAAQ;GACtB,IAAM,IAAS,EAAY,OAAO,aAAa;AAC/C,UACE,EAAM,KAAK,aAAa,CAAC,SAAS,EAAO,KACxC,EAAM,aAAa,aAAa,CAAC,SAAS,EAAO,IAAI;;AAG1D,SAAO;GACP;AAEF,QACE,kBAAC,GAAD;EACE,OACE,IACI,EAAE,8BAA8B,aAAa,GAC7C,EAAE,2BAA2B,aAAa;EAEhD,aACE,IACI,EAAE,oCAAoC,6BAA6B,GACnE,EAAE,iCAAiC,qCAAqC;EAE9E,SACE,kBAAC,GAAD;GACE,UAAU,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;GAC5B,eAAe,EAAW,IAAQ,QAAQ,EAAM,eAAe,aAAa;aAE3E,EAAE,yCAAyC,eAAe;GACpD,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAa,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;eACjC,EACC,6BACA,gTACD;KACW,CAAA;IAGb,EAAO,SAAS,IACf,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAW,WAAU;OAAQ,MAAA;OAAK,WAAU;iBAA5C,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAE,mCAAmC,eAAe;QACnD,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAO;QACN,CAAA,CACM;;MACZ,kBAAC,GAAD;OAAW,WAAU;OAAS,WAAU;iBAAxC,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAE,oCAAoC,SAAS;QAC9C,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAO,QAAQ,MAAU,EAAM,WAAW,SAAS,CAAC;QACnD,CAAA,CACM;;MACZ,kBAAC,GAAD;OAAW,WAAU;OAAS,WAAU;iBAAxC,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAE,wCAAwC,mBAAmB;QAC5D,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAa,EAAO,QAAQ,GAAK,MAAU,KAAO,EAAM,kBAAkB,IAAI,EAAE,CAAC;QAChF,CAAA,CACM;;MACR;SACJ;IAGJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;OACE,aAAa,EAAE,uCAAuC,mBAAmB;OACzE,OAAO,EAAY,UAAU;OAC7B,UAAU;OACV,WAAU;OACV,CAAA,CACE;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,SAAS,MAAa,SAAS,cAAc;OAC7C,MAAK;OACL,eAAe,EAAY,OAAO;iBAElC,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;OACd,CAAA,EACT,kBAAC,GAAD;OACE,SAAS,MAAa,SAAS,cAAc;OAC7C,MAAK;OACL,eAAe,EAAY,OAAO;iBAElC,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;OACX,CAAA,CACL;QACF;;IAGL,IACC,kBAAC,OAAD;KACE,WAAW,EACT,SACA,MAAa,SACT,mDACA,gBACL;eAEA;MAAC;MAAG;MAAG;MAAG;MAAG;MAAG;MAAE,CAAC,KAAK,MACvB,kBAAC,OAAD,EAEE,WAAU,+DACV,EAFK,EAEL,CACF;KACE,CAAA,GACJ,IACF,kBAAC,GAAD;KAAY,UAAS;KAAgB;KAAO,eAAe,KAAK,GAAS;KAAI,CAAA,GAC3E,EAAe,SAAS,IAC1B,MAAa,SACX,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAe,KAAK,MACnB,kBAAC,GAAD;MAEE,WAAU;MACV,eAAe,EAAW,UAAU,EAAM,KAAK;gBAE/C,kBAAC,GAAD;OAAa,WAAU;iBAAvB;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAAW;WAAS,CAAA,EACpC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAmC,EAAM;WAAU,CAAA,EACjE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA;WACjC,CAAA,CACF,EAAA,CAAA,CACF;aACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WACE,SAAQ;WACR,MAAK;WACL,WAAU;WACV,UAAU,MAAM;AAEd,YADA,EAAE,iBAAiB,EACnB,EAAY,MAAa,EAAM,KAAK,OAAO,EAAM,GAAG;;qBAGtD,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;WACnB,CAAA,EACR,MAAa,EAAM,MAClB,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,UAAD;aACE,WAAU;aACV,UAAU,MAAM;AAGd,cAFA,EAAE,iBAAiB,EACnB,EAAW,UAAU,EAAM,GAAG,OAAO,EACrC,EAAY,KAAK;;uBALrB,CAQE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAE,oCAAoC,aAAa,CAC7C;;YACT,kBAAC,UAAD;aACE,WAAU;aACV,UAAU,MAAM;AAGd,cAFA,EAAE,iBAAiB,EACnB,EAAW,UAAU,EAAM,GAAG,OAAO,EACrC,EAAY,KAAK;;uBALrB,CAQE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EACrB,EAAE,oCAAoC,aAAa,CAC7C;;YACT,kBAAC,UAAD;aACE,WAAU;aACV,UAAU,MAAM;AAEd,cADA,EAAE,iBAAiB,EACnB,GAAa,EAAM;;uBAJvB,CAOE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAE,iCAAiC,SAAS,CACtC;;YACL;aAEJ;YACF;;QACN,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAM;SACL,CAAA;QACJ,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAe,MAAM,IAAM,CAAA;WAC1B,EAAa,EAAM,eAAe;WAAE;WACpC,EAAE,qCAAqC,aAAa;WAChD;;SACH,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACG,EAAE,kCAAkC,UAAU;WAAE;WAChD,EAAmB,EAAM,UAAU;WAC/B;aACP,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,UAAU,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;UAC5B,UAAU,MAAM;AAEd,WADA,EAAE,iBAAiB,EACnB,EAAW,UAAU,EAAM,GAAG,OAAO;;oBAGtC,EAAE,+BAA+B,OAAO;UAClC,CAAA,CACL;;QACM;;MACT,EA7FA,EAAM,GA6FN,CACP;KACE,CAAA,GAEN,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAe,KAAK,MACnB,kBAAC,GAAD;MAEE,WAAU;MACV,eAAe,EAAW,UAAU,EAAM,KAAK;gBAE/C,kBAAC,GAAD;OAAa,WAAU;iBACrB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAW;UAAS,CAAA,EACpC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAmC,EAAM;WAAU,CAAA,EACjE,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA,CACjC;aACN,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAM;UACL,CAAA,CACA,EAAA,CAAA,CACF;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAa,EAAM,eAAe,EAAC,cAAkB,EAAA,CAAA;UAC5D,kBAAC,QAAD,EAAA,UAAO,EAAmB,EAAM,UAAU,EAAQ,CAAA;UAClD,kBAAC,GAAD;WAAQ,SAAQ;WAAU,MAAK;WAAK,UAAU,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;qBAC9D,kBAAC,GAAD;YAAM,IAAI,EAAQ,UAAU,EAAM,GAAG,OAAO;sBAAE;YAAW,CAAA;WAClD,CAAA;UACL;WACF;;OACM,CAAA;MACT,EA3BA,EAAM,GA2BN,CACP;KACE,CAAA,GAGR,kBAAC,IAAD;KACE,cAAa;KACb,OAAO,EAAE,iCAAiC,gBAAgB;KAC1D,aAAa,EACX,uCACA,wEACD;KACD,QACE,kBAAC,GAAD;MACE,UAAU,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;MAC5B,eAAe,EAAW,IAAQ,QAAQ,EAAM,eAAe,aAAa;gBAE3E,EAAE,yCAAyC,eAAe;MACpD,CAAA;KAEX,CAAA;IAEA;;EACK,CAAA"}
@@ -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 Botlit 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 /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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"}