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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mise.toml
# Next.js
.next
out
next-env.d.ts

# Superset
# Ignore .superset directory except for config and scripts
Expand Down
6 changes: 0 additions & 6 deletions apps/admin/next-env.d.ts

This file was deleted.

4 changes: 1 addition & 3 deletions apps/admin/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { NextConfig } from "next";

const config: NextConfig = {
experimental: {
reactCompiler: true,
},
reactCompiler: true,
typescript: { ignoreBuildErrors: true },
};

Expand Down
10 changes: 5 additions & 5 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
"@trpc/tanstack-react-query": "^11.7.1",
"geist": "^1.5.1",
"lucide-react": "^0.560.0",
"next": "^15.5.7",
"next": "^16.0.10",
"next-themes": "^0.4.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"server-only": "^0.0.1",
"superjson": "^2.2.5",
"zod": "^4.1.13"
Expand All @@ -36,8 +36,8 @@
"@superset/typescript": "workspace:*",
"@tailwindcss/postcss": "^4.0.9",
"@types/node": "^24.9.1",
"@types/react": "^19.1.11",
"@types/react-dom": "^19.1.7",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"babel-plugin-react-compiler": "^1.0.0",
"tailwindcss": "^4.0.9",
"typescript": "^5.9.3"
Expand Down
Binary file added apps/admin/public/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
177 changes: 177 additions & 0 deletions apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
"use client";

import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@superset/ui/collapsible";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from "@superset/ui/sidebar";
import {
BarChart3,
Bot,
ChevronRight,
Database,
Home,
Settings,
Shield,
Users,
Webhook,
} from "lucide-react";

import type { User } from "@/lib/auth/types";
import { AppSidebarHeader } from "./components/AppSidebarHeader";
import { NavUser } from "./components/NavUser";
import { SearchForm } from "./components/SearchForm";

const navigation = [
{
title: "Overview",
items: [
{
title: "Dashboard",
url: "/",
icon: Home,
},
],
},
{
title: "User Management",
items: [
{
title: "All Users",
url: "/users",
icon: Users,
},
{
title: "Deleted Users",
url: "/users/deleted",
},
{
title: "Permissions",
url: "/users/permissions",
icon: Shield,
},
],
},
{
title: "Analytics",
items: [
{
title: "Overview",
url: "/analytics",
icon: BarChart3,
},
{
title: "User Activity",
url: "/analytics/activity",
},
{
title: "Performance",
url: "/analytics/performance",
},
],
},
{
title: "AI Lab",
items: [
{
title: "Plan Testing",
url: "/ai-lab",
icon: Bot,
},
{
title: "Model Config",
url: "/ai-lab/models",
},
],
},
{
title: "System",
items: [
{
title: "Database",
url: "/system/database",
icon: Database,
},
{
title: "Webhooks",
url: "/system/webhooks",
icon: Webhook,
},
{
title: "Settings",
url: "/settings",
icon: Settings,
},
],
},
];

export interface AppSidebarProps extends React.ComponentProps<typeof Sidebar> {
user: User;
}

export function AppSidebar({ user, ...props }: AppSidebarProps) {
return (
<Sidebar {...props}>
<SidebarHeader>
<AppSidebarHeader />
<SearchForm />
</SidebarHeader>
<SidebarContent className="gap-0">
{navigation.map((section) => (
<Collapsible
key={section.title}
title={section.title}
defaultOpen
className="group/collapsible"
>
<SidebarGroup>
<SidebarGroupLabel
asChild
className="group/label text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground text-sm"
>
<CollapsibleTrigger>
{section.title}
<ChevronRight className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent>
<SidebarMenu>
{section.items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
{item.icon && <item.icon className="size-4" />}
{item.title}
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</CollapsibleContent>
</SidebarGroup>
</Collapsible>
))}
</SidebarContent>
<SidebarFooter>
<NavUser user={user} />
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@superset/ui/sidebar";
import Image from "next/image";

export function AppSidebarHeader() {
return (
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<a href="/">
<Image
src="/icon.png"
alt="Superset"
width={32}
height={32}
className="size-8 rounded-lg"
/>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-medium">Superset</span>
<span className="">Admin Panel</span>
</div>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AppSidebarHeader } from "./AppSidebarHeader";
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use client";

import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@superset/ui/dropdown-menu";
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@superset/ui/sidebar";
import {
BadgeCheck,
Bell,
ChevronsUpDown,
LogOut,
Settings,
} from "lucide-react";
import { useSignOut } from "@/lib/auth/client";
import type { User } from "@/lib/auth/types";

export interface NavUserProps {
user: User;
}

export function NavUser({ user }: NavUserProps) {
const { isMobile } = useSidebar();
const { signOut } = useSignOut();

const userInitials = user.name
.split(" ")
.map((name) => name[0])
.join("");
Comment on lines +37 to +40
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle edge cases in userInitials calculation.

The current implementation could throw if user.name contains empty strings after splitting (e.g., "John Doe" with double space) or if name is empty.

Apply this diff to add defensive handling:

 	const userInitials = user.name
+		.trim()
 		.split(" ")
+		.filter((name) => name.length > 0)
 		.map((name) => name[0])
+		.slice(0, 2)
 		.join("");

This ensures:

  • Trimmed whitespace
  • Empty segments filtered out
  • Maximum 2 initials (common UX pattern)
  • No crash on edge case names
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const userInitials = user.name
.split(" ")
.map((name) => name[0])
.join("");
const userInitials = user.name
.trim()
.split(" ")
.filter((name) => name.length > 0)
.map((name) => name[0])
.slice(0, 2)
.join("");
🤖 Prompt for AI Agents
In
apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx
around lines 37 to 40, the userInitials computation must be made defensive: trim
the full name, split on whitespace, filter out any empty segments, map to the
first character of each non-empty segment, take at most the first two initials,
and join them; also handle an empty or missing name by returning an empty string
or a sensible fallback (e.g., "?") so the code cannot throw on edge-case names
like "" or "John  Doe".


return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.imageUrl} alt={user.name} />
<AvatarFallback className="rounded-lg">
{userInitials}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.imageUrl} alt={user.name} />
<AvatarFallback className="rounded-lg">
{userInitials}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<Settings />
Settings
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={signOut}>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { NavUser } from "./NavUser";
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import { Label } from "@superset/ui/label";
import {
SidebarGroup,
SidebarGroupContent,
SidebarInput,
} from "@superset/ui/sidebar";
import { Search } from "lucide-react";

export function SearchForm({ ...props }: React.ComponentProps<"form">) {
return (
<form {...props}>
<SidebarGroup className="py-0">
<SidebarGroupContent className="relative">
<Label htmlFor="search" className="sr-only">
Search
</Label>
<SidebarInput id="search" placeholder="Search..." className="pl-8" />
<Search className="pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" />
</SidebarGroupContent>
</SidebarGroup>
</form>
);
}
Loading