Files
OmniRoute/src/shared/components/Button.tsx
nguyenha935 4012bac41d fix(i18n): preserve remaining Vietnamese localization (#7935)
* fix(i18n): preserve remaining Vietnamese localization

* chore(quality): rebaseline file-size cap for 9 dashboard components (i18n wiring)

Restoring the Vietnamese localization on 9 dashboard components (useTranslations
wiring + t()/tc() call-site swaps for previously hardcoded strings) grows each
file by a small, irreducible amount. Bumps the frozen file-size-baseline.json
caps to match, with a justification entry per the project's own ratchet policy.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(i18n): wire weekday localization + add missing qwen CLI description

Two gaps left by this PR's own new contract tests, caught while
reconciling the branch against the release tip:

- CostOverviewTab.tsx added formatWeekdayLabel() but never called it;
  the Weekly Usage Pattern chart still showed raw English day
  abbreviations regardless of locale. Now maps weeklyPattern rows
  through it before handing them to WeeklyPatternCard.
- cliTools.toolDescriptions was missing an entry for "qwen" (a
  baseUrlSupport:"full" tool) in both en.json and vi.json, failing
  the PR's own cli-catalog-display-contract.test.ts.

Covered by the PR's existing tests/unit/dashboard-localization-contract.test.ts
and tests/unit/cli-catalog-display-contract.test.ts (both now pass).

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* i18n(vi): backfill the 4 proxySubscription keys #7299 added to en.json

#7299 (proxy subscriptions) merged while this branch was rebasing, adding
settings.proxySubscriptionsTab and settings.proxySubscription.error.{LOCAL_CORE_ENDPOINT_INVALID,
NEEDS_CORE_NOT_CONFIGURED,NO_USABLE_NODES} to en.json. This PR's own
i18n-vi-completeness contract asserts full en↔vi key parity, so the merge of the
current release tip surfaced them as missing. Adds the Vietnamese translations,
keeping the SS/VMess/Trojan/VLESS/SOCKS5 technical terms verbatim.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: nguyenha935 <208228297+nguyenha935@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: nguyenha935 <nguyenha935@users.noreply.github.com>
2026-07-21 13:41:02 -03:00

89 lines
2.4 KiB
TypeScript

"use client";
import { cn } from "@/shared/utils/cn";
const variants = {
primary: "bg-[image:var(--grad-brand)] text-white shadow-sm hover:brightness-105",
accent: "bg-accent text-white shadow-sm hover:bg-accent-hover",
secondary:
"bg-white dark:bg-white/10 border border-black/10 dark:border-white/10 text-text-main hover:bg-black/5 dark:hover:bg-white/5",
outline: "border border-black/15 dark:border-white/15 text-text-main hover:bg-black/5",
ghost: "text-text-muted hover:bg-black/5 dark:hover:bg-white/5 hover:text-text-main",
warning: "bg-amber-500 text-white hover:bg-amber-600 shadow-sm",
danger: "bg-red-500 text-white hover:bg-red-600 shadow-sm",
};
export type ButtonVariant = keyof typeof variants;
const sizes = {
sm: "h-7 px-3 text-xs rounded-control",
md: "h-9 px-4 text-sm rounded-control",
lg: "h-11 px-6 text-sm rounded-control",
};
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children?: React.ReactNode;
variant?: ButtonVariant;
size?: keyof typeof sizes;
icon?: string;
iconRight?: string;
loading?: boolean;
fullWidth?: boolean;
className?: string;
}
export default function Button({
children,
variant = "primary",
size = "md",
icon,
iconRight,
disabled = false,
loading = false,
fullWidth = false,
className,
...props
}: ButtonProps) {
return (
<button
type="button"
className={cn(
"inline-flex items-center justify-center gap-2 font-medium transition-all duration-200 cursor-pointer",
"active:scale-[0.99] disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100",
variants[variant],
sizes[size],
fullWidth && "w-full",
className
)}
disabled={disabled || loading}
aria-busy={loading || undefined}
{...props}
>
{loading ? (
<span
className="material-symbols-outlined animate-spin text-[18px] pointer-events-none"
aria-hidden="true"
>
progress_activity
</span>
) : icon ? (
<span
className="material-symbols-outlined text-[18px] pointer-events-none"
aria-hidden="true"
>
{icon}
</span>
) : null}
{children}
{iconRight && !loading && (
<span
className="material-symbols-outlined text-[18px] pointer-events-none"
aria-hidden="true"
>
{iconRight}
</span>
)}
</button>
);
}