Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import { LogOut, ShieldAlert, User } from "lucide-react";

import { useTRPC } from "@/trpc/client";
import { AppSidebar } from "@/components/app-sidebar";
import { TeamSelector } from "@/components/team-selector";
import { EnvironmentSelector } from "@/components/environment-selector";
import { ThemeToggle } from "@/components/theme-toggle";
import { ChangePasswordDialog } from "@/components/change-password-dialog";
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar";
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
Expand Down Expand Up @@ -133,7 +136,10 @@ export default function DashboardLayout({
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-14 shrink-0 items-center gap-2 border-b px-4">
<header className="flex h-14 shrink-0 items-center gap-3 border-b px-4">
<TeamSelector />
<Separator orientation="vertical" className="!h-5" />
<EnvironmentSelector />
Comment on lines +139 to +142
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TeamSelector returns null when teams.length === 0 (which occurs during the initial load, before teamsQuery.data arrives). During that window, the header renders [null] | Separator | [EnvironmentSelector skeleton] — the separator appears to float with nothing on its left side.

The isTeamless guard only activates after teamsQuery.isSuccess && teams.length === 0, so the full sidebar layout (including this header) is visible while teams are still loading.

Consider one of these fixes:

Option 1: Conditionally render the separator only when TeamSelector produces content

<TeamSelector />
{teams.length > 0 ? (
  <Separator orientation="vertical" className="!h-5" />
) : null}
<EnvironmentSelector />

Option 2: Add a loading skeleton to TeamSelector (mirroring EnvironmentSelector) so it occupies space during load, making the transition smoother.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/(dashboard)/layout.tsx
Line: 139-142

Comment:
`TeamSelector` returns `null` when `teams.length === 0` (which occurs during the initial load, before `teamsQuery.data` arrives). During that window, the header renders `[null] | Separator | [EnvironmentSelector skeleton]` — the separator appears to float with nothing on its left side.

The `isTeamless` guard only activates after `teamsQuery.isSuccess && teams.length === 0`, so the full sidebar layout (including this header) is visible while teams are still loading.

Consider one of these fixes:

**Option 1: Conditionally render the separator only when TeamSelector produces content**
```tsx
<TeamSelector />
{teams.length > 0 ? (
  <Separator orientation="vertical" className="!h-5" />
) : null}
<EnvironmentSelector />
```

**Option 2: Add a loading skeleton to TeamSelector** (mirroring `EnvironmentSelector`) so it occupies space during load, making the transition smoother.

How can I resolve this? If you propose a fix, please make it concise.

<div className="ml-auto flex items-center gap-4">
<ThemeToggle />
<DropdownMenu>
Expand Down
48 changes: 1 addition & 47 deletions src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ import {
Settings,
ChevronsLeft,
ChevronsRight,
Users,
} from "lucide-react";
import { useTRPC } from "@/trpc/client";
import { TeamSelector } from "@/components/team-selector";
import { EnvironmentSelector } from "@/components/environment-selector";
import { Separator } from "@/components/ui/separator";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useTeamStore } from "@/stores/team-store";
import { useEnvironmentStore } from "@/stores/environment-store";

Expand Down Expand Up @@ -85,8 +80,7 @@ export function AppSidebar() {

return (
<Sidebar collapsible="icon">
<SidebarHeader className="p-0 group-data-[collapsible=icon]:p-0">
{/* Logo row — matches the h-14 main content header for border alignment */}
<SidebarHeader className="p-0">
<div className="flex h-14 items-center px-4 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-2">
<Link href="/" className="flex items-center gap-2">
<span className="text-xl tracking-tight group-data-[collapsible=icon]:hidden">
Expand All @@ -97,46 +91,6 @@ export function AppSidebar() {
</Link>
</div>
<Separator />
{/* Context selectors */}
<div className="space-y-1.5 p-3 group-data-[collapsible=icon]:hidden">
<TeamSelector />
<EnvironmentSelector />
</div>
{/* Collapsed mode: icon buttons with popovers */}
<div className="hidden group-data-[collapsible=icon]:flex flex-col items-center gap-1 py-2">
<Popover>
<Tooltip>
<TooltipTrigger asChild>
<PopoverTrigger asChild>
<button className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground" aria-label="Select team">
<Users className="h-4 w-4" />
</button>
</PopoverTrigger>
</TooltipTrigger>
<TooltipContent side="right">Team</TooltipContent>
</Tooltip>
<PopoverContent side="right" align="start" className="w-56 p-3">
<p className="mb-2 text-xs font-medium text-muted-foreground">Team</p>
<TeamSelector />
</PopoverContent>
</Popover>
<Popover>
<Tooltip>
<TooltipTrigger asChild>
<PopoverTrigger asChild>
<button className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground" aria-label="Select environment">
<Layers className="h-4 w-4" />
</button>
</PopoverTrigger>
</TooltipTrigger>
<TooltipContent side="right">Environment</TooltipContent>
</Tooltip>
<PopoverContent side="right" align="start" className="w-56 p-3">
<p className="mb-2 text-xs font-medium text-muted-foreground">Environment</p>
<EnvironmentSelector />
</PopoverContent>
</Popover>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/components/environment-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function EnvironmentSelector() {
}, [selectedEnvironmentId, systemEnvironment, setIsSystemEnvironment]);

if (envsQuery.isLoading) {
return <Skeleton className="h-8 w-full" />;
return <Skeleton className="h-8 w-[160px]" />;
}

if (environments.length === 0 && !systemEnvironment) {
Expand All @@ -94,7 +94,7 @@ export function EnvironmentSelector() {
value={selectedEnvironmentId ?? ""}
onValueChange={handleEnvironmentChange}
>
<SelectTrigger className="h-8 w-full text-xs">
<SelectTrigger className="h-8 w-[160px] text-xs">
<Layers className="mr-1.5 h-3.5 w-3.5 text-muted-foreground" />
<SelectValue placeholder="Select environment" />
</SelectTrigger>
Expand Down
2 changes: 1 addition & 1 deletion src/components/team-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function TeamSelector() {

return (
<Select value={selectedTeamId ?? undefined} onValueChange={setSelectedTeamId}>
<SelectTrigger className="w-full h-8 text-xs">
<SelectTrigger className="h-8 w-[160px] text-xs">
<Users className="mr-1.5 h-3.5 w-3.5 text-muted-foreground" />
<SelectValue placeholder="Select team" />
</SelectTrigger>
Expand Down
Loading