TroubleshootingSections.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. 'use client'
  2. import { ExternalLink } from 'lucide-react'
  3. import { useState } from 'react'
  4. import { AccordionContent, AccordionItem, AccordionTrigger, Button } from 'ui'
  5. import { RestartProjectDialog } from './RestartProjectDialog'
  6. import { AiAssistantDropdown } from '@/components/ui/AiAssistantDropdown'
  7. import { useTrack } from '@/lib/telemetry/track'
  8. interface StepTriggerProps {
  9. number: number
  10. title: string
  11. }
  12. function StepTrigger({ number, title }: StepTriggerProps) {
  13. return (
  14. <AccordionTrigger className="py-3 hover:no-underline">
  15. <div className="flex items-center gap-2.5">
  16. <span className="shrink-0 w-6 h-6 border border-button-hover text-foreground font-mono tabular-nums bg-button rounded-md text-xs font-medium flex items-center justify-center">
  17. {number}
  18. </span>
  19. <span className="text-sm font-medium text-foreground text-left">{title}</span>
  20. </div>
  21. </AccordionTrigger>
  22. )
  23. }
  24. interface RestartDatabaseTroubleshootingSectionProps {
  25. number: number
  26. errorType: string
  27. /** Override the restart handler. If not provided, opens the restart dialog internally. */
  28. onRestartProject?: () => void
  29. }
  30. export function RestartDatabaseTroubleshootingSection({
  31. number,
  32. errorType,
  33. onRestartProject,
  34. }: RestartDatabaseTroubleshootingSectionProps) {
  35. const track = useTrack()
  36. const [showDialog, setShowDialog] = useState(false)
  37. const handleClick = () => {
  38. track('inline_error_troubleshooter_action_clicked', {
  39. errorType,
  40. ctaType: 'restart_db',
  41. })
  42. if (onRestartProject) {
  43. onRestartProject()
  44. } else {
  45. setShowDialog(true)
  46. }
  47. }
  48. return (
  49. <>
  50. <AccordionItem
  51. value={`step-${number}`}
  52. className="border-b border-default last:border-b-0 px-3 py-2"
  53. >
  54. <StepTrigger number={number} title="Try restarting your project" />
  55. <AccordionContent className="pt-1">
  56. <div className="px-2">
  57. <p className="text-sm text-foreground-light mb-3">
  58. Restarting your project can help resolve timeout errors or stale connections.
  59. </p>
  60. <Button type="default" size="tiny" onClick={handleClick}>
  61. Restart project
  62. </Button>
  63. </div>
  64. </AccordionContent>
  65. </AccordionItem>
  66. <RestartProjectDialog
  67. visible={showDialog}
  68. onClose={() => setShowDialog(false)}
  69. restartType="database"
  70. />
  71. </>
  72. )
  73. }
  74. interface TroubleshootingGuideSectionProps {
  75. number: number
  76. errorType: string
  77. href: string
  78. title?: string
  79. description?: string
  80. }
  81. export function TroubleshootingGuideSection({
  82. number,
  83. errorType,
  84. href,
  85. title = 'Try our troubleshooting guide',
  86. description,
  87. }: TroubleshootingGuideSectionProps) {
  88. const track = useTrack()
  89. return (
  90. <AccordionItem
  91. value={`step-${number}`}
  92. className="border-b border-default last:border-b-0 px-3 py-2"
  93. >
  94. <StepTrigger number={number} title={title} />
  95. <AccordionContent className="pt-1">
  96. <div className="px-2">
  97. {description && <p className="text-sm text-foreground-light mb-3">{description}</p>}
  98. <Button
  99. asChild
  100. type="default"
  101. size="tiny"
  102. onClick={() =>
  103. track('inline_error_troubleshooter_action_clicked', {
  104. errorType,
  105. ctaType: 'troubleshooting_guide',
  106. })
  107. }
  108. iconRight={<ExternalLink />}
  109. >
  110. <a href={href} target="_blank" rel="noopener noreferrer">
  111. View troubleshooting guide
  112. </a>
  113. </Button>
  114. </div>
  115. </AccordionContent>
  116. </AccordionItem>
  117. )
  118. }
  119. interface FixWithAITroubleshootingSectionProps {
  120. number: number
  121. errorType: string
  122. description?: string
  123. onDebugWithAI?: (prompt: string) => void
  124. buildPrompt: () => string
  125. }
  126. export function FixWithAITroubleshootingSection({
  127. number,
  128. errorType,
  129. description = 'Let our AI assistant help diagnose and suggest solutions.',
  130. onDebugWithAI,
  131. buildPrompt,
  132. }: FixWithAITroubleshootingSectionProps) {
  133. const track = useTrack()
  134. return (
  135. <AccordionItem
  136. value={`step-${number}`}
  137. className="border-b border-default last:border-b-0 px-3 py-2"
  138. >
  139. <StepTrigger number={number} title="Debug with AI" />
  140. <AccordionContent className="pt-1">
  141. <div className="px-2">
  142. <p className="text-sm text-foreground-light mb-3">{description}</p>
  143. <AiAssistantDropdown
  144. label="Debug with AI"
  145. buildPrompt={buildPrompt}
  146. onOpenAssistant={() => {
  147. track('inline_error_troubleshooter_action_clicked', {
  148. errorType,
  149. ctaType: 'ask_ai',
  150. })
  151. onDebugWithAI?.(buildPrompt())
  152. }}
  153. size="tiny"
  154. />
  155. </div>
  156. </AccordionContent>
  157. </AccordionItem>
  158. )
  159. }