TorbatYar/frontend/components/ui/Button.tsx
Mortezakoohjani 800b0ba2c5 Deploy TorbatYar for torbatyar.ir with nginx multi-tenant routing.
Wire production domain, CORS for tenant subdomains, celery volume mounts, and nginx reverse proxy configs for apex, API, identity, auth, and wildcard tenants.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 21:43:33 +03:30

60 lines
1.8 KiB
TypeScript

import type { ButtonHTMLAttributes, ReactNode } from "react";
type ButtonVariant = "primary" | "secondary" | "ghost" | "outline";
type ButtonSize = "sm" | "md" | "lg";
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
children: ReactNode;
loading?: boolean;
loadingText?: string;
}
const variantClasses: Record<ButtonVariant, string> = {
primary: "bg-primary text-white hover:opacity-90 shadow-sm",
secondary: "bg-secondary text-white hover:opacity-90",
ghost: "bg-transparent text-secondary hover:bg-[var(--color-secondary-muted)]",
outline:
"border border-gray-200 bg-white text-secondary hover:border-primary hover:text-primary",
};
const sizeClasses: Record<ButtonSize, string> = {
sm: "px-3 py-1.5 text-sm",
md: "px-5 py-2.5 text-sm",
lg: "px-6 py-3 text-base",
};
export function Button({
variant = "primary",
size = "md",
className = "",
children,
loading = false,
loadingText,
disabled,
...props
}: ButtonProps) {
return (
<button
type="button"
disabled={disabled || loading}
aria-busy={loading || undefined}
className={`inline-flex items-center justify-center gap-2 rounded-xl font-semibold transition-all disabled:cursor-not-allowed disabled:opacity-50 ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}
{...props}
>
{loading ? (
<>
<span
className="inline-block h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-current border-r-transparent"
aria-hidden
/>
<span>{loadingText ?? "در حال انجام..."}</span>
</>
) : (
children
)}
</button>
);
}