refactor: frontend improvements

This commit is contained in:
Charlie Lamb
2026-05-14 11:14:30 +01:00
parent 4d5c70a9d8
commit e882bcb048
43 changed files with 294 additions and 570 deletions

2
ai

Submodule ai updated: 008fd7a292...761b842553

View File

@@ -11,7 +11,7 @@
"enumMembers",
"duplicates"
],
"ignore": ["ai/**", "others/**"],
"ignore": ["ai/**", "others/**", ".trigger/**"],
"ignoreWorkspaces": [
"packages/atmn",
"packages/autumn-js",

View File

@@ -14,7 +14,7 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { cn } from "@/lib/utils";
const providers = {

View File

@@ -417,8 +417,8 @@ export const PromptInputActionAddAttachments = ({
return (
<DropdownMenuItem
{...props}
onSelect={(e) => {
e.preventDefault();
closeOnClick={false}
onClick={() => {
attachments.openFileDialog();
}}
>

View File

@@ -29,6 +29,7 @@ function ColumnCheckboxItem<T>({ column }: { column: Column<T> }) {
return (
<DropdownMenuItem
key={column.id}
closeOnClick={false}
onClick={() => {
column.toggleVisibility(!isVisible);
}}

View File

@@ -4,7 +4,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
export function TableDropdownMenuCell({
children,

View File

@@ -1,308 +0,0 @@
"use client";
import { Menu as MenuPrimitive } from "@base-ui/react/menu";
import { Check, ChevronRight, Circle } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";
import SmallSpinner from "../general/SmallSpinner";
function DropdownMenu({
...props
}: MenuPrimitive.Root.Props) {
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
}
const DropdownMenuTrigger = React.forwardRef<
HTMLButtonElement,
MenuPrimitive.Trigger.Props & { asChild?: boolean }
>(({ asChild, children, ...props }, ref) => {
if (asChild && React.isValidElement(children)) {
return (
<MenuPrimitive.Trigger
ref={ref}
data-slot="dropdown-menu-trigger"
render={children}
{...props}
/>
);
}
return (
<MenuPrimitive.Trigger ref={ref} data-slot="dropdown-menu-trigger" {...props}>
{children}
</MenuPrimitive.Trigger>
);
});
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
function DropdownMenuGroup({
...props
}: MenuPrimitive.Group.Props) {
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
}
function DropdownMenuPortal({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
function DropdownMenuSub({
...props
}: MenuPrimitive.SubmenuRoot.Props) {
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
}
function DropdownMenuRadioGroup({
...props
}: MenuPrimitive.RadioGroup.Props) {
return (
<MenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
);
}
const DropdownMenuSubTrigger = React.forwardRef<
HTMLDivElement,
MenuPrimitive.SubmenuTrigger.Props & {
inset?: boolean;
withIcon?: boolean;
}
>(({ className, inset, children, withIcon = true, ...props }, ref) => (
<MenuPrimitive.SubmenuTrigger
ref={ref}
data-slot="dropdown-menu-sub-trigger"
className={cn(
"flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-zinc-100 data-popup-open:bg-zinc-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:focus:bg-zinc-800 dark:data-popup-open:bg-zinc-800",
inset && "pl-8",
className,
)}
{...props}
>
{children}
{withIcon && <ChevronRight className="ml-auto text-t3" />}
</MenuPrimitive.SubmenuTrigger>
));
DropdownMenuSubTrigger.displayName = "DropdownMenuSubTrigger";
const DropdownMenuSubContent = React.forwardRef<
HTMLDivElement,
MenuPrimitive.Popup.Props & {
sideOffset?: number;
}
>(({ className, sideOffset = 0, ...props }, ref) => (
<MenuPrimitive.Portal>
<MenuPrimitive.Positioner
sideOffset={sideOffset}
positionMethod="fixed"
className="isolate z-[160]"
>
<MenuPrimitive.Popup
ref={ref}
data-slot="dropdown-menu-sub-content"
className={cn(
"z-[160] min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 shadow-lg data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50 text-zinc-800 font-regular",
className,
)}
{...props}
/>
</MenuPrimitive.Positioner>
</MenuPrimitive.Portal>
));
DropdownMenuSubContent.displayName = "DropdownMenuSubContent";
const DropdownMenuContent = React.forwardRef<
HTMLDivElement,
MenuPrimitive.Popup.Props & {
sideOffset?: number;
side?: "top" | "bottom" | "left" | "right";
align?: "start" | "center" | "end";
onCloseAutoFocus?: (e: any) => void;
}
>(({ className, sideOffset = 4, side = "bottom", align = "start", onCloseAutoFocus: _onCloseAutoFocus, ...props }, ref) => (
<MenuPrimitive.Portal>
<MenuPrimitive.Positioner
sideOffset={sideOffset}
side={side}
align={align}
positionMethod="fixed"
className="isolate z-[160] outline-none"
>
<MenuPrimitive.Popup
ref={ref}
data-slot="dropdown-menu-content"
className={cn(
"z-[160] min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-md dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</MenuPrimitive.Positioner>
</MenuPrimitive.Portal>
));
DropdownMenuContent.displayName = "DropdownMenuContent";
const DropdownMenuItem = React.forwardRef<
HTMLDivElement,
MenuPrimitive.Item.Props & {
inset?: boolean;
isLoading?: boolean;
shimmer?: boolean;
asChild?: boolean;
}
>(({ className, inset, shimmer = false, isLoading, children, asChild, ...props }, ref) => {
if (asChild && React.isValidElement(children)) {
return (
<MenuPrimitive.Item
ref={ref}
data-slot="dropdown-menu-item"
closeOnClick={false}
render={children}
className={cn(
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
shimmer && "shimmer",
inset && "pl-8",
className,
)}
disabled={shimmer || isLoading}
{...props}
/>
);
}
return (
<MenuPrimitive.Item
ref={ref}
data-slot="dropdown-menu-item"
closeOnClick={false}
className={cn(
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
shimmer && "shimmer",
inset && "pl-8",
className,
)}
disabled={shimmer || isLoading}
{...props}
>
{isLoading ? (
<>
{children}
<SmallSpinner />
</>
) : (
children
)}
</MenuPrimitive.Item>
);
});
DropdownMenuItem.displayName = "DropdownMenuItem";
const DropdownMenuCheckboxItem = React.forwardRef<
HTMLDivElement,
MenuPrimitive.CheckboxItem.Props
>(({ className, children, checked, ...props }, ref) => (
<MenuPrimitive.CheckboxItem
ref={ref}
data-slot="dropdown-menu-checkbox-item"
className={cn(
"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-1 pr-2 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-disabled:pointer-events-none data-disabled:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
className,
)}
checked={checked}
{...props}
>
{children}
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<MenuPrimitive.CheckboxItemIndicator>
<Check className="h-4 w-4 text-t3" />
</MenuPrimitive.CheckboxItemIndicator>
</span>
</MenuPrimitive.CheckboxItem>
));
DropdownMenuCheckboxItem.displayName = "DropdownMenuCheckboxItem";
const DropdownMenuRadioItem = React.forwardRef<
HTMLDivElement,
MenuPrimitive.RadioItem.Props
>(({ className, children, ...props }, ref) => (
<MenuPrimitive.RadioItem
ref={ref}
data-slot="dropdown-menu-radio-item"
className={cn(
"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-disabled:pointer-events-none data-disabled:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenuPrimitive.RadioItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</MenuPrimitive.RadioItemIndicator>
</span>
{children}
</MenuPrimitive.RadioItem>
));
DropdownMenuRadioItem.displayName = "DropdownMenuRadioItem";
const DropdownMenuLabel = React.forwardRef<
HTMLDivElement,
MenuPrimitive.GroupLabel.Props & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<MenuPrimitive.GroupLabel
ref={ref}
data-slot="dropdown-menu-label"
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className,
)}
{...props}
/>
));
DropdownMenuLabel.displayName = "DropdownMenuLabel";
const DropdownMenuSeparator = React.forwardRef<
HTMLDivElement,
MenuPrimitive.Separator.Props
>(({ className, ...props }, ref) => (
<MenuPrimitive.Separator
ref={ref}
data-slot="dropdown-menu-separator"
className={cn("-mx-1 my-1 h-px bg-zinc-100 dark:bg-zinc-800", className)}
{...props}
/>
));
DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
);
};
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
};

View File

@@ -88,8 +88,7 @@ const DialogContent = React.forwardRef<
ref={ref}
data-slot="dialog-content"
className={cn(
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-[40%] left-[50%] z-[180] grid translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200",
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-[40%] left-[50%] z-[180] grid translate-x-[-50%] translate-y-[-50%] rounded-lg shadow-lg ring-1 ring-foreground/10 duration-200",
"w-full max-w-md gap-3 bg-background",
"p-4",
className,

View File

@@ -2,7 +2,7 @@
import { Menu as MenuPrimitive } from "@base-ui/react/menu";
import { cva, type VariantProps } from "class-variance-authority";
import { Check, ChevronRight, Circle } from "lucide-react";
import { Check, ChevronRight } from "lucide-react";
import * as React from "react";
import { useHotkeys } from "react-hotkeys-hook";
@@ -14,16 +14,17 @@ const DropdownMenuContext = React.createContext<{ isOpen: boolean }>({
});
const dropdownMenuItemVariants = cva(
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-t2 outline-hidden transition-colors data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
"group/dropdown-menu-item relative flex cursor-default select-none items-center gap-1.5 rounded-md px-1.5 py-1 text-sm text-t2 outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default: "hover:bg-accent focus:bg-accent",
default:
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground",
destructive:
"hover:bg-destructive/10 focus:bg-destructive/10 text-destructive",
"text-destructive focus:bg-destructive/10 focus:text-destructive dark:focus:bg-destructive/20",
},
inset: {
true: "pl-8",
true: "pl-7",
false: "",
},
},
@@ -38,9 +39,7 @@ type DropdownMenuItemVariantProps = VariantProps<
typeof dropdownMenuItemVariants
>;
function DropdownMenu(
props: MenuPrimitive.Root.Props,
) {
function DropdownMenu(props: MenuPrimitive.Root.Props) {
const isOpen = props.open ?? false;
return (
<DropdownMenuContext.Provider value={{ isOpen }}>
@@ -74,31 +73,19 @@ const DropdownMenuTrigger = React.forwardRef<
);
});
function DropdownMenuGroup(
props: MenuPrimitive.Group.Props,
) {
return (
<MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
);
function DropdownMenuGroup(props: MenuPrimitive.Group.Props) {
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
}
function DropdownMenuPortal({
children,
}: {
children: React.ReactNode;
}) {
function DropdownMenuPortal({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
function DropdownMenuSub(
props: MenuPrimitive.SubmenuRoot.Props,
) {
function DropdownMenuSub(props: MenuPrimitive.SubmenuRoot.Props) {
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
}
function DropdownMenuRadioGroup(
props: MenuPrimitive.RadioGroup.Props,
) {
function DropdownMenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) {
return (
<MenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
@@ -122,35 +109,39 @@ const DropdownMenuSubTrigger = React.forwardRef<
ref={ref}
data-slot="dropdown-menu-sub-trigger"
className={cn(
"flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm text-t2 outline-hidden hover:bg-accent focus:bg-accent data-popup-open:bg-accent [&>svg]:pointer-events-none [&>svg]:size-4 [&>svg]:shrink-0",
inset && "pl-8",
"flex cursor-default gap-1.5 select-none items-center rounded-md px-1.5 py-1 text-sm text-t2 outline-hidden focus:bg-accent focus:text-accent-foreground data-popup-open:bg-accent data-popup-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
inset && "pl-7",
className,
)}
{...rest}
>
{children}
{withIcon && <ChevronRight className="ml-auto text-t3" />}
{withIcon && <ChevronRight className="ml-auto size-3.5 text-t3" />}
</MenuPrimitive.SubmenuTrigger>
);
});
const DropdownMenuSubContent = React.forwardRef<
HTMLDivElement,
MenuPrimitive.Popup.Props & { sideOffset?: number }
MenuPrimitive.Popup.Props & {
sideOffset?: number;
alignOffset?: number;
}
>(function DropdownMenuSubContent(props, ref) {
const { className, sideOffset = 0, ...rest } = props;
const { className, sideOffset = 6, alignOffset = -4, ...rest } = props;
return (
<MenuPrimitive.Portal>
<MenuPrimitive.Positioner
sideOffset={sideOffset}
alignOffset={alignOffset}
positionMethod="fixed"
className="isolate z-[200]"
className="isolate z-[300] outline-none"
>
<MenuPrimitive.Popup
ref={ref}
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-interactive-secondary text-t2 min-w-[8rem] overflow-hidden rounded-lg border p-1 shadow-md",
"min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-lg bg-interactive-secondary p-1 text-t2 shadow-lg ring-1 ring-foreground/10 duration-100 origin-(--transform-origin)",
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
@@ -172,7 +163,14 @@ const DropdownMenuContent = React.forwardRef<
HTMLDivElement,
DropdownMenuContentProps
>(function DropdownMenuContent(props, ref) {
const { className, sideOffset = 4, side = "bottom", align = "start", onCloseAutoFocus: _onCloseAutoFocus, ...rest } = props;
const {
className,
sideOffset = 4,
side = "bottom",
align = "start",
onCloseAutoFocus: _onCloseAutoFocus,
...rest
} = props;
return (
<MenuPrimitive.Portal>
<MenuPrimitive.Positioner
@@ -180,13 +178,13 @@ const DropdownMenuContent = React.forwardRef<
side={side}
align={align}
positionMethod="fixed"
className="isolate z-[200] outline-none"
className="isolate z-[300] outline-none"
>
<MenuPrimitive.Popup
ref={ref}
data-slot="dropdown-menu-content"
className={cn(
"bg-interactive-secondary text-t2 min-w-[8rem] overflow-hidden rounded-lg border p-1 shadow-md",
"max-h-(--available-height) min-w-32 overflow-x-hidden overflow-y-auto rounded-lg bg-interactive-secondary p-1 text-t2 shadow-md ring-1 ring-foreground/10 duration-100 origin-(--transform-origin)",
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
@@ -202,6 +200,8 @@ type DropdownMenuItemProps = MenuPrimitive.Item.Props &
isLoading?: boolean;
shimmer?: boolean;
shortcut?: string;
asChild?: boolean;
closeOnClick?: boolean;
};
const DropdownMenuItem = React.forwardRef<
@@ -214,6 +214,8 @@ const DropdownMenuItem = React.forwardRef<
shimmer = false,
isLoading,
shortcut,
asChild,
closeOnClick,
className,
children,
onClick,
@@ -233,11 +235,30 @@ const DropdownMenuItem = React.forwardRef<
},
);
if (asChild && React.isValidElement(children)) {
return (
<MenuPrimitive.Item
ref={ref}
data-slot="dropdown-menu-item"
render={children}
closeOnClick={closeOnClick}
className={cn(
dropdownMenuItemVariants({ variant, inset }),
shimmer && "shimmer",
className,
)}
disabled={shimmer || isLoading}
onClick={onClick}
{...rest}
/>
);
}
return (
<MenuPrimitive.Item
ref={ref}
data-slot="dropdown-menu-item"
closeOnClick={false}
closeOnClick={closeOnClick}
className={cn(
dropdownMenuItemVariants({ variant, inset }),
shimmer && "shimmer",
@@ -261,23 +282,25 @@ const DropdownMenuItem = React.forwardRef<
const DropdownMenuCheckboxItem = React.forwardRef<
HTMLDivElement,
MenuPrimitive.CheckboxItem.Props
MenuPrimitive.CheckboxItem.Props & { inset?: boolean }
>(function DropdownMenuCheckboxItem(props, ref) {
const { className, children, ...rest } = props;
const { className, children, inset, ...rest } = props;
return (
<MenuPrimitive.CheckboxItem
ref={ref}
data-slot="dropdown-menu-checkbox-item"
data-inset={inset}
closeOnClick={false}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-1 pr-2 text-sm text-t2 outline-hidden transition-colors hover:bg-accent focus:bg-accent data-disabled:pointer-events-none data-disabled:opacity-50",
"relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...rest}
>
{children}
<span className="absolute right-2 flex size-3.5 items-center justify-center">
<span className="pointer-events-none absolute right-2 flex items-center justify-center">
<MenuPrimitive.CheckboxItemIndicator>
<Check className="size-4 text-t3" />
<Check className="size-4" />
</MenuPrimitive.CheckboxItemIndicator>
</span>
</MenuPrimitive.CheckboxItem>
@@ -286,22 +309,23 @@ const DropdownMenuCheckboxItem = React.forwardRef<
const DropdownMenuRadioItem = React.forwardRef<
HTMLDivElement,
MenuPrimitive.RadioItem.Props
MenuPrimitive.RadioItem.Props & { inset?: boolean }
>(function DropdownMenuRadioItem(props, ref) {
const { className, children, ...rest } = props;
const { className, children, inset, ...rest } = props;
return (
<MenuPrimitive.RadioItem
ref={ref}
data-slot="dropdown-menu-radio-item"
data-inset={inset}
className={cn(
"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm text-t2 outline-hidden transition-colors hover:bg-accent focus:bg-accent data-disabled:pointer-events-none data-disabled:opacity-50",
"relative flex cursor-default select-none items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...rest}
>
<span className="absolute left-2 flex size-3.5 items-center justify-center">
<span className="pointer-events-none absolute right-2 flex items-center justify-center">
<MenuPrimitive.RadioItemIndicator>
<Circle className="size-2 fill-current" />
<Check className="size-4" />
</MenuPrimitive.RadioItemIndicator>
</span>
{children}
@@ -322,9 +346,9 @@ const DropdownMenuLabel = React.forwardRef<
<MenuPrimitive.GroupLabel
ref={ref}
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
className,
)}
{...rest}
@@ -354,7 +378,10 @@ function DropdownMenuShortcut(props: DropdownMenuShortcutProps) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn("ml-auto text-xs tracking-widest text-t4", className)}
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
className,
)}
{...rest}
/>
);
@@ -367,7 +394,11 @@ export {
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,

View File

@@ -1,21 +1,6 @@
import { Check, ChevronsUpDown } from "lucide-react";
import { useState } from "react";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/v2/buttons/Button";
import { cn } from "@/lib/utils";
import { stripeCurrencyCodes } from "@/utils/constants/stripeCurrencyCodes";
import { SearchableSelect } from "./SearchableSelect";
export const CurrencySelect = ({
defaultCurrency,
@@ -28,54 +13,19 @@ export const CurrencySelect = ({
className?: string;
disabled?: boolean;
}) => {
const [open, setOpen] = useState(false);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="secondary"
role="combobox"
aria-expanded={open}
className={cn("w-full justify-between", className)}
disabled={disabled}
disableActive
>
{defaultCurrency ? defaultCurrency : "Select currency..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0 w-[var(--anchor-width)]">
<Command className="p-0">
<CommandInput placeholder="Search currency..." />
<CommandEmpty>No currency found.</CommandEmpty>
<CommandList className="p-0">
<CommandGroup className="p-0">
{stripeCurrencyCodes.map((currency) => (
<CommandItem
key={currency.code}
value={currency.code}
onSelect={(value) => {
setDefaultCurrency(value.toUpperCase());
setOpen(false);
}}
className="p-2 flex items-center justify-between"
>
{currency.currency} - {currency.code}
<Check
className={cn(
"mr-2 h-4 w-4",
defaultCurrency === currency.code
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<SearchableSelect
value={defaultCurrency}
onValueChange={(value) => setDefaultCurrency(value.toUpperCase())}
options={stripeCurrencyCodes}
getOptionValue={(currency) => currency.code}
getOptionLabel={(currency) => `${currency.currency} - ${currency.code}`}
searchable
searchPlaceholder="Search currency..."
placeholder="Select currency..."
emptyText="No currency found."
disabled={disabled}
triggerClassName={cn("w-full", className)}
/>
);
};

View File

@@ -93,12 +93,12 @@ function SelectContent({
side={side}
alignItemWithTrigger={false}
positionMethod="fixed"
className="isolate z-[160]"
className="isolate z-[300] outline-none"
>
<SelectPrimitive.Popup
data-slot="select-content"
className={cn(
"bg-interactive-secondary text-popover-foreground relative max-h-[var(--available-height)] min-w-[var(--anchor-width)] overflow-x-hidden overflow-y-auto rounded-md border shadow-md p-1",
"bg-interactive-secondary text-t2 relative max-h-[var(--available-height)] min-w-[var(--anchor-width)] overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 ring-foreground/10 p-1",
className,
)}
{...props}
@@ -132,12 +132,12 @@ function SelectItem({
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
"data-highlighted:bg-accent data-highlighted:text-accent-foreground data-highlighted:**:text-accent-foreground focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className,
)}
{...props}
>
<span className="absolute right-2 flex size-3.5 items-center justify-center">
<span className="pointer-events-none absolute right-2 flex items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</SelectPrimitive.ItemIndicator>

View File

@@ -46,15 +46,16 @@ export const FilterStatusSubMenu = () => {
{statuses.map((status: any) => {
const isActive = selectedStatuses.includes(status);
return (
<DropdownMenuItem
key={status}
onClick={(e) => {
e.preventDefault();
toggleStatus(status);
}}
onSelect={(e) => e.preventDefault()}
className="flex items-center gap-2 cursor-pointer text-sm"
>
<DropdownMenuItem
key={status}
closeOnClick={false}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleStatus(status);
}}
className="flex items-center gap-2 cursor-pointer text-sm"
>
<Checkbox checked={isActive} className="border-border" />
{keyToTitle(status)}
</DropdownMenuItem>

View File

@@ -70,11 +70,11 @@ export const ProcessorSubMenu = () => {
return (
<DropdownMenuItem
key={value}
closeOnClick={false}
onClick={(e) => {
e.preventDefault();
toggleProcessor(value);
}}
onSelect={(e) => e.preventDefault()}
className="flex items-center gap-2 cursor-pointer text-sm"
>
<Checkbox checked={isActive} className="border-border" />

View File

@@ -192,11 +192,11 @@ export const ProductsSubMenu = () => {
{versionCount === 1 ? (
// Single version - show just one button for the product
<DropdownMenuItem
closeOnClick={false}
onClick={(e) => {
e.preventDefault();
toggleVersion(product.id, "1");
}}
onSelect={(e) => e.preventDefault()}
className="flex items-center gap-2 cursor-pointer font-medium"
>
<Checkbox
@@ -232,11 +232,11 @@ export const ProductsSubMenu = () => {
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem
closeOnClick={false}
onClick={(e) => {
e.preventDefault();
toggleProduct(product);
}}
onSelect={(e) => e.preventDefault()}
className="flex items-center gap-2 cursor-pointer font-medium"
>
<Checkbox
@@ -257,11 +257,11 @@ export const ProductsSubMenu = () => {
return (
<DropdownMenuItem
key={versionKey}
closeOnClick={false}
onClick={(e) => {
e.preventDefault();
toggleVersion(product.id, version.toString());
}}
onSelect={(e) => e.preventDefault()}
className="flex items-center gap-2 cursor-pointer text-sm"
>
<Checkbox

View File

@@ -9,6 +9,7 @@ import {
import { Button } from "@/components/v2/buttons/Button";
import { Input } from "@/components/v2/inputs/Input";
import { cn } from "@/lib/utils";
import { useAxiosInstance } from "@/services/useAxiosInstance";
import { getBackendErr } from "@/utils/genUtils";
import { useSavedViewsQuery } from "../../hooks/useSavedViewsQuery";
@@ -63,17 +64,16 @@ export const SaveViewPopover = ({ onClose }: SaveViewPopoverProps) => {
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="skeleton"
size="sm"
<button
type="button"
className={cn(
"w-full !h-full text-t3 text-sm hover:bg-accent hover:text-t2 rounded-none gap-0",
"flex-1 flex items-center justify-center gap-1.5 rounded-br-lg px-2 py-1.5 text-xs text-t3 hover:text-t2 hover:bg-accent cursor-default",
open && "bg-accent text-t1",
)}
>
<Pin size={12} className="mr-2" />
<Pin size={10} />
Save
</Button>
</button>
</PopoverTrigger>
<PopoverContent
sideOffset={2}

View File

@@ -2,17 +2,17 @@ import { TrashIcon } from "@phosphor-icons/react";
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import {
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@/components/v2/dropdowns/DropdownMenu";
import { useAxiosInstance } from "@/services/useAxiosInstance";
import { getBackendErr } from "@/utils/genUtils";
import { useCustomerFilters } from "../../hooks/useCustomerFilters";
@@ -54,7 +54,9 @@ export const SavedViews = ({
status: statusParam ? statusParam.split(",").filter(Boolean) : [],
version: versionParam ? versionParam.split(",").filter(Boolean) : [],
none: noneParam === "true",
processor: processorParam ? processorParam.split(",").filter(Boolean) : [],
processor: processorParam
? processorParam.split(",").filter(Boolean)
: [],
});
toast.success(`Applied filters from ${view.name} view`);

View File

@@ -181,13 +181,12 @@ export const SelectFeatureDropdown = () => {
{filteredOptions.map((option) => {
const featureLabel = formatFeatureLabel(option.linkedFeatures);
return (
<DropdownMenuCheckboxItem
key={option.eventName}
checked={option.selected}
onCheckedChange={() => handleToggleItem(option)}
onSelect={(e) => e.preventDefault()}
className="pl-2"
>
<DropdownMenuCheckboxItem
key={option.eventName}
checked={option.selected}
onCheckedChange={() => handleToggleItem(option)}
className="pl-2"
>
<div className="flex items-center gap-1.5 min-w-0">
<span className="text-xs truncate">
{option.eventName}

View File

@@ -10,6 +10,7 @@ import { IconButton } from "@/components/v2/buttons/IconButton";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
@@ -133,6 +134,7 @@ export const SelectGroupByDropdown = ({
<div className="max-h-[300px] overflow-y-auto pt-1">
<DropdownMenuItem
closeOnClick={false}
onClick={() => handleSelect({ property: null })}
className="flex items-center justify-between"
>
@@ -144,6 +146,7 @@ export const SelectGroupByDropdown = ({
<DropdownMenuSeparator />
{showCustomerIdOption && (
<DropdownMenuItem
closeOnClick={false}
onClick={() => handleSelect({ property: "customer_id" })}
className="flex items-center justify-between"
>
@@ -154,6 +157,7 @@ export const SelectGroupByDropdown = ({
</DropdownMenuItem>
)}
<DropdownMenuItem
closeOnClick={false}
onClick={() => handleSelect({ property: "entity_id" })}
className="flex items-center justify-between"
>
@@ -174,6 +178,7 @@ export const SelectGroupByDropdown = ({
{filteredOptions.map((property) => (
<DropdownMenuItem
key={property}
closeOnClick={false}
onClick={() => handleSelect({ property })}
className="flex items-center justify-between"
>
@@ -233,10 +238,12 @@ export const SelectGroupByDropdown = ({
{currentGroupBy && availableGroupValues.length > 0 && (
<>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuLabel className="text-xs text-t4 font-normal">
Filter by value
</DropdownMenuLabel>
<DropdownMenuItem
closeOnClick={false}
onClick={() => setGroupFilter(null)}
className="flex items-center justify-between"
>
@@ -255,6 +262,7 @@ export const SelectGroupByDropdown = ({
return (
<DropdownMenuItem
key={value}
closeOnClick={false}
onClick={() => setGroupFilter(value)}
className="flex items-center justify-between"
>
@@ -267,6 +275,7 @@ export const SelectGroupByDropdown = ({
</DropdownMenuItem>
);
})}
</DropdownMenuGroup>
</>
)}
</div>

View File

@@ -2,10 +2,12 @@ import type { CreateCustomer, Customer } from "@autumn/shared";
import { useState } from "react";
import { useNavigate } from "react-router";
import { toast } from "sonner";
import { Button } from "@/components/v2/buttons/Button";
import { ShortcutButton } from "@/components/v2/buttons/ShortcutButton";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/v2/dialogs/Dialog";
import { FormLabel as FieldLabel } from "@/components/v2/form/FormLabel";
@@ -76,7 +78,12 @@ const UpdateCustomerDialog = ({
return (
<DialogContent className="w-md bg-card">
<DialogTitle>Update Customer</DialogTitle>
<DialogHeader>
<DialogTitle>Update Customer</DialogTitle>
<DialogDescription>
Edit customer details and billing configuration.
</DialogDescription>
</DialogHeader>
<CustomerConfig
customer={customer}
@@ -102,13 +109,15 @@ const UpdateCustomerDialog = ({
)}
<DialogFooter>
<Button
<ShortcutButton
variant="primary"
onClick={() => handleAddClicked()}
isLoading={loading}
metaShortcut="enter"
className="w-full"
>
Update
</Button>
Update Customer
</ShortcutButton>
</DialogFooter>
</DialogContent>
);

View File

@@ -30,7 +30,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import {
Tooltip,
TooltipContent,

View File

@@ -4,9 +4,11 @@ import { useNavigate } from "react-router";
import { toast } from "sonner";
import { useQueryClient } from "@tanstack/react-query";
import { Button } from "@/components/v2/buttons/Button";
import { ShortcutButton } from "@/components/v2/buttons/ShortcutButton";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
@@ -79,14 +81,6 @@ export function CustomerListCreateButton() {
{ enableOnFormTags: false },
);
useHotkeys(
"meta+enter",
(event) => {
event.preventDefault();
handleCreate();
},
{ enableOnFormTags: true },
);
return (
<Dialog
@@ -113,12 +107,16 @@ export function CustomerListCreateButton() {
<DialogContent className="w-[400px] bg-card">
<DialogHeader>
<DialogTitle>Create Customer</DialogTitle>
<DialogDescription>
Add a new customer to your workspace.
</DialogDescription>
</DialogHeader>
<div className="flex gap-2">
<div className="flex-1">
<FieldLabel>Name</FieldLabel>
<Input
ref={nameInputRef}
placeholder="John Doe"
value={fields.name}
onChange={(e) => setFields({ ...fields, name: e.target.value })}
/>
@@ -126,6 +124,7 @@ export function CustomerListCreateButton() {
<div className="flex-1">
<FieldLabel>ID</FieldLabel>
<Input
placeholder="cus_123"
value={fields.id}
onChange={(e) => setFields({ ...fields, id: e.target.value })}
/>
@@ -135,19 +134,21 @@ export function CustomerListCreateButton() {
<FieldLabel>Email</FieldLabel>
<Input
value={fields.email}
placeholder="optional"
placeholder="jane@example.com"
onChange={(e) => setFields({ ...fields, email: e.target.value })}
/>
</div>
<DialogFooter>
<Button
<ShortcutButton
onClick={handleCreate}
isLoading={isLoading}
variant="primary"
metaShortcut="enter"
disabled={!fields.id.trim() && !fields.email.trim()}
className="w-full"
>
Create
</Button>
Create Customer
</ShortcutButton>
</DialogFooter>
</DialogContent>
</Dialog>

View File

@@ -6,7 +6,6 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/v2/dropdowns/DropdownMenu";
@@ -72,18 +71,16 @@ export function CustomerListFilterButton() {
setDropdownOpen={setOpen}
/>
)}
<div className="flex h-9 items-stretch justify-between">
<DropdownMenuItem
onClick={() => clearFilters()}
className="cursor-pointer justify-center gap-0 flex-1"
<div className="flex items-stretch">
<button
type="button"
onClick={clearFilters}
className="flex-1 flex items-center justify-center gap-1.5 rounded-bl-lg px-2 py-1.5 text-xs text-t3 hover:text-t2 hover:bg-accent cursor-default"
>
<X size={12} className="mr-2 text-t3" />
<p className="text-t3">Clear</p>
</DropdownMenuItem>
<div className="flex-1">
<SaveViewPopover onClose={closeFilterModal} />
</div>
<X size={10} />
Clear
</button>
<SaveViewPopover onClose={closeFilterModal} />
</div>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -6,7 +6,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { DeleteCustomerDialog } from "@/views/customers/customer/components/DeleteCustomerDialog";
import type { CustomerWithProducts } from "./CustomerListColumns";
@@ -26,9 +26,14 @@ export const CustomerListRowToolbar = ({
setOpen={setDeleteOpen}
/>
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<DropdownMenuTrigger asChild>
<ToolbarButton />
</DropdownMenuTrigger>
<div
onClick={(e) => { e.preventDefault(); e.stopPropagation(); }}
onMouseDown={(e) => { e.preventDefault(); e.stopPropagation(); }}
>
<DropdownMenuTrigger asChild>
<ToolbarButton />
</DropdownMenuTrigger>
</div>
<DropdownMenuContent
className="text-t2"
align="end"

View File

@@ -3,7 +3,7 @@ import { FlaskIcon, PencilIcon } from "@phosphor-icons/react";
import type { Row, Table } from "@tanstack/react-table";
import { ArrowRightLeft, Delete, RotateCcw } from "lucide-react";
import { TableDropdownMenuCell } from "@/components/general/table/table-dropdown-menu-cell";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import { DropdownMenuItem } from "@/components/v2/dropdowns/DropdownMenu";
import { createDateTimeColumn } from "@/views/customers2/utils/ColumnHelpers";
import { AdminHover } from "../../../../../components/general/AdminHover";
import { getCusProductHoverTexts } from "../../../../admin/adminUtils";

View File

@@ -2,10 +2,11 @@ import type { Reward } from "@autumn/shared";
import { RewardType } from "@autumn/shared";
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/v2/buttons/Button";
import { ShortcutButton } from "@/components/v2/buttons/ShortcutButton";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
@@ -105,6 +106,9 @@ export const AddCouponDialog = ({
<DialogContent className="w-[400px] bg-card">
<DialogHeader>
<DialogTitle>Add Reward</DialogTitle>
<DialogDescription>
Apply a reward or coupon to this customer.
</DialogDescription>
</DialogHeader>
{getExistingCoupon() && (
<InfoBox variant="warning">
@@ -177,7 +181,7 @@ export const AddCouponDialog = ({
)}
</div>
<DialogFooter>
<Button
<ShortcutButton
variant="primary"
onClick={handleAddClicked}
disabled={
@@ -186,9 +190,11 @@ export const AddCouponDialog = ({
!promoCodeSelected)
}
isLoading={loading}
metaShortcut="enter"
className="w-full"
>
Add Reward
</Button>
</ShortcutButton>
</DialogFooter>
</DialogContent>
</Dialog>

View File

@@ -3,10 +3,11 @@ import { FeatureUsageType } from "@autumn/shared";
import { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router";
import { toast } from "sonner";
import { Button } from "@/components/v2/buttons/Button";
import { ShortcutButton } from "@/components/v2/buttons/ShortcutButton";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
@@ -111,6 +112,9 @@ export const CreateEntity = ({
<DialogContent className="w-[400px] bg-card">
<DialogHeader>
<DialogTitle>Create Entity</DialogTitle>
<DialogDescription>
Create a new entity for this customer to track usage separately.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
<div className="flex gap-2">
@@ -138,9 +142,9 @@ export const CreateEntity = ({
}
items={Object.fromEntries(continuousFeatures.map((feature: Feature) => [feature.id, feature.name]))}
>
<SelectTrigger>
<SelectValue placeholder="Select feature" />
</SelectTrigger>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select feature" />
</SelectTrigger>
<SelectContent>
{continuousFeatures.map((feature: Feature) => (
<SelectItem key={feature.id} value={feature.id}>
@@ -153,13 +157,15 @@ export const CreateEntity = ({
</div>
<DialogFooter>
<Button
<ShortcutButton
onClick={handleCreateClicked}
isLoading={isLoading}
variant="primary"
metaShortcut="enter"
className="w-full"
>
Create
</Button>
Create Entity
</ShortcutButton>
</DialogFooter>
</DialogContent>
</Dialog>

View File

@@ -7,7 +7,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { DeleteApiKeyDialog } from "./DeleteApiKeyDialog";
const APIKeyToolbarItems = ({

View File

@@ -7,7 +7,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { cn } from "@/lib/utils";
import { keyToTitle } from "@/utils/formatUtils/formatTextUtils";
import { pushPage } from "@/utils/genUtils";

View File

@@ -10,7 +10,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { useOrg } from "@/hooks/common/useOrg";
import { cn } from "@/lib/utils";
import { envToPath } from "@/utils/genUtils";

View File

@@ -10,7 +10,7 @@ import {
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { useEnv } from "@/utils/envUtils";
import { useOnboardingVisibility } from "@/views/onboarding4/hooks/useOnboardingProgress";
import { NavButton } from "./NavButton";

View File

@@ -5,7 +5,7 @@ import { toast } from "sonner";
import {
DropdownMenuItem,
DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { authClient, useSession } from "@/lib/auth-client";
import { useEnv } from "@/utils/envUtils";
import { getBackendErr, notNullish } from "@/utils/genUtils";

View File

@@ -1,15 +1,16 @@
import { useState } from "react";
import { useNavigate } from "react-router";
import { toast } from "sonner";
import FieldLabel from "@/components/general/modal-components/FieldLabel";
import { Button } from "@/components/v2/buttons/Button";
import { ShortcutButton } from "@/components/v2/buttons/ShortcutButton";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/v2/dialogs/Dialog";
import { FormLabel as FieldLabel } from "@/components/v2/form/FormLabel";
import { Input } from "@/components/v2/inputs/Input";
import { useOrg } from "@/hooks/common/useOrg";
import { authClient } from "@/lib/auth-client";
@@ -63,45 +64,50 @@ export const CreateNewOrg = ({
if (!open) setDialogType(null);
}}
>
<DialogContent className="gap-0 p-0 rounded-xs min-w-[400px]">
<div className="p-6 flex flex-col gap-4">
<DialogHeader>
<DialogTitle>Create New Organization</DialogTitle>
</DialogHeader>
<div className="flex gap-4">
<div>
<FieldLabel>Name</FieldLabel>
<Input
value={name}
onChange={(e) => {
setName(e.target.value);
if (!slugChanged) {
setSlug(slugify(e.target.value));
}
}}
/>
</div>
<div>
<FieldLabel>Slug</FieldLabel>
<Input
value={slug}
onChange={(e) => {
if (!slugChanged) {
setSlug(slugify(e.target.value));
}
}}
/>
</div>
<DialogContent className="w-[400px] bg-card">
<DialogHeader>
<DialogTitle>Create Organization</DialogTitle>
<DialogDescription>
Set up a new organization workspace.
</DialogDescription>
</DialogHeader>
<div className="flex gap-2">
<div className="flex-1">
<FieldLabel>Name</FieldLabel>
<Input
placeholder="Acme Inc"
value={name}
onChange={(e) => {
setName(e.target.value);
if (!slugChanged) {
setSlug(slugify(e.target.value));
}
}}
/>
</div>
<div className="flex-1">
<FieldLabel>Slug</FieldLabel>
<Input
placeholder="acme-inc"
value={slug}
onChange={(e) => {
if (!slugChanged) {
setSlug(slugify(e.target.value));
}
}}
/>
</div>
</div>
<DialogFooter className="px-4 pb-4">
<Button
<DialogFooter>
<ShortcutButton
variant="primary"
onClick={handleCreate}
isLoading={isLoading}
metaShortcut="enter"
className="w-full"
>
Create
</Button>
Create Organization
</ShortcutButton>
</DialogFooter>
</DialogContent>
</Dialog>

View File

@@ -1,6 +1,6 @@
import { LogOut } from "lucide-react";
import React from "react";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import { DropdownMenuItem } from "@/components/v2/dropdowns/DropdownMenu";
import { authClient } from "@/lib/auth-client";
export const LogOutItem = () => {

View File

@@ -1,4 +1,3 @@
import { DropdownMenuGroup } from "@/components/v2/dropdowns/DropdownMenu";
import { useQueryClient } from "@tanstack/react-query";
import {
ChevronDown,
@@ -14,9 +13,11 @@ import { useNavigate } from "react-router";
import { toast } from "sonner";
import { AdminHover } from "@/components/general/AdminHover";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuSeparator,
@@ -24,8 +25,7 @@ import {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Skeleton } from "@/components/ui/skeleton";
} from "@/components/v2/dropdowns/DropdownMenu";
import { useTheme } from "@/contexts/ThemeProvider";
import { setLastSwitchedOrgId, useOrg } from "@/hooks/common/useOrg";
import {
@@ -122,7 +122,7 @@ export const OrgDropdown = () => {
</AdminHover>
<DropdownMenuContent
align="start"
className="border-1 border-zinc-200 shadow-sm w-48"
className="w-48"
>
<AdminDropdownItems />
<DropdownMenuItem className="flex justify-between w-full items-center gap-2 text-t2">

View File

@@ -1,6 +1,6 @@
import { AppEnv } from "@autumn/shared";
import { ChevronDown, FlaskConical, Sailboat } from "lucide-react";
import { DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { DropdownMenuTrigger } from "@/components/v2/dropdowns/DropdownMenu";
import { cn } from "@/lib/utils";
import { useEnv } from "@/utils/envUtils";
import { useSidebarContext } from "../SidebarContext";

View File

@@ -5,7 +5,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { IconButton } from "@/components/v2/buttons/IconButton";
import { useProductsQueryState } from "@/views/products/hooks/useProductsQueryState";

View File

@@ -7,7 +7,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import UpdateFeatureSheet from "../components/UpdateFeatureSheet";
import UpdateCreditSystemSheet from "../credit-systems/components/UpdateCreditSystemSheet";
import { DeleteFeatureDialog } from "../feature-row-toolbar/DeleteFeatureDialog";

View File

@@ -6,7 +6,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { IconButton } from "@/components/v2/buttons/IconButton";
import { useProductStore } from "@/hooks/stores/useProductStore";
import { cn } from "@/lib/utils";

View File

@@ -45,7 +45,7 @@ function CreateProductSheet({
const axiosInstance = useAxiosInstance();
const navigate = useNavigate();
const { invalidate } = useProductsQuery();
const { invalidate, products } = useProductsQuery();
const handleCreateClicked = async () => {
const productName = product.name?.trim() || "";
@@ -80,13 +80,18 @@ function CreateProductSheet({
setOpen(false);
};
// Reset product state when sheet opens/closes
const isFirstPlan = !products || products.length === 0;
useEffect(() => {
if (open) {
reset();
setProduct({ ...DEFAULT_PRODUCT, is_add_on: isAddOn });
setProduct({
...DEFAULT_PRODUCT,
is_add_on: isAddOn,
is_default: isFirstPlan && !isAddOn,
});
}
}, [open, reset, setProduct, isAddOn]);
}, [open, reset, setProduct, isAddOn, isFirstPlan]);
return (
<Sheet open={open} onOpenChange={setOpen}>

View File

@@ -5,7 +5,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { IconButton } from "@/components/v2/buttons/IconButton";
import { useProductsQueryState } from "@/views/products/hooks/useProductsQueryState";

View File

@@ -51,9 +51,14 @@ export const ProductListRowToolbar = ({
/>
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<DropdownMenuTrigger asChild>
<ToolbarButton />
</DropdownMenuTrigger>
<div
onClick={(e) => { e.preventDefault(); e.stopPropagation(); }}
onMouseDown={(e) => { e.preventDefault(); e.stopPropagation(); }}
>
<DropdownMenuTrigger asChild>
<ToolbarButton />
</DropdownMenuTrigger>
</div>
<DropdownMenuContent align="end">
<DropdownMenuSub>
<DropdownMenuSubTrigger className="flex gap-2">

View File

@@ -10,7 +10,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { useOrgStripeQuery } from "@/hooks/queries/useOrgStripeQuery";
import { useRewardsQuery } from "@/hooks/queries/useRewardsQuery";
import { RewardService } from "@/services/products/RewardService";

View File

@@ -9,7 +9,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
} from "@/components/v2/dropdowns/DropdownMenu";
import { useRewardsQuery } from "@/hooks/queries/useRewardsQuery";
import { useAxiosInstance } from "@/services/useAxiosInstance";
import { getBackendErr } from "@/utils/genUtils";