Skip to content

Side menu and Org dropdown improvements #1801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 18, 2025
4 changes: 2 additions & 2 deletions apps/webapp/app/components/ImpersonationBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { UserMinusIcon } from "@heroicons/react/20/solid";
import { Form } from "@remix-run/react";
import { Paragraph } from "./primitives/Paragraph";
import { Button } from "./primitives/Buttons";
import { UserMinusIcon } from "@heroicons/react/20/solid";

export function ImpersonationBanner() {
return (
Expand All @@ -13,6 +12,7 @@ export function ImpersonationBanner() {
LeadingIcon={UserMinusIcon}
fullWidth
textAlignLeft
className="text-amber-400"
>
Stop impersonating
</Button>
Expand Down
69 changes: 42 additions & 27 deletions apps/webapp/app/components/SetupCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "./primitives/ClientTabs";
import { ClipboardField } from "./primitives/ClipboardField";
import { Paragraph } from "./primitives/Paragraph";
import { Header3 } from "./primitives/Headers";

type PackageManagerContextType = {
activePackageManager: string;
Expand Down Expand Up @@ -61,7 +62,12 @@ function getApiUrlArg() {
return apiUrl ? `-a ${apiUrl}` : undefined;
}

export function InitCommandV3() {
// Add title prop to the component interfaces
type TabsProps = {
title?: string;
};

export function InitCommandV3({ title }: TabsProps) {
const project = useProject();
const projectRef = project.externalRef;
const apiUrlArg = getApiUrlArg();
Expand All @@ -77,30 +83,33 @@ export function InitCommandV3() {
value={activePackageManager}
onValueChange={setActivePackageManager}
>
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
<div className="flex items-center gap-4">
{title && <span>{title}</span>}
<ClientTabsList className={title ? "ml-auto" : ""}>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
</div>
<ClientTabsContent value={"npm"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`npx ${initCommand}`}
/>
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`pnpm dlx ${initCommand}`}
/>
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`yarn dlx ${initCommand}`}
Expand All @@ -110,7 +119,7 @@ export function InitCommandV3() {
);
}

export function TriggerDevStepV3() {
export function TriggerDevStepV3({ title }: TabsProps) {
const { activePackageManager, setActivePackageManager } = usePackageManager();

return (
Expand All @@ -119,30 +128,33 @@ export function TriggerDevStepV3() {
value={activePackageManager}
onValueChange={setActivePackageManager}
>
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
<div className="flex items-center gap-4">
{title && <Header3>{title}</Header3>}
<ClientTabsList className={title ? "ml-auto" : ""}>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
</div>
<ClientTabsContent value={"npm"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`npx trigger.dev@${v3PackageTag} dev`}
/>
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`pnpm dlx trigger.dev@${v3PackageTag} dev`}
/>
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`yarn dlx trigger.dev@${v3PackageTag} dev`}
Expand All @@ -152,7 +164,7 @@ export function TriggerDevStepV3() {
);
}

export function TriggerLoginStepV3() {
export function TriggerLoginStepV3({ title }: TabsProps) {
const { activePackageManager, setActivePackageManager } = usePackageManager();

return (
Expand All @@ -161,30 +173,33 @@ export function TriggerLoginStepV3() {
value={activePackageManager}
onValueChange={setActivePackageManager}
>
<ClientTabsList>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
<div className="flex items-center gap-4">
{title && <span>{title}</span>}
<ClientTabsList className={title ? "ml-auto" : ""}>
<ClientTabsTrigger value={"npm"}>npm</ClientTabsTrigger>
<ClientTabsTrigger value={"pnpm"}>pnpm</ClientTabsTrigger>
<ClientTabsTrigger value={"yarn"}>yarn</ClientTabsTrigger>
</ClientTabsList>
</div>
<ClientTabsContent value={"npm"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`npx trigger.dev@${v3PackageTag} login`}
/>
</ClientTabsContent>
<ClientTabsContent value={"pnpm"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`pnpm dlx trigger.dev@${v3PackageTag} login`}
/>
</ClientTabsContent>
<ClientTabsContent value={"yarn"}>
<ClipboardField
variant="primary/medium"
variant="secondary/medium"
iconButton
className="mb-4"
value={`yarn dlx trigger.dev@${v3PackageTag} login`}
Expand Down
7 changes: 3 additions & 4 deletions apps/webapp/app/components/navigation/EnvironmentSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { useNavigation } from "@remix-run/react";
import { useEffect, useState } from "react";
import { useEnvironmentSwitcher } from "~/hooks/useEnvironmentSwitcher";
import { useFeatures } from "~/hooks/useFeatures";
import { type MatchedOrganization } from "~/hooks/useOrganizations";
import { cn } from "~/utils/cn";
import { v3BillingPath } from "~/utils/pathBuilder";
import { EnvironmentCombo } from "../environments/EnvironmentLabel";
import {
Popover,
Expand All @@ -11,10 +14,6 @@ import {
PopoverSectionHeader,
} from "../primitives/Popover";
import { type SideMenuEnvironment, type SideMenuProject } from "./SideMenu";
import { cn } from "~/utils/cn";
import { useFeatures } from "~/hooks/useFeatures";
import { v3BillingPath } from "~/utils/pathBuilder";
import { TextLink } from "../primitives/TextLink";

export function EnvironmentSelector({
organization,
Expand Down
72 changes: 34 additions & 38 deletions apps/webapp/app/components/navigation/SideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,11 @@ import {
v3TestPath,
v3UsagePath,
} from "~/utils/pathBuilder";
import { useDevPresence } from "../DevPresence";
import { ImpersonationBanner } from "../ImpersonationBanner";
import { PackageManagerProvider, TriggerDevStepV3 } from "../SetupCommands";
import { UserProfilePhoto } from "../UserProfilePhoto";
import connectedImage from "../../assets/images/cli-connected.png";
import disconnectedImage from "../../assets/images/cli-disconnected.png";
import { FreePlanUsage } from "../billing/FreePlanUsage";
import { useDevPresence } from "../DevPresence";
import { ImpersonationBanner } from "../ImpersonationBanner";
import { Button, ButtonContent, LinkButton } from "../primitives/Buttons";
import {
Dialog,
Expand All @@ -80,18 +78,15 @@ import {
PopoverTrigger,
} from "../primitives/Popover";
import { TextLink } from "../primitives/TextLink";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
import { PackageManagerProvider, TriggerDevStepV3 } from "../SetupCommands";
import { UserProfilePhoto } from "../UserProfilePhoto";
import { EnvironmentSelector } from "./EnvironmentSelector";
import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
import { SideMenuHeader } from "./SideMenuHeader";
import { SideMenuItem } from "./SideMenuItem";
import { SideMenuSection } from "./SideMenuSection";
import {
SimpleTooltip,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../primitives/Tooltip";
import { InlineCode } from "../code/InlineCode";

type SideMenuUser = Pick<User, "email" | "admin"> & { isImpersonating: boolean };
export type SideMenuProject = Pick<
Expand Down Expand Up @@ -144,8 +139,8 @@ export function SideMenu({
>
<div
className={cn(
"flex items-center justify-between px-1 py-1 transition",
showHeaderDivider ? " border-grid-bright" : "border-transparent"
"flex items-center justify-between border-b px-1 py-1 transition duration-300",
showHeaderDivider ? "border-grid-bright" : "border-transparent"
)}
>
<ProjectSelector
Expand Down Expand Up @@ -303,7 +298,10 @@ function ProjectSelector({
<PopoverArrowTrigger
isOpen={isOrgMenuOpen}
overflowHidden
className="h-8 w-full justify-between overflow-hidden py-1 pl-1.5"
className={cn(
"h-8 w-full justify-between overflow-hidden py-1 pl-1.5",
user.isImpersonating && "border border-dashed border-amber-400"
)}
>
<span className="flex items-center gap-1.5 overflow-hidden">
<Avatar avatar={organization.avatar} className="size-5" />
Expand All @@ -320,25 +318,26 @@ function ProjectSelector({
>
<div className="flex flex-col gap-2 bg-charcoal-750 p-2">
<div className="flex items-center gap-2.5">
<div className="box-content size-10 overflow-clip rounded-sm border border-charcoal-700 bg-charcoal-850">
<div className="box-content size-10 overflow-clip rounded-sm bg-charcoal-800">
<Avatar avatar={organization.avatar} className="size-10" includePadding />
</div>
<div className="space-y-0.5">
<Paragraph variant="extra-small/bright">{organization.title}</Paragraph>
<div className="flex items-baseline">
<Paragraph variant="small/bright">{organization.title}</Paragraph>
<div className="flex items-baseline gap-2">
{plan && (
<Paragraph variant="extra-small">
<TextLink variant="secondary" to={v3BillingPath(organization)}>
{plan}
</TextLink>
</Paragraph>
)}
<Paragraph variant="extra-small">
<TextLink
variant="secondary"
to={organizationTeamPath(organization)}
>{simplur`${organization.membersCount} member[|s]`}</TextLink>
</Paragraph>
className="text-xs"
to={v3BillingPath(organization)}
>
{plan}
</TextLink>
)}
<TextLink
variant="secondary"
className="text-xs"
to={organizationTeamPath(organization)}
>{simplur`${organization.membersCount} member[|s]`}</TextLink>
</div>
</div>
</div>
Expand All @@ -348,6 +347,7 @@ function ProjectSelector({
to={organizationSettingsPath(organization)}
fullWidth
iconSpacing="gap-1.5"
className="group-hover/button:border-charcoal-500"
>
<CogIcon className="size-4 text-text-dimmed" />
<span className="text-text-bright">Settings</span>
Expand All @@ -357,6 +357,7 @@ function ProjectSelector({
to={v3UsagePath(organization)}
fullWidth
iconSpacing="gap-1.5"
className="group-hover/button:border-charcoal-500"
>
<ChartBarIcon className="size-4 text-text-dimmed" />
<span className="text-text-bright">Usage</span>
Expand Down Expand Up @@ -550,9 +551,7 @@ export function DevConnection() {
</div>
<DialogContent>
<DialogHeader>
{isConnected
? "Your dev server is connected to Trigger.dev"
: "Your dev server is not connected to Trigger.dev"}
{isConnected ? "Your dev server is connected" : "Your dev server is not connected"}
</DialogHeader>
<div className="mt-2 flex flex-col gap-3 px-2">
<div className="flex flex-col items-center justify-center gap-6 px-6 py-10">
Expand All @@ -571,20 +570,17 @@ export function DevConnection() {
{isConnected ? null : (
<div className="space-y-3">
<PackageManagerProvider>
<TriggerDevStepV3 />
<TriggerDevStepV3 title="Run this command to connect" />
</PackageManagerProvider>
<Paragraph variant="small">
Run this CLI `dev` command to connect to the Trigger.dev servers to start developing
locally. Keep it running while you develop to stay connected.
Run this CLI <InlineCode variant="extra-small">dev</InlineCode> command to connect
to the Trigger.dev servers to start developing locally. Keep it running while you
develop to stay connected. Learn more in the{" "}
<TextLink to={docsPath("cli-dev")}>CLI docs</TextLink>.
</Paragraph>
</div>
)}
</div>
<DialogFooter>
<LinkButton variant="tertiary/medium" LeadingIcon={BookOpenIcon} to={docsPath("cli-dev")}>
CLI docs
</LinkButton>
</DialogFooter>
</DialogContent>
</Dialog>
);
Expand Down
8 changes: 4 additions & 4 deletions apps/webapp/app/components/navigation/SideMenuSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ export function SideMenuSection({
expanded: {
height: "auto",
transition: {
height: { duration: 0.3, ease: "easeOut" },
height: { duration: 0.3, ease: "easeInOut" },
},
},
collapsed: {
height: 0,
transition: {
height: { duration: 0.2, ease: "easeIn" },
height: { duration: 0.2, ease: "easeInOut" },
},
},
}}
Expand All @@ -67,12 +67,12 @@ export function SideMenuSection({
expanded: {
translateY: 0,
opacity: 1,
transition: { duration: 0.3, ease: "easeOut" },
transition: { duration: 0.3, ease: "easeInOut" },
},
collapsed: {
translateY: "-100%",
opacity: 0,
transition: { duration: 0.2, ease: "easeIn" },
transition: { duration: 0.2, ease: "easeInOut" },
},
}}
>
Expand Down
Loading