Home / Function/ AdvisorWidget() — supabase Function Reference

AdvisorWidget() — supabase Function Reference

Architecture documentation for the AdvisorWidget() function in AdvisorWidget.tsx from the supabase codebase.

Entity Profile

Relationship Graph

Source Code

apps/studio/components/interfaces/Home/AdvisorWidget.tsx lines 41–350

export const AdvisorWidget = () => {
  const { ref: projectRef } = useParams()
  const [selectedTab, setSelectedTab] = useState<'security' | 'performance'>('security')
  const { data: lints, isPending: isLoadingLints } = useProjectLintsQuery({ projectRef })
  const { data: slowestQueriesData, isLoading: isLoadingSlowestQueries } = useQueryPerformanceQuery(
    {
      preset: 'slowestExecutionTime',
    }
  )
  const snap = useAiAssistantStateSnapshot()
  const { openSidebar } = useSidebarManagerSnapshot()
  const { setSelectedItem } = useAdvisorStateSnapshot()
  const track = useTrack()

  const securityLints = useMemo(
    () => (lints ?? []).filter((lint: Lint) => lint.categories.includes('SECURITY')),
    [lints]
  )
  const performanceLints = useMemo(
    () => (lints ?? []).filter((lint: Lint) => lint.categories.includes('PERFORMANCE')),
    [lints]
  )

  const securityErrorCount = securityLints.filter(
    (lint: Lint) => lint.level === LINTER_LEVELS.ERROR
  ).length
  const securityWarningCount = securityLints.filter(
    (lint: Lint) => lint.level === LINTER_LEVELS.WARN
  ).length
  const performanceErrorCount = performanceLints.filter(
    (lint: Lint) => lint.level === LINTER_LEVELS.ERROR
  ).length
  const performanceWarningCount = performanceLints.filter(
    (lint: Lint) => lint.level === LINTER_LEVELS.WARN
  ).length

  const top5SlowestQueries = useMemo(
    () => ((slowestQueriesData ?? []) as SlowQuery[]).slice(0, 5),
    [slowestQueriesData]
  )

  const handleLintClick = useCallback(
    (lint: Lint) => {
      setSelectedItem(lint.cache_key, 'lint')
      openSidebar(SIDEBAR_KEYS.ADVISOR_PANEL)
    },
    [setSelectedItem, openSidebar]
  )

  const totalIssues =
    securityErrorCount + securityWarningCount + performanceErrorCount + performanceWarningCount
  const hasErrors = securityErrorCount > 0 || performanceErrorCount > 0
  const hasWarnings = securityWarningCount > 0 || performanceWarningCount > 0

  let titleContent: React.ReactNode

  if (totalIssues === 0) {
    titleContent = <h2>No issues available</h2>
  } else {
    const issuesText = totalIssues === 1 ? 'issue' : 'issues'
    const numberDisplay = totalIssues.toString()

    let attentionClassName = ''
    if (hasErrors) {
      attentionClassName = 'text-destructive'
    } else if (hasWarnings) {
      attentionClassName = 'text-warning'
    }

    titleContent = (
      <h2>
        {numberDisplay} {issuesText} need
        {totalIssues === 1 ? 's' : ''} <span className={attentionClassName}>attention</span>
      </h2>
    )
  }

  const renderLintTabContent = (
    title: string,
    lints: Lint[],
    errorCount: number,
    warningCount: number,
    isLoading: boolean
  ) => {
    const topIssues = lints
      .filter((lint) => lint.level === LINTER_LEVELS.ERROR || lint.level === LINTER_LEVELS.WARN)
      .sort((a, b) => (a.level === LINTER_LEVELS.ERROR ? -1 : 1))

    return (
      <div className="h-full">
        {isLoading && (
          <div className="flex flex-col p-4 gap-2">
            <ShimmeringLoader />
            <ShimmeringLoader className="w-3/4" />
            <ShimmeringLoader className="w-1/2" />
          </div>
        )}
        {!isLoading && (errorCount > 0 || warningCount > 0) && (
          <ul>
            {topIssues.map((lint) => {
              const lintText = lint.detail ? lint.detail : lint.title
              return (
                <li
                  key={lint.cache_key}
                  className="text-sm w-full border-b my-0 last:border-b-0 group px-4 "
                >
                  <div className="flex items-center justify-between w-full group">
                    <button
                      onClick={() => handleLintClick(lint)}
                      className="flex items-center gap-2 transition truncate flex-1 min-w-0 py-3 text-left"
                    >
                      <EntityTypeIcon type={lint.metadata?.type} />
                      <p className="flex-1 font-mono text-xs leading-6 text-xs text-foreground-light group-hover:text-foreground truncate">
                        {lintText.replace(/\\`/g, '`')}
                      </p>
                    </button>
                    <div
                      onClick={(e) => {
                        e.stopPropagation()
                        e.preventDefault()
                      }}
                      className="opacity-0 group-hover:opacity-100"
                    >
                      <AiAssistantDropdown
                        label="Ask Assistant"
                        iconOnly
                        tooltip="Help me fix this issue"
                        buildPrompt={() => createLintSummaryPrompt(lint)}
                        onOpenAssistant={() => {
                          openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
                          snap.newChat({
                            name: 'Summarize lint',
                            initialInput: createLintSummaryPrompt(lint),
                          })
                          track('advisor_assistant_button_clicked', {
                            origin: 'homepage',
                            advisorCategory: lint.categories[0],
                            advisorType: lint.name,
                            advisorLevel: lint.level,
                          })
                        }}
                        telemetrySource="advisor_widget"
                        type="text"
                        className="px-1 w-7"
                      />
                    </div>
                  </div>
                </li>
              )
            })}
          </ul>
        )}
        {!isLoading && errorCount === 0 && warningCount === 0 && (
          <div className="flex-1 flex flex-col h-full items-center justify-center gap-2">
            <Shield size={20} strokeWidth={1.5} className="text-foreground-muted" />
            <p className="text-sm text-foreground-light">No {title.toLowerCase()} issues found</p>
          </div>
        )}
      </div>
    )
  }

  return (
    <div className="@container">
      {isLoadingLints ? (
        <ShimmeringLoader className="w-96 mb-6" />
      ) : (
        <div className="flex justify-between items-center mb-6">{titleContent}</div>
      )}
      <div className="grid grid-cols-1 @xl:grid-cols-2 gap-4">
        <Card className="h-80">
          <Tabs value={selectedTab} className="h-full flex flex-col">
            <CardHeader className="h-10 py-0 pl-4 pr-2 flex flex-row items-center justify-between flex-0">
              <TabsList className="flex justify-start rounded-none gap-x-4 border-b-0 !mt-0 pt-0">
                <TabsTrigger
                  value="security"
                  onClick={() => setSelectedTab('security')}
                  className="flex items-center gap-2 text-xs py-3 border-b-[1px] font-mono uppercase"
                >
                  Security{' '}
                  {securityErrorCount + securityWarningCount > 0 && (
                    <div className="rounded bg-warning text-warning-100 px-1">
                      {securityErrorCount + securityWarningCount}
                    </div>
                  )}
                </TabsTrigger>
                <TabsTrigger
                  value="performance"
                  onClick={() => setSelectedTab('performance')}
                  className="flex items-center gap-2 text-xs py-3 border-b-[1px] font-mono uppercase"
                >
                  Performance{' '}
                  {performanceErrorCount + performanceWarningCount > 0 && (
                    <div className="rounded bg-warning text-warning-100 px-1">
                      {performanceErrorCount + performanceWarningCount}
                    </div>
                  )}
                </TabsTrigger>
              </TabsList>
              <ButtonTooltip
                asChild
                type="text"
                className="!mt-0 w-7"
                icon={<ExternalLink />}
                tooltip={{
                  content: {
                    side: 'bottom',
                    text: `Open ${selectedTab} Advisor`,
                    className: 'capitalize',
                  },
                }}
              >
                <Link href={`/project/${projectRef}/advisors/${selectedTab}`} />
              </ButtonTooltip>
            </CardHeader>
            <CardContent className="!p-0 mt-0 flex-1 overflow-y-auto">
              <TabsContent value="security" className="p-0 mt-0 h-full">
                {renderLintTabContent(
                  'Security',
                  securityLints,
                  securityErrorCount,
                  securityWarningCount,
                  isLoadingLints
                )}
              </TabsContent>
              <TabsContent value="performance" className="p-0 mt-0 h-full">
                {renderLintTabContent(
                  'Performance',
                  performanceLints,
                  performanceErrorCount,
                  performanceWarningCount,
                  isLoadingLints
                )}
              </TabsContent>
            </CardContent>
          </Tabs>
        </Card>

        <Card className="h-80 flex flex-col">
          <CardHeader className="h-10 flex-row items-center justify-between py-0 pl-4 pr-2">
            <CardTitle>Slow Queries</CardTitle>
            <ButtonTooltip
              asChild
              type="text"
              className="!mt-0 w-7"
              icon={<ExternalLink />}
              tooltip={{
                content: {
                  side: 'bottom',
                  text: `Open Query Performance Advisor`,
                },
              }}
            >
              <Link href={`/project/${projectRef}/reports/query-performance`} />
            </ButtonTooltip>
          </CardHeader>
          <CardContent className="!p-0 flex-1 overflow-y-auto">
            {isLoadingSlowestQueries ? (
              <div className="space-y-2 p-4">
                <ShimmeringLoader />
                <ShimmeringLoader className="w-3/4" />
                <ShimmeringLoader className="w-1/2" />
                <ShimmeringLoader className="w-3/4" />
                <ShimmeringLoader className="w-1/2" />
              </div>
            ) : top5SlowestQueries.length === 0 ? (
              <div className="flex-1 flex flex-col h-full items-center justify-center gap-2">
                <Activity strokeWidth={1.5} size={20} className="text-foreground-muted" />
                <p className="text-sm text-foreground-light">
                  No slow queries found in the selected period
                </p>
              </div>
            ) : (
              <Table className="text-xs font-mono max-w-full">
                <TableHeader>
                  <TableRow>
                    <TableHead className="text-foreground-lighter truncate py-2 h-auto">
                      Query
                    </TableHead>
                    <TableHead className="text-foreground-lighter truncate py-2 h-auto">
                      Avg time
                    </TableHead>
                    <TableHead className="text-foreground-lighter truncate py-2 h-auto">
                      Calls
                    </TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {/* Added explicit types for map parameters */}
                  {top5SlowestQueries.map((query: SlowQuery, i: number) => (
                    <TableRow key={i} className="py-2">
                      <TableCell className="font-mono truncate max-w-xs">{query.query}</TableCell>

                      <TableCell className="font-mono truncate max-w-xs">
                        {typeof query.mean_time === 'number'
                          ? `${(query.mean_time / 1000).toFixed(2)}s`
                          : 'N/A'}
                      </TableCell>
                      <TableCell className="font-mono truncate max-w-xs">{query.calls}</TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            )}
          </CardContent>
        </Card>
      </div>
    </div>
  )
}

Subdomains

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free