ErrorCodeTooltip.tsx130 lines · main
1import { ExternalLink } from 'lucide-react'
2import { useTheme } from 'next-themes'
3import Image from 'next/image'
4import { useState } from 'react'
5import { cn, HoverCard, HoverCardContent, HoverCardTrigger, InfoIcon } from 'ui'
6import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
7
8import { getErrorCodeInfo } from './ErrorCodeTooltip.utils'
9import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
10import { AiAssistantDropdown } from '@/components/ui/AiAssistantDropdown'
11import { useErrorCodesQuery } from '@/data/content-api/docs-error-codes-query'
12import { Service } from '@/data/graphql/graphql'
13import { BASE_PATH } from '@/lib/constants'
14import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
15import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
16
17interface ErrorCodeTooltipProps {
18 errorCode: string
19 service?: Service
20 children: React.ReactNode
21}
22
23export const ErrorCodeTooltip = ({ errorCode, service, children }: ErrorCodeTooltipProps) => {
24 const [isOpen, setIsOpen] = useState(false)
25 const { resolvedTheme } = useTheme()
26 const snap = useAiAssistantStateSnapshot()
27 const { openSidebar } = useSidebarManagerSnapshot()
28
29 const { definition, docsUrl: sharedDocsUrl } = getErrorCodeInfo(errorCode, service)
30
31 const { data, isPending } = useErrorCodesQuery(
32 { code: errorCode, service },
33 { enabled: isOpen && !definition }
34 )
35
36 const errors = data?.errors?.nodes?.filter((e) => !!e.message) ?? []
37
38 const description = definition?.description ?? errors[0]?.message
39 const docsUrl = sharedDocsUrl
40
41 const buildPrompt = () => {
42 const servicePart = service ? ` in Briven ${service}` : ''
43 const descriptionPart = description ? `\n\nError description: ${description}` : ''
44 return `I'm encountering error code \`${errorCode}\`${servicePart}.${descriptionPart}\n\nCan you explain what this error means and suggest steps to fix it?`
45 }
46
47 const handleOpenAssistant = () => {
48 openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
49 snap.newChat({
50 name: `Fix error ${errorCode}`,
51 initialMessage: buildPrompt(),
52 })
53 }
54
55 return (
56 <HoverCard open={isOpen} onOpenChange={setIsOpen} openDelay={200} closeDelay={100}>
57 <HoverCardTrigger asChild>
58 <span className="inline-flex items-center gap-1 cursor-default">
59 {children}
60 <InfoIcon hideBackground className="w-3.5 h-3.5 shrink-0 fill-foreground-muted" />
61 </span>
62 </HoverCardTrigger>
63 <HoverCardContent side="top" align="center" className="w-[360px] p-0 overflow-hidden">
64 <div className="flex flex-col">
65 <div className="px-4 pt-3 pb-2.5 border-b border-border">
66 <p className="font-mono text-xs uppercase text-foreground-light truncate max-w-[40ch]">
67 {errorCode}
68 </p>
69 </div>
70
71 <div className="px-4 py-3 space-y-2">
72 {isPending && !definition ? (
73 <div className="space-y-1.5">
74 <ShimmeringLoader className="w-full" />
75 <ShimmeringLoader className="w-4/5" />
76 <ShimmeringLoader className="w-3/5" />
77 </div>
78 ) : !description ? (
79 <p className="text-sm text-foreground-lighter">
80 No description available for this error code.
81 </p>
82 ) : (
83 <p className="text-sm text-foreground leading-relaxed">{description}</p>
84 )}
85 </div>
86
87 <div
88 className={cn(
89 'border-t border-border px-4 py-2.5 flex items-center',
90 docsUrl ? 'justify-between' : 'justify-end'
91 )}
92 >
93 {docsUrl && (
94 <div className="flex items-center gap-1.5">
95 <Image
96 src={
97 resolvedTheme?.includes('dark')
98 ? `${BASE_PATH}/img/briven-dark.svg`
99 : `${BASE_PATH}/img/briven-light.svg`
100 }
101 alt="Briven"
102 height={14}
103 width={72}
104 />
105 <span className="font-mono text-[11px] font-semibold text-brand tracking-wide">
106 DOCS
107 </span>
108 </div>
109 )}
110 <AiAssistantDropdown
111 showExternalAI
112 label="Fix with AI"
113 buildPrompt={buildPrompt}
114 onOpenAssistant={handleOpenAssistant}
115 telemetrySource="error_code"
116 copyLabel="Copy Markdown"
117 additionalDropdownItems={[
118 {
119 label: 'Go to Docs',
120 icon: <ExternalLink size={14} />,
121 href: docsUrl,
122 },
123 ]}
124 />
125 </div>
126 </div>
127 </HoverCardContent>
128 </HoverCard>
129 )
130}