Files
supabase/apps/studio/components/layouts/Navigation/NavigationBar/OrgSelector.tsx
Gildas Garcia 243e079a2c chore: remove _Shadcn_ suffix from Command components (#46153)
## Problem

The `_Shadcn_` suffix isn't needed anymore on `Command` components

## Solution

- Remove the `_Shadcn_` suffix
- Simplify UI package exports
- Apply prettier

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Simplified command component imports and exports across the UI library
by removing internal naming aliases and adopting direct component
references. Updated the public UI package barrel export to use wildcard
re-exports for cleaner API surface.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46153?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 -->
2026-05-20 15:45:32 +02:00

173 lines
6.3 KiB
TypeScript

import { useBreakpoint, useParams } from 'common'
import { Boxes, ChevronsUpDown, Plus } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
Popover,
PopoverContent,
PopoverTrigger,
ScrollArea,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { OrgSelectorSheet } from './OrgSelectorSheet'
import { OrgCommandItem } from '@/components/layouts/AppLayout/OrgCommandItem'
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
import { useOrgProjectsInfiniteQuery } from '@/data/projects/org-projects-infinite-query'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
export function OrgSelector() {
const router = useRouter()
const { slug: routeSlug } = useParams()
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const { data: organizations, isPending: isLoadingOrganizations } = useOrganizationsQuery()
const organizationCreationEnabled = useIsFeatureEnabled('organizations:create')
const [open, setOpen] = useState(false)
const slug = selectedOrganization?.slug
const isPlatformOrg = selectedOrganization?.plan?.id === 'platform'
const selectedOrgInitial = selectedOrganization?.name?.trim().charAt(0).toUpperCase() || 'O'
const { data: projects } = useOrgProjectsInfiniteQuery(
{ slug, limit: 1 },
{ enabled: Boolean(slug) && !isPlatformOrg }
)
const numProjects = projects?.pages[0]?.pagination.count
const projectsLabel = isPlatformOrg
? 'Platform'
: typeof numProjects === 'number'
? `${numProjects} project${numProjects === 1 ? '' : 's'}`
: 'No projects'
const isMobile = useBreakpoint('md')
if (isLoadingOrganizations) return <ShimmeringLoader className="ml-1 w-[120px]" />
const triggerButton = (
<SidebarMenuButton
size="lg"
className="data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground gap-2 h-auto text-left group px-1.5 py-1 touch-manipulation"
onClick={isMobile ? () => setOpen(true) : undefined}
>
<span className="flex w-8 aspect-square shrink-0 items-center justify-center rounded-sm border bg-surface-100 text-xs font-medium text-foreground-lighter">
{selectedOrgInitial}
</span>
<div className="flex min-w-0 flex-1 flex-col text-left -mb-0.5">
<div className="truncate text-foreground font-medium leading-tight min-w-[100px] max-w-[250px]">
{selectedOrganization?.name ?? 'Select organization'}
</div>
{selectedOrganization && (
<div className="flex items-center gap-1 truncate text-foreground-light leading-tight text-xs">
<Boxes className="shrink-0 size-3" strokeWidth={1.5} />
<span>{projectsLabel}</span>
</div>
)}
</div>
<ChevronsUpDown
strokeWidth={1}
className="ml-auto text-foreground-light md:hidden md:group-hover:block w-4! h-4!"
/>
</SidebarMenuButton>
)
if (isMobile) {
return (
<>
<SidebarMenu className="shrink">
<SidebarMenuItem>
{isLoadingOrganizations ? <ShimmeringLoader className="p-2 w-[90px]" /> : triggerButton}
</SidebarMenuItem>
</SidebarMenu>
<OrgSelectorSheet
open={open}
onOpenChange={setOpen}
onClose={() => setOpen(false)}
selectedOrganization={selectedOrganization ?? null}
/>
</>
)
}
return (
<SidebarMenu>
<SidebarMenuItem>
<Popover open={open} onOpenChange={setOpen} modal={false}>
<PopoverTrigger asChild>{triggerButton}</PopoverTrigger>
<PopoverContent className="p-0" side="bottom" align="start">
<Command>
<CommandInput placeholder="Find organization..." />
<CommandList>
<CommandEmpty>No organizations found</CommandEmpty>
<CommandGroup>
<ScrollArea
className={(organizations || []).length > 7 ? 'h-full md:h-[210px]' : ''}
>
{organizations?.map((org) => (
<OrgCommandItem
key={org.slug}
org={org}
selectedSlug={slug}
routePathname={router.pathname}
hasRouteSlug={!!routeSlug}
onClose={() => setOpen(false)}
/>
))}
</ScrollArea>
</CommandGroup>
<CommandSeparator />
<CommandGroup>
<CommandItem
className="cursor-pointer w-full"
onSelect={() => {
setOpen(false)
router.push('/organizations')
}}
onClick={() => setOpen(false)}
>
<Link href="/organizations" className="flex items-center gap-2 w-full">
<p>All Organizations</p>
</Link>
</CommandItem>
</CommandGroup>
{organizationCreationEnabled && (
<>
<CommandSeparator />
<CommandGroup>
<CommandItem
className="cursor-pointer w-full"
onSelect={() => {
setOpen(false)
router.push('/new')
}}
onClick={() => setOpen(false)}
>
<Link href="/new" className="flex items-center gap-2 w-full">
<Plus size={14} strokeWidth={1.5} />
<p>New organization</p>
</Link>
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
</SidebarMenuItem>
</SidebarMenu>
)
}