mirror of
https://github.com/supabase/supabase.git
synced 2026-06-13 01:39:53 +08:00
## What kind of change does this PR introduce? Feature. Resolves FE-3470. ## What is the current behavior? Organization surfaces have a `G then ,` shortcut to enter org settings, but once inside there is no keyboard navigation, sidebar tooltips, or action shortcuts for the various org pages. | Area | Current behaviour | | --- | --- | | Org Settings sidebar | Routes are click-only once users are inside Settings. | | OAuth Apps | Publish / confirm actions have no keyboard shortcuts. | | Private Apps | Create app has no keyboard shortcut. | | Team | Invite / send actions have no keyboard shortcuts. | | Integrations | Add project connection has no keyboard shortcut. | | Org Projects | New project and search have no keyboard shortcuts. | | Audit Logs | Refresh has no keyboard shortcut. | ## What is the new behavior? Mirrors the Project Settings shortcut pattern (#46352) across all Organization surfaces. | Area | New shortcut coverage | | --- | --- | | Org Settings sidebar | `S then G/C/S/A/P/W/L/D` for General, Security, SSO, OAuth apps, Private apps, Webhooks, Audit logs, Legal documents. Shortcut badge appears on hover in the sidebar. | | Org Settings entry | `G then ,` (remapped from `G then O`) to match the Project Settings chord. | | OAuth Apps | `Shift+N` opens Publish app panel; `Mod+Enter` confirms the open panel. | | Private Apps | `Shift+N` opens Create app sheet (works in both empty-state and list-state). | | Team | `Shift+N` opens Invite members dialog; `Mod+Enter` sends the invitation(s). | | Integrations | `Shift+N` triggers Add project connection when permitted. | | Org Projects | `Shift+N` navigates to new project; `Shift+F` focuses the search input. | | Audit Logs | `Shift+R` refreshes the log list. | ### Implementation notes - Threads `shortcutId` through the `WithSidebar` pipeline (`SidebarLink` → `SubMenuSection` → `ProductMenuGroup`) so tooltip display is automatic — no new rendering logic. - Layout-scoped chords mount only while `OrganizationSettingsLayout` is active, so `S then G` in org settings does not conflict with `S then G` in project settings. - Cheatsheet reference groups promoted to typed constants with readable labels (was: bare strings like `'org-oauth-apps'`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * System-wide keyboard shortcuts for org areas: project search & new project, private app creation, OAuth app publish/confirm, add GitHub integration, invite members (open/submit), and refresh audit logs. * Sidebar and product menu now show assigned shortcuts for faster navigation; org settings navigation shortcut remapped. * **Tests** * Added coverage for org shortcut registry behavior, sequences, and ordering. * **Chores** * New shortcut reference groups and ordering for improved discoverability. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46356?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: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Ali Waseem <waseema393@gmail.com>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import type { SidebarSection } from './AccountLayout.types'
|
|
import type { SubMenuSection } from '@/components/ui/ProductMenu/ProductMenu.types'
|
|
|
|
/**
|
|
* Converts AccountLayout SidebarSection[] to SubMenuSection[] for SubMenu/ProductMenu.
|
|
* Defensive: handles missing or malformed sections/links.
|
|
*/
|
|
export function toSubMenuSections(sections: SidebarSection[]): SubMenuSection[] {
|
|
if (!Array.isArray(sections)) return []
|
|
return sections
|
|
.filter((s): s is SidebarSection => s != null && typeof s === 'object')
|
|
.map((s) => ({
|
|
key: s.key ?? '',
|
|
heading: s.heading,
|
|
links: (s.links ?? [])
|
|
.filter((l) => l != null && typeof l === 'object' && l.key && l.label != null)
|
|
.map((l) => ({
|
|
key: l.key,
|
|
label: l.label,
|
|
href: l.href,
|
|
shortcutId: l.shortcutId,
|
|
})),
|
|
}))
|
|
.filter((s) => s.key || s.heading)
|
|
}
|
|
|
|
/**
|
|
* Returns the key of the first active link across all sections.
|
|
* Used to highlight the current page in SubMenu.
|
|
*/
|
|
export function getActiveKey(sections: SidebarSection[]): string | undefined {
|
|
if (!Array.isArray(sections)) return undefined
|
|
for (const section of sections) {
|
|
if (!section?.links || !Array.isArray(section.links)) continue
|
|
const active = section.links.find((l) => l?.isActive === true)
|
|
if (active?.key) return active.key
|
|
}
|
|
return undefined
|
|
}
|