mirror of
https://github.com/supabase/supabase.git
synced 2026-05-31 09:52:58 +08:00
## Summary Adds discoverable keyboard shortcuts for the Advisors area, covering both navigation between advisor sub-pages and in-page actions on the Security/Performance Advisor pages. Built on the shared shortcut registry (`apps/studio/state/shortcuts/`) so they show up in the command menu and follow the existing chord conventions (`V` for adVisor, mirroring auth-nav / database-nav). Linear: [FE-3413](https://linear.app/supabase/issue/FE-3413) ### Shortcuts | Shortcut | Action | Scope | | --- | --- | --- | | `V` then `S` | Go to Security Advisor | Anywhere under `/project/<ref>/advisors/*` | | `V` then `P` | Go to Performance Advisor | Anywhere under `/project/<ref>/advisors/*` | | `V` then `R` | Go to Advisor Settings (Rules) | Anywhere under `/project/<ref>/advisors/*` | | `1` | Switch to Errors tab | Security / Performance Advisor page | | `2` | Switch to Warnings tab | Security / Performance Advisor page | | `3` | Switch to Info tab | Security / Performance Advisor page | | `Shift+R` | Refresh / rerun the advisor | Security / Performance Advisor page | | `Escape` | Close lint details panel | When a lint row is selected | ## Test plan - [x] From anywhere in Advisors, `V S` / `V P` / `V R` route to Security / Performance / Rules - [x] On Security and Performance Advisor pages, `1` / `2` / `3` switch tabs and update the `preset` query param - [x] `Shift+R` reruns the linter (disabled while a refresh is in-flight) - [x] `Escape` closes the lint details side panel when a lint is selected - [x] Digit shortcuts do not fire while typing in inputs (`ignoreInputs: true`) - [x] Shortcuts appear in the command menu under the Advisors group <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added keyboard shortcuts for Advisors (tab navigation, refresh/rerun, close detail) with visible shortcut hints on tabs, refresh/rerun buttons, close controls, and the Advisors menu; pages wire shortcuts to tab switching, refresh, and close actions. * **Chores** * Registered Advisors shortcuts globally and added an Advisors navigation group for discovery in the shortcuts reference. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46238?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
128 lines
4.2 KiB
TypeScript
128 lines
4.2 KiB
TypeScript
import { InformationCircleIcon } from '@heroicons/react/16/solid'
|
|
import { MessageSquareMore } from 'lucide-react'
|
|
import { useRouter } from 'next/router'
|
|
import {
|
|
cn,
|
|
Tabs_Shadcn_,
|
|
TabsList_Shadcn_,
|
|
TabsTrigger_Shadcn_,
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from 'ui'
|
|
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
|
|
|
import { LINT_TABS, LINTER_LEVELS } from '@/components/interfaces/Linter/Linter.constants'
|
|
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
|
|
import { Lint } from '@/data/lint/lint-query'
|
|
|
|
interface LintPageTabsProps {
|
|
currentTab: string
|
|
setCurrentTab: (value: LINTER_LEVELS) => void
|
|
isLoading: boolean
|
|
activeLints: Lint[]
|
|
}
|
|
const LintPageTabs = ({ currentTab, setCurrentTab, isLoading, activeLints }: LintPageTabsProps) => {
|
|
const router = useRouter()
|
|
|
|
const warnLintsCount = activeLints.filter((x) => x.level === 'WARN').length
|
|
const errorLintsCount = activeLints.filter((x) => x.level === 'ERROR').length
|
|
const infoLintsCount = activeLints.filter((x) => x.level === 'INFO').length
|
|
|
|
const LintCountLabel = ({ tab }: { tab: (typeof LINT_TABS)[number] }) => {
|
|
let count = 0
|
|
let label = ''
|
|
if (tab.id === LINTER_LEVELS.ERROR) {
|
|
count = errorLintsCount
|
|
label = 'errors'
|
|
}
|
|
|
|
if (tab.id === LINTER_LEVELS.WARN) {
|
|
count = warnLintsCount
|
|
label = 'warnings'
|
|
}
|
|
|
|
if (tab.id === LINTER_LEVELS.INFO) {
|
|
count = infoLintsCount
|
|
label = 'suggestions'
|
|
}
|
|
|
|
return (
|
|
<span className="text-xs text-foreground-muted group-hover:text-foreground-lighter group-data-[state=active]:text-foreground-lighter transition">
|
|
{isLoading ? (
|
|
<ShimmeringLoader className="w-20 pt-1" />
|
|
) : (
|
|
<>
|
|
{count} {label}
|
|
</>
|
|
)}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Tabs_Shadcn_
|
|
value={currentTab}
|
|
onValueChange={(value) => {
|
|
setCurrentTab(value as LINTER_LEVELS)
|
|
const { sort, search, ...rest } = router.query
|
|
router.push({ ...router, query: { ...rest, preset: value, id: null } })
|
|
}}
|
|
>
|
|
<TabsList_Shadcn_ className={cn('flex gap-0 border-0 items-end z-10 relative')}>
|
|
{LINT_TABS.map((tab) => (
|
|
<ShortcutTooltip
|
|
key={tab.id}
|
|
shortcutId={tab.shortcutId}
|
|
label={`Switch to ${tab.label}`}
|
|
side="top"
|
|
align="start"
|
|
>
|
|
<TabsTrigger_Shadcn_
|
|
value={tab.id}
|
|
className={cn(
|
|
'group relative',
|
|
'px-6 py-3 border-b-0 flex flex-col items-start shadow-none! border-default border-t',
|
|
'even:border-x last:border-r even:border-x-strong! last:border-r-strong!',
|
|
tab.id === currentTab ? 'bg-surface-200!' : 'bg-surface-200/33!',
|
|
'hover:bg-surface-100!',
|
|
'data-[state=active]:bg-surface-200!',
|
|
'hover:text-foreground-light',
|
|
'transition'
|
|
)}
|
|
>
|
|
{tab.id === currentTab && (
|
|
<div className="absolute top-0 left-0 w-full h-px bg-foreground" />
|
|
)}
|
|
<div className="flex items-center gap-x-2">
|
|
<span
|
|
className={
|
|
tab.id === LINTER_LEVELS.ERROR
|
|
? 'text-destructive-600'
|
|
: tab.id === LINTER_LEVELS.WARN
|
|
? 'text-warning'
|
|
: 'text-brand-500'
|
|
}
|
|
>
|
|
<MessageSquareMore size={14} fill="currentColor" strokeWidth={0} />
|
|
</span>
|
|
|
|
<span className="">{tab.label}</span>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<InformationCircleIcon className="transition text-foreground-muted w-3 h-3 data-[state=delayed-open]:text-foreground-light" />
|
|
</TooltipTrigger>
|
|
<TooltipContent side="top">{tab.description}</TooltipContent>
|
|
</Tooltip>
|
|
</div>
|
|
<LintCountLabel tab={tab} />
|
|
</TabsTrigger_Shadcn_>
|
|
</ShortcutTooltip>
|
|
))}
|
|
</TabsList_Shadcn_>
|
|
</Tabs_Shadcn_>
|
|
)
|
|
}
|
|
|
|
export default LintPageTabs
|