1
0
Fork 0

feat(impersonation): open user picker from account menu

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jason Fraley 2026-09-01 17:17:57 -04:00
parent 7624327ab9
commit 39e13da41b
2 changed files with 43 additions and 14 deletions

View file

@ -274,15 +274,9 @@ export function AppSidebar({
<hr /> <hr />
<NavSecondary <NavSecondary
items={ items={
!adminUrl && !canImpersonate adminUrl
? data.navSecondary ? [...data.navSecondary, { title: "Admin", url: adminUrl, icon: Shield }]
: [ : data.navSecondary
...data.navSecondary,
...(adminUrl ? [{ title: "Admin", url: adminUrl, icon: Shield }] : []),
...(canImpersonate
? [{ title: "Impersonate", url: "/impersonate", icon: UserCog }]
: []),
]
} }
className="mt-auto" className="mt-auto"
/> />
@ -327,6 +321,7 @@ export function AppSidebar({
avatar: "", avatar: "",
}} }}
showCallsign={showCallsign} showCallsign={showCallsign}
canImpersonate={canImpersonate}
/> />
</SidebarFooter> </SidebarFooter>
</Sidebar> </Sidebar>

View file

@ -1,6 +1,6 @@
"use client"; "use client";
import { BadgeCheck, Bird, Bomb, ChevronsUpDown, FileTextIcon, LogOut } from "lucide-react"; import { BadgeCheck, Bird, Bomb, ChevronsUpDown, FileTextIcon, LogOut, Users2 } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@ -21,10 +21,14 @@ import {
SidebarMenuItem, SidebarMenuItem,
useSidebar, useSidebar,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import type { User } from "@/payload-types";
import { ImpersonationModal } from "@/components/frontend/impersonation/ImpersonationModal";
import { getImpersonationUsers } from "@/app/(frontend)/impersonate/actions";
export function NavUser({ export function NavUser({
user, user,
showCallsign = false, showCallsign = false,
canImpersonate = false,
}: { }: {
user: { user: {
displayName: string; displayName: string;
@ -32,10 +36,14 @@ export function NavUser({
avatar: string; avatar: string;
}; };
showCallsign?: boolean; showCallsign?: boolean;
canImpersonate?: boolean;
}) { }) {
const { isMobile } = useSidebar(); const { isMobile } = useSidebar();
const router = useRouter(); const router = useRouter();
const [loggingOut, setLoggingOut] = useState(false); const [loggingOut, setLoggingOut] = useState(false);
const [showImpersonationModal, setShowImpersonationModal] = useState(false);
const [impersonationUsers, setImpersonationUsers] = useState<any[]>([]);
const [loadingImpersonationUsers, setLoadingImpersonationUsers] = useState(false);
const primaryLine = showCallsign ? user.username : user.displayName; const primaryLine = showCallsign ? user.username : user.displayName;
const secondaryLine = showCallsign ? user.displayName : user.username; const secondaryLine = showCallsign ? user.displayName : user.username;
@ -53,6 +61,24 @@ export function NavUser({
router.refresh(); router.refresh();
}; };
const handleOpenImpersonationModal = async () => {
setLoadingImpersonationUsers(true);
try {
const users = await getImpersonationUsers();
setImpersonationUsers(users);
} catch (error) {
console.error("Failed to load impersonation users:", error);
} finally {
setLoadingImpersonationUsers(false);
}
setShowImpersonationModal(true);
};
const handleImpersonationStarted = async () => {
router.push("/");
router.refresh();
};
return ( return (
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
@ -122,10 +148,12 @@ export function NavUser({
Account Account
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
{/* <DropdownMenuItem> */} {canImpersonate && (
{/* <Bell /> */} <DropdownMenuItem onClick={handleOpenImpersonationModal}>
{/* Notifications */} <Users2 />
{/* </DropdownMenuItem> */} Impersonate
</DropdownMenuItem>
)}
</DropdownMenuGroup> </DropdownMenuGroup>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => handleLogout()}> <DropdownMenuItem onSelect={() => handleLogout()}>
@ -135,6 +163,12 @@ export function NavUser({
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</SidebarMenuItem> </SidebarMenuItem>
<ImpersonationModal
open={showImpersonationModal}
onOpenChange={setShowImpersonationModal}
users={impersonationUsers}
onStart={handleImpersonationStarted}
/>
</SidebarMenu> </SidebarMenu>
); );
} }