From d0e34fa9dd645f8423b9177f879d1240c27eb7f5 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Mon, 20 Apr 2026 10:45:45 +0530 Subject: [PATCH] feat: global AI assistant floating button for supervisors (#578) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AiFloatingButton: FAB (bottom-right) opens a slide-in drawer with the supervisor AI chat panel. Close button collapses drawer, FAB reappears. Chat state persists across open/close and page navigation. - app-shell: mounts FAB for admin users (isAdmin), same pattern as CallWidget for agents. - team-dashboard: removed inline AI panel + toggle button — replaced by the global FAB. Dashboard content reclaims the full width. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/layout/app-shell.tsx | 4 +- src/components/shared/ai-floating-button.tsx | 50 +++++++++++++++++++ src/pages/team-dashboard.tsx | 52 ++++++-------------- 3 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 src/components/shared/ai-floating-button.tsx diff --git a/src/components/layout/app-shell.tsx b/src/components/layout/app-shell.tsx index 5f0a928..11142ca 100644 --- a/src/components/layout/app-shell.tsx +++ b/src/components/layout/app-shell.tsx @@ -15,6 +15,7 @@ import { useData } from '@/providers/data-provider'; import { useMaintShortcuts } from '@/hooks/use-maint-shortcuts'; import { useNetworkStatus } from '@/hooks/use-network-status'; // import { GlobalSearch } from '@/components/shared/global-search'; +import { AiFloatingButton } from '@/components/shared/ai-floating-button'; import { apiClient } from '@/lib/api-client'; import { cx } from '@/utils/cx'; @@ -24,7 +25,7 @@ interface AppShellProps { export const AppShell = ({ children }: AppShellProps) => { const { pathname } = useLocation(); - const { isCCAgent } = useAuth(); + const { isCCAgent, isAdmin } = useAuth(); const { isOpen, activeAction, close } = useMaintShortcuts(); const { connectionStatus, isRegistered } = useSip(); const networkQuality = useNetworkStatus(); @@ -143,6 +144,7 @@ export const AppShell = ({ children }: AppShellProps) => {
{children}
{isCCAgent && pathname !== '/' && pathname !== '/call-desk' && } + {isAdmin && } { + const [open, setOpen] = useState(false); + + return ( + <> + {/* FAB — bottom right, hidden when drawer is open */} + {!open && ( + + )} + + {/* Drawer — slides in from right */} +
+ {open && ( + <> +
+
+ + AI Assistant +
+ +
+
+ +
+ + )} +
+ + ); +}; diff --git a/src/pages/team-dashboard.tsx b/src/pages/team-dashboard.tsx index 100bb15..fa5899e 100644 --- a/src/pages/team-dashboard.tsx +++ b/src/pages/team-dashboard.tsx @@ -1,8 +1,5 @@ import { useMemo, useState } from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faSidebarFlip, faSidebar } from '@fortawesome/pro-duotone-svg-icons'; import { PageHeader } from '@/components/layout/page-header'; -import { AiChatPanel } from '@/components/call-desk/ai-chat-panel'; import { DashboardKpi } from '@/components/dashboard/kpi-cards'; import { MissedQueue } from '@/components/dashboard/missed-queue'; import { @@ -29,7 +26,6 @@ const getDateRangeStart = (range: DateRange): Date => { export const TeamDashboardPage = () => { const { calls, leads, campaigns, loading } = useData(); const [dateRange, setDateRange] = useState('week'); - const [aiOpen, setAiOpen] = useState(true); // Pull the richer supervisor rollup (NPS, idle, time breakdown, alerts) // from the sidecar. Only `today`/`week`/`month` overlap with the rollup's @@ -61,29 +57,20 @@ export const TeamDashboardPage = () => { subtitle={dateRangeLabel} infoText="Aggregated call metrics, agent performance, and operational alerts." controls={ - <> -
- {(['today', 'week', 'month'] as const).map((range) => ( - - ))} -
- - +
+ {(['today', 'week', 'month'] as const).map((range) => ( + + ))} +
} /> @@ -154,17 +141,6 @@ export const TeamDashboardPage = () => { - {/* AI panel — collapsible */} -
- {aiOpen && ( -
- -
- )} -