AdvisorButton.tsx82 lines · main
| 1 | import { Lightbulb } from 'lucide-react' |
| 2 | import { useMemo } from 'react' |
| 3 | import { cn } from 'ui' |
| 4 | |
| 5 | import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' |
| 6 | import { useAdvisorSignals } from '@/components/ui/AdvisorPanel/useAdvisorSignals' |
| 7 | import { ButtonTooltip } from '@/components/ui/ButtonTooltip' |
| 8 | import { useProjectLintsQuery } from '@/data/lint/lint-query' |
| 9 | import { useNotificationsV2Query } from '@/data/notifications/notifications-v2-query' |
| 10 | import { useTrack } from '@/lib/telemetry/track' |
| 11 | import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' |
| 12 | |
| 13 | export const AdvisorButton = ({ projectRef }: { projectRef?: string }) => { |
| 14 | const { toggleSidebar, activeSidebar } = useSidebarManagerSnapshot() |
| 15 | const track = useTrack() |
| 16 | |
| 17 | const { data: lints } = useProjectLintsQuery({ projectRef }) |
| 18 | const { data: signalItems } = useAdvisorSignals({ projectRef }) |
| 19 | |
| 20 | const { data: notificationsData } = useNotificationsV2Query({ |
| 21 | filters: {}, |
| 22 | limit: 20, |
| 23 | }) |
| 24 | const notifications = useMemo(() => { |
| 25 | return notificationsData?.pages.flatMap((page) => page) ?? [] |
| 26 | }, [notificationsData?.pages]) |
| 27 | const hasUnreadNotifications = notifications.some((x) => x?.status === 'new') |
| 28 | const hasCriticalNotifications = notifications.some((x) => x?.priority === 'Critical') |
| 29 | const hasSignals = signalItems.length > 0 |
| 30 | const hasCriticalSignals = signalItems.some((item) => item.severity === 'critical') |
| 31 | |
| 32 | const hasCriticalIssues = |
| 33 | hasCriticalNotifications || |
| 34 | hasCriticalSignals || |
| 35 | (Array.isArray(lints) && lints.some((lint) => lint.level === 'ERROR')) |
| 36 | const hasWarningIssues = hasSignals && !hasCriticalIssues |
| 37 | |
| 38 | const isOpen = activeSidebar?.id === SIDEBAR_KEYS.ADVISOR_PANEL |
| 39 | |
| 40 | const handleClick = () => { |
| 41 | track('header_advisor_button_clicked') |
| 42 | toggleSidebar(SIDEBAR_KEYS.ADVISOR_PANEL) |
| 43 | } |
| 44 | |
| 45 | return ( |
| 46 | <div className="relative"> |
| 47 | <ButtonTooltip |
| 48 | type="outline" |
| 49 | size="tiny" |
| 50 | id="advisor-center-trigger" |
| 51 | className={cn( |
| 52 | 'rounded-full w-[32px] h-[32px] flex items-center justify-center p-0 group', |
| 53 | hasCriticalIssues && 'bg-destructive-200 border-destructive-500', |
| 54 | isOpen && 'bg-foreground text-background' |
| 55 | )} |
| 56 | onClick={handleClick} |
| 57 | tooltip={{ |
| 58 | content: { |
| 59 | text: 'Advisor Center', |
| 60 | }, |
| 61 | }} |
| 62 | > |
| 63 | <Lightbulb |
| 64 | size={16} |
| 65 | strokeWidth={1.5} |
| 66 | className={cn( |
| 67 | 'text-foreground-light group-hover:text-foreground', |
| 68 | isOpen && 'text-background group-hover:text-background' |
| 69 | )} |
| 70 | /> |
| 71 | <span className="sr-only">Advisor Center</span> |
| 72 | </ButtonTooltip> |
| 73 | {hasCriticalIssues ? ( |
| 74 | <span className="absolute top-1.5 right-1.5 w-1.5 h-1.5 rounded-full bg-destructive" /> |
| 75 | ) : hasWarningIssues ? ( |
| 76 | <span className="absolute top-1.5 right-1.5 w-1.5 h-1.5 rounded-full bg-warning" /> |
| 77 | ) : hasUnreadNotifications ? ( |
| 78 | <span className="absolute top-1.5 right-1.5 w-1.5 h-1.5 rounded-full bg-brand" /> |
| 79 | ) : null} |
| 80 | </div> |
| 81 | ) |
| 82 | } |