Skip to content

Commit 3e45f78

Browse files
authored
Dashboard: Org menu improvements for admins (#1969)
* Adds path for admin route * Updates impersonation banner and show new org button if only 1 org * Simpler UI for displaying admin access
1 parent 04b9973 commit 3e45f78

File tree

3 files changed

+55
-23
lines changed

3 files changed

+55
-23
lines changed

apps/webapp/app/components/ImpersonationBanner.tsx

+20-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import { UserMinusIcon } from "@heroicons/react/20/solid";
22
import { Form } from "@remix-run/react";
33
import { Button } from "./primitives/Buttons";
4+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/Tooltip";
45

56
export function ImpersonationBanner() {
67
return (
7-
<div className="w-full">
8-
<Form action="/resources/impersonation" method="delete" reloadDocument className="w-full">
9-
<Button
10-
type="submit"
11-
variant="small-menu-item"
12-
LeadingIcon={UserMinusIcon}
13-
fullWidth
14-
textAlignLeft
15-
className="text-amber-400"
16-
>
17-
Stop impersonating
18-
</Button>
8+
<div>
9+
<Form action="/resources/impersonation" method="delete" reloadDocument>
10+
<TooltipProvider disableHoverableContent={true}>
11+
<Tooltip>
12+
<TooltipTrigger>
13+
<Button
14+
type="submit"
15+
variant="small-menu-item"
16+
LeadingIcon={UserMinusIcon}
17+
fullWidth
18+
textAlignLeft
19+
className="text-amber-400"
20+
/>
21+
</TooltipTrigger>
22+
<TooltipContent side="bottom" className={"text-xs"}>
23+
Stop impersonating
24+
</TooltipContent>
25+
</Tooltip>
26+
</TooltipProvider>
1927
</Form>
2028
</div>
2129
);

apps/webapp/app/components/navigation/SideMenu.tsx

+31-11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
RectangleStackIcon,
1717
ServerStackIcon,
1818
Squares2X2Icon,
19+
UsersIcon,
1920
} from "@heroicons/react/20/solid";
2021
import { useLocation, useNavigation } from "@remix-run/react";
2122
import { useEffect, useRef, useState, type ReactNode } from "react";
@@ -28,12 +29,14 @@ import { Avatar } from "~/components/primitives/Avatar";
2829
import { type MatchedEnvironment } from "~/hooks/useEnvironment";
2930
import { type MatchedOrganization } from "~/hooks/useOrganizations";
3031
import { type MatchedProject } from "~/hooks/useProject";
32+
import { useHasAdminAccess } from "~/hooks/useUser";
3133
import { type User } from "~/models/user.server";
3234
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
3335
import { type FeedbackType } from "~/routes/resources.feedback";
3436
import { cn } from "~/utils/cn";
3537
import {
3638
accountPath,
39+
adminPath,
3740
logoutPath,
3841
newOrganizationPath,
3942
newProjectPath,
@@ -110,6 +113,7 @@ export function SideMenu({
110113
const currentPlan = useCurrentPlan();
111114
const { isConnected } = useDevPresence();
112115
const isFreeUser = currentPlan?.v3Subscription?.isPaying === false;
116+
const isAdmin = useHasAdminAccess();
113117

114118
useEffect(() => {
115119
const handleScroll = () => {
@@ -143,6 +147,20 @@ export function SideMenu({
143147
project={project}
144148
user={user}
145149
/>
150+
{isAdmin && !user.isImpersonating ? (
151+
<TooltipProvider disableHoverableContent={true}>
152+
<Tooltip>
153+
<TooltipTrigger>
154+
<LinkButton variant="minimal/medium" to={adminPath()} TrailingIcon={UsersIcon} />
155+
</TooltipTrigger>
156+
<TooltipContent side="bottom" className={"text-xs"}>
157+
Admin dashboard
158+
</TooltipContent>
159+
</Tooltip>
160+
</TooltipProvider>
161+
) : isAdmin && user.isImpersonating ? (
162+
<ImpersonationBanner />
163+
) : null}
146164
</div>
147165
<div
148166
className="overflow-hidden overflow-y-auto pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
@@ -325,10 +343,7 @@ function ProjectSelector({
325343
<PopoverArrowTrigger
326344
isOpen={isOrgMenuOpen}
327345
overflowHidden
328-
className={cn(
329-
"h-8 w-full justify-between py-1 pl-1.5",
330-
user.isImpersonating && "border border-dashed border-amber-400"
331-
)}
346+
className="h-8 w-full justify-between py-1 pl-1.5"
332347
>
333348
<span className="flex items-center gap-1.5 overflow-hidden">
334349
<Avatar avatar={organization.avatar} size={1.25} orgName={organization.title} />
@@ -412,19 +427,24 @@ function ProjectSelector({
412427
<PopoverMenuItem to={newProjectPath(organization)} title="New project" icon={PlusIcon} />
413428
</div>
414429
<div className="border-t border-charcoal-700 p-1">
415-
<SwitchOrganizations organizations={organizations} organization={organization} />
430+
{organizations.length > 1 ? (
431+
<SwitchOrganizations organizations={organizations} organization={organization} />
432+
) : (
433+
<PopoverMenuItem
434+
to={newOrganizationPath()}
435+
title="New organization"
436+
icon={PlusIcon}
437+
leadingIconClassName="text-text-dimmed"
438+
/>
439+
)}
416440
</div>
417441
<div className="border-t border-charcoal-700 p-1">
418442
<PopoverMenuItem
419443
to={accountPath()}
420444
title="Account"
421445
icon={UserProfilePhoto}
422-
leadingIconClassName={cn(
423-
"text-text-dimmed rounded-full border border-transparent",
424-
user.isImpersonating && "rounded-full border-yellow-500"
425-
)}
446+
leadingIconClassName="text-text-dimmed rounded-full border border-transparent"
426447
/>
427-
{user.isImpersonating && <ImpersonationBanner />}
428448
</div>
429449
<div className="border-t border-charcoal-700 p-1">
430450
<PopoverMenuItem
@@ -519,7 +539,7 @@ function SwitchOrganizations({
519539
<div className="border-t border-charcoal-700 p-1">
520540
<PopoverMenuItem
521541
to={newOrganizationPath()}
522-
title="New Organization"
542+
title="New organization"
523543
icon={PlusIcon}
524544
leadingIconClassName="text-text-dimmed"
525545
/>

apps/webapp/app/utils/pathBuilder.ts

+4
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,7 @@ export function docsPath(path: string) {
430430
export function docsTroubleshootingPath(path: string) {
431431
return `${docsRoot()}/v3/troubleshooting`;
432432
}
433+
434+
export function adminPath() {
435+
return `/@`;
436+
}

0 commit comments

Comments
 (0)