Side menu and Org dropdown improvements (#1801)
* Organize imports * Removes animation on the dropdown menu icon * Improves Settings and Usage button states * Side menu sections animate with easeInOut * Incorporates docs button into the text and adds title to the clipboard field * Icons look better as the small variant * Improved the typography layout of the org name and small links * Made the divide line show when the side menu scrolls * Made the impersonating icon amber so it stands out more * Adds an optional amber border to the org dropdown
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { Form } from "@remix-run/react";
|
||||
import { Paragraph } from "./primitives/Paragraph";
|
||||
import { Button } from "./primitives/Buttons";
|
||||
import { UserMinusIcon } from "@heroicons/react/20/solid";
|
||||
import { Form } from "@remix-run/react";
|
||||
import { Button } from "./primitives/Buttons";
|
||||
|
||||
export function ImpersonationBanner() {
|
||||
return (
|
||||
@@ -13,6 +12,7 @@ export function ImpersonationBanner() {
|
||||
LeadingIcon={UserMinusIcon}
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
className="text-amber-400"
|
||||
>
|
||||
Stop impersonating
|
||||
</Button>
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -77,14 +83,17 @@ 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}`}
|
||||
@@ -92,7 +101,7 @@ export function InitCommandV3() {
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"pnpm"}>
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
variant="secondary/medium"
|
||||
iconButton
|
||||
className="mb-4"
|
||||
value={`pnpm dlx ${initCommand}`}
|
||||
@@ -100,7 +109,7 @@ export function InitCommandV3() {
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"yarn"}>
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
variant="secondary/medium"
|
||||
iconButton
|
||||
className="mb-4"
|
||||
value={`yarn dlx ${initCommand}`}
|
||||
@@ -110,7 +119,7 @@ export function InitCommandV3() {
|
||||
);
|
||||
}
|
||||
|
||||
export function TriggerDevStepV3() {
|
||||
export function TriggerDevStepV3({ title }: TabsProps) {
|
||||
const { activePackageManager, setActivePackageManager } = usePackageManager();
|
||||
|
||||
return (
|
||||
@@ -119,14 +128,17 @@ 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`}
|
||||
@@ -134,7 +146,7 @@ export function TriggerDevStepV3() {
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"pnpm"}>
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
variant="secondary/medium"
|
||||
iconButton
|
||||
className="mb-4"
|
||||
value={`pnpm dlx trigger.dev@${v3PackageTag} dev`}
|
||||
@@ -142,7 +154,7 @@ export function TriggerDevStepV3() {
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"yarn"}>
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
variant="secondary/medium"
|
||||
iconButton
|
||||
className="mb-4"
|
||||
value={`yarn dlx trigger.dev@${v3PackageTag} dev`}
|
||||
@@ -152,7 +164,7 @@ export function TriggerDevStepV3() {
|
||||
);
|
||||
}
|
||||
|
||||
export function TriggerLoginStepV3() {
|
||||
export function TriggerLoginStepV3({ title }: TabsProps) {
|
||||
const { activePackageManager, setActivePackageManager } = usePackageManager();
|
||||
|
||||
return (
|
||||
@@ -161,14 +173,17 @@ 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`}
|
||||
@@ -176,7 +191,7 @@ export function TriggerLoginStepV3() {
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"pnpm"}>
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
variant="secondary/medium"
|
||||
iconButton
|
||||
className="mb-4"
|
||||
value={`pnpm dlx trigger.dev@${v3PackageTag} login`}
|
||||
@@ -184,7 +199,7 @@ export function TriggerLoginStepV3() {
|
||||
</ClientTabsContent>
|
||||
<ClientTabsContent value={"yarn"}>
|
||||
<ClipboardField
|
||||
variant="primary/medium"
|
||||
variant="secondary/medium"
|
||||
iconButton
|
||||
className="mb-4"
|
||||
value={`yarn dlx trigger.dev@${v3PackageTag} login`}
|
||||
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
@@ -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<
|
||||
@@ -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
|
||||
@@ -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" />
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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">
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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" },
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -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" },
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import {
|
||||
BuildingOffice2Icon,
|
||||
CodeBracketSquareIcon,
|
||||
CubeIcon,
|
||||
FaceSmileIcon,
|
||||
FireIcon,
|
||||
RocketLaunchIcon,
|
||||
StarIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { type Prisma } from "@trigger.dev/database";
|
||||
import { useLayoutEffect, useRef, useState } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -175,12 +175,7 @@ function PopoverArrowTrigger({
|
||||
>
|
||||
{children}
|
||||
</Paragraph>
|
||||
<DropdownIcon
|
||||
className={cn(
|
||||
"h-4 w-4 min-w-[0.75rem] text-text-dimmed transition group-hover:text-text-bright",
|
||||
isOpen && "-scale-y-100"
|
||||
)}
|
||||
/>
|
||||
<DropdownIcon className="size-4 min-w-[0.75rem] text-text-dimmed transition group-hover:text-text-bright" />
|
||||
</PopoverTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user