Files
edr-platform/apps/edr-freight-web/backoffice/src/shared/common/ui/button.tsx
natib21 22cf8eabd1 fix ui
2026-07-14 07:59:17 +00:00

162 lines
8.1 KiB
TypeScript

import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/shared/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
outline:
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-muted-foreground underline-offset-13 hover:underline hover:text-primary",
linkActive: "text-primary-700 underline-offset-13 underline -mt-2",
addRecordBtn:
"bg-primary text-white size-35 shadow-xs hover:bg-primary-700 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
viewDetail:
"bg-transparent border border-primary text-primary text-base font-normal me-2 !px-2.5 py-0.5 rounded-full scale-90 origin-center hover:bg-primary/10 transition-colors dark:bg-primary/20 dark:border-primary dark:text-primary-foreground leading-tight whitespace-nowrap",
incomingmenu:
"bg-transparent border border-primary text-primary text-md font-normal me-2 !px-4 py-0.5 rounded-md w-[170px] scale-90 origin-center hover:bg-primary/10 transition-colors dark:bg-primary/20 dark:border-primary dark:text-primary-foreground leading-tight whitespace-nowrap",
incomingmenu1:
"bg-transparent border border-gray-500 text-gray-500 text-md font-normal me-2 !px-4 py-0.5 rounded-md w-[170px] scale-90 origin-center hover:bg-gray-500/10 transition-colors dark:border-gray-300 dark:text-gray-300 leading-tight whitespace-nowrap",
actionGreen:
"bg-emerald-50/90 dark:bg-emerald-950/20 border border-emerald-200 dark:border-emerald-900/40 text-emerald-700 dark:text-emerald-300 hover:bg-emerald-100 dark:hover:bg-emerald-950/40 hover:text-emerald-800 dark:hover:text-emerald-200 hover:border-emerald-300 dark:hover:border-emerald-800/60 focus-visible:ring-emerald-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300",
actionLightGreen:
"bg-green-50/50 dark:bg-green-950/10 border border-green-100 dark:border-green-900/20 text-green-600 dark:text-green-400 hover:bg-green-100/50 dark:hover:bg-green-950/30 hover:text-green-700 dark:hover:text-green-300 hover:border-green-200 dark:hover:border-green-800/40 focus-visible:ring-green-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300",
actionRed:
"bg-rose-50/90 dark:bg-rose-950/20 border border-rose-200 dark:border-rose-900/40 text-rose-700 dark:text-rose-300 hover:bg-rose-100 dark:hover:bg-rose-950/40 hover:text-rose-800 dark:hover:text-rose-200 hover:border-rose-300 dark:hover:border-rose-800/60 focus-visible:ring-rose-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300",
actionYellow:
"bg-amber-50/90 dark:bg-amber-950/20 border border-amber-200 dark:border-amber-900/40 text-amber-700 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-950/40 hover:text-amber-800 dark:hover:text-amber-200 hover:border-amber-300 dark:hover:border-amber-800/60 focus-visible:ring-amber-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300",
actionBlue:
"bg-primary-50/90 dark:bg-primary-950/20 border border-primary-200 dark:border-primary-900/40 text-primary-700 dark:text-primary-300 hover:bg-primary-100 dark:hover:bg-primary-950/40 hover:text-primary-800 dark:hover:text-primary-200 hover:border-primary-300 dark:hover:border-primary-800/60 focus-visible:ring-primary-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300",
actionPurple:
"bg-purple-50/90 dark:bg-purple-950/20 border border-purple-200 dark:border-purple-900/40 text-purple-700 dark:text-purple-300 hover:bg-purple-100 dark:hover:bg-purple-950/40 hover:text-purple-800 dark:hover:text-purple-200 hover:border-purple-300 dark:hover:border-purple-800/60 focus-visible:ring-purple-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
type ButtonProps = React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
};
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, type, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
data-slot="button"
// When `asChild` is used, Radix will attach the ref to the child via Slot.
ref={ref as any}
className={cn(
buttonVariants({ variant, size, className }),
"cursor-pointer",
)}
{...(asChild ? {} : { type: type ?? "button" })}
{...props}
/>
);
},
);
Button.displayName = "Button";
export function getActionButtonVariant(actionName: string): any {
const normalized = actionName.trim().toLowerCase();
if (
normalized.includes("approve") ||
normalized.includes("sign") ||
normalized.includes("reply") ||
normalized === "accept"
) {
return "actionGreen";
}
if (
normalized.includes("accept and forward") ||
normalized.includes("accept & forward")
) {
return "actionLightGreen";
}
if (normalized.includes("delete") || normalized.includes("reject")) {
return "actionRed";
}
if (normalized.includes("forward")) {
return "actionPurple";
}
if (
normalized.includes("adjustment") ||
normalized.includes("adjust") ||
normalized.includes("secure letter") ||
normalized.includes("make urgent") ||
normalized.includes("urgent") ||
normalized.includes("assign") ||
normalized.includes("reassign") ||
normalized.includes("under review") ||
normalized.includes("return")
) {
return "actionYellow";
}
if (normalized.includes("edit") || normalized.includes("comment")) {
return "actionBlue";
}
if (
normalized.includes("send") ||
normalized.includes("dispatch") ||
normalized.includes("share")
) {
return "actionPurple";
}
// Custom keyword heuristics
if (
normalized.includes("remove") ||
normalized.includes("cancel") ||
normalized.includes("fail") ||
normalized.includes("discard")
) {
return "actionRed";
}
if (
normalized.includes("warn") ||
normalized.includes("caution") ||
normalized.includes("alert") ||
normalized.includes("pending")
) {
return "actionYellow";
}
if (
normalized.includes("success") ||
normalized.includes("complete") ||
normalized.includes("done") ||
normalized.includes("save")
) {
return "actionGreen";
}
return "actionBlue"; // Informational fallback
}
export { Button, buttonVariants };