AdvisorButton.tsx 3.1 KB

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