refactor: replace button elements with Button component for consistent styling across various UI components

This commit is contained in:
Anish Sarkar 2026-05-14 13:30:20 +05:30
parent 88f9c3353c
commit 198c38b335
13 changed files with 234 additions and 180 deletions

View file

@ -7,6 +7,7 @@ import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { loginMutationAtom } from "@/atoms/auth/auth-mutation.atoms";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { getAuthErrorDetails, isNetworkError } from "@/lib/auth-errors";
import { AUTH_TYPE } from "@/lib/env-config";
@ -120,11 +121,13 @@ export function LocalLoginForm() {
<p className="text-sm font-semibold mb-1">{error.title}</p>
<p className="text-sm text-destructive">{error.message}</p>
</div>
<button
<Button
variant="ghost"
size="icon"
onClick={() => {
setError({ title: null, message: null });
}}
className="flex-shrink-0 text-destructive hover:text-destructive/90 transition-colors"
className="size-6 flex-shrink-0 text-destructive hover:bg-transparent hover:text-destructive/90"
aria-label="Dismiss error"
type="button"
>
@ -143,7 +146,7 @@ export function LocalLoginForm() {
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</Button>
</div>
</motion.div>
)}
@ -191,21 +194,23 @@ export function LocalLoginForm() {
}`}
disabled={isLoggingIn}
/>
<button
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => setShowPassword((prev) => !prev)}
className="absolute inset-y-0 right-0 flex items-center pr-3 text-muted-foreground hover:text-foreground"
className="absolute inset-y-0 right-0 h-full w-10 text-muted-foreground hover:bg-transparent hover:text-foreground"
aria-label={showPassword ? t("hide_password") : t("show_password")}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</Button>
</div>
</div>
<button
<Button
type="submit"
disabled={isLoggingIn}
className="relative w-full rounded-md bg-primary px-4 py-1.5 md:py-2 text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-all text-sm md:text-base flex items-center justify-center gap-2"
className="relative h-auto w-full px-4 py-1.5 text-sm md:py-2 md:text-base"
>
<span className={isLoggingIn ? "invisible" : ""}>{t("sign_in")}</span>
{isLoggingIn && (
@ -213,7 +218,7 @@ export function LocalLoginForm() {
<Spinner size="sm" className="text-primary-foreground" />
</span>
)}
</button>
</Button>
</form>
{authType === "LOCAL" && (

View file

@ -6,6 +6,7 @@ import { useTranslations } from "next-intl";
import { Suspense, useEffect, useState } from "react";
import { toast } from "sonner";
import { Logo } from "@/components/Logo";
import { Button } from "@/components/ui/button";
import { useGlobalLoadingEffect } from "@/hooks/use-global-loading";
import { getAuthErrorDetails, shouldRetry } from "@/lib/auth-errors";
import { setRedirectPath } from "@/lib/auth-utils";
@ -154,10 +155,12 @@ function LoginContent() {
<p className="text-sm font-semibold mb-1">{urlError.title}</p>
<p className="text-sm text-red-700 dark:text-red-300">{urlError.message}</p>
</div>
<button
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => setUrlError(null)}
className="flex-shrink-0 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-200 transition-colors"
className="size-6 flex-shrink-0 text-red-500 hover:bg-transparent hover:text-red-700 dark:text-red-400 dark:hover:text-red-200"
aria-label="Dismiss error"
>
<svg
@ -175,7 +178,7 @@ function LoginContent() {
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</Button>
</div>
</motion.div>
)}

View file

@ -9,6 +9,7 @@ import { useEffect, useState } from "react";
import { type ExternalToast, toast } from "sonner";
import { registerMutationAtom } from "@/atoms/auth/auth-mutation.atoms";
import { Logo } from "@/components/Logo";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { getAuthErrorDetails, isNetworkError, shouldRetry } from "@/lib/auth-errors";
import { getBearerToken } from "@/lib/auth-utils";
@ -199,11 +200,13 @@ export default function RegisterPage() {
<p className="text-sm font-semibold mb-1">{error.title}</p>
<p className="text-sm text-red-700 dark:text-red-300">{error.message}</p>
</div>
<button
<Button
variant="ghost"
size="icon"
onClick={() => {
setError({ title: null, message: null });
}}
className="flex-shrink-0 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-200 transition-colors"
className="size-6 flex-shrink-0 text-red-500 hover:bg-transparent hover:text-red-700 dark:text-red-400 dark:hover:text-red-200"
aria-label="Dismiss error"
type="button"
>
@ -222,7 +225,7 @@ export default function RegisterPage() {
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</Button>
</div>
</motion.div>
)}
@ -295,18 +298,18 @@ export default function RegisterPage() {
/>
</div>
<button
<Button
type="submit"
disabled={isRegistering}
className="relative w-full rounded-md bg-primary px-4 py-1.5 md:py-2 text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-1 focus:ring-primary/40 disabled:cursor-not-allowed disabled:opacity-50 transition-all text-sm md:text-base flex items-center justify-center gap-2"
className="relative h-auto w-full px-4 py-1.5 text-sm md:py-2 md:text-base"
>
<span className={isRegistering ? "invisible" : ""}>{t("register")}</span>
{isRegistering && (
<span className="absolute inset-0 flex items-center justify-center gap-2">
<Spinner size="sm" className="text-white" />
<Spinner size="sm" className="text-primary-foreground" />
</span>
)}
</button>
</Button>
</form>
<div className="mt-4 text-center text-sm">

View file

@ -4,6 +4,7 @@ import { motion } from "motion/react";
import { useState } from "react";
import { BuyPagesContent } from "@/components/settings/buy-pages-content";
import { BuyTokensContent } from "@/components/settings/buy-tokens-content";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const TABS = [
@ -26,19 +27,21 @@ export default function BuyMorePage() {
>
<div className="flex items-center justify-center rounded-lg border bg-muted/30 p-1">
{TABS.map((tab) => (
<button
<Button
key={tab.id}
type="button"
variant="ghost"
size="sm"
onClick={() => setActiveTab(tab.id)}
className={cn(
"flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors",
"h-auto flex-1 px-3 py-1.5 text-sm",
activeTab === tab.id
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:text-accent-foreground"
)}
>
{tab.label}
</button>
</Button>
))}
</div>

View file

@ -44,6 +44,7 @@ import {
} from "@/components/assistant-ui/edit-message-dialog";
import { StepSeparatorDataUI } from "@/components/assistant-ui/step-separator";
import { Thread } from "@/components/assistant-ui/thread";
import { Button } from "@/components/ui/button";
import {
createTokenUsageStore,
type TokenUsageData,
@ -76,8 +77,6 @@ import {
import { createStreamFlushHelpers } from "@/lib/chat/stream-flush";
import {
consumeSseEvents,
hasPersistableContent,
markInterruptsCompleted,
processSharedStreamEvent,
} from "@/lib/chat/stream-pipeline";
import {
@ -88,7 +87,6 @@ import {
} from "@/lib/chat/stream-side-effects";
import {
addToolCall,
buildContentForPersistence,
buildContentForUI,
type ContentPartsState,
type FrameBatchedUpdater,
@ -1718,8 +1716,19 @@ export default function NewChatPage() {
}
const byTcId = new Map<string, (typeof incoming)[number]>();
for (let i = 0; i < tcIds.length; i++) byTcId.set(tcIds[i], incoming[i]);
const submittedDecisions = tcIds.map((id) => byTcId.get(id)!);
const submittedDecisions: typeof incoming = [];
for (let i = 0; i < tcIds.length; i++) {
const tcId = tcIds[i];
const decision = incoming[i];
if (tcId === undefined || decision === undefined) {
toast.error(
`Cannot resume: ${incoming.length} decision(s) submitted for ${N} pending actions.`
);
return;
}
byTcId.set(tcId, decision);
submittedDecisions.push(decision);
}
setMessages((prev) =>
prev.map((m) => {
@ -2341,16 +2350,15 @@ export default function NewChatPage() {
return (
<div className="flex h-full flex-col items-center justify-center gap-4">
<div className="text-destructive">Failed to load chat</div>
<button
<Button
type="button"
onClick={() => {
setIsInitializing(true);
initializeThread();
}}
className="rounded-md bg-primary px-4 py-2 text-primary-foreground hover:bg-primary/90"
>
Try Again
</button>
</Button>
</div>
);
}

View file

@ -482,13 +482,15 @@ function MemberRow({
{showActions ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
<Button
type="button"
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-accent-foreground transition-colors"
variant="ghost"
size="sm"
className="h-auto gap-1.5 px-0 py-0 text-sm text-muted-foreground hover:bg-transparent hover:text-accent-foreground"
>
{roleName}
<ChevronDown className="h-4 w-4" />
</button>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"

View file

@ -6,6 +6,7 @@ import { useMemo } from "react";
import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import type { AgentFeatureFlags } from "@/lib/apis/agent-flags-api.service";
@ -246,14 +247,16 @@ export function AgentStatusContent() {
<AlertTitle>Failed to load agent status</AlertTitle>
<AlertDescription className="flex items-center gap-2">
{error instanceof Error ? error.message : "Unknown error."}
<button
<Button
type="button"
variant="outline"
size="sm"
onClick={() => refetch()}
className="ml-auto inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs hover:bg-background"
className="ml-auto h-auto gap-1 px-2 py-0.5 text-xs hover:bg-background"
>
<RotateCcw className="size-3" />
Retry
</button>
</Button>
</AlertDescription>
</Alert>
);

View file

@ -3,6 +3,7 @@
import { ExternalLink } from "lucide-react";
import Link from "next/link";
import { useEffect, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { buildIssueUrl } from "@/lib/error-toast";
export default function DashboardError({
@ -39,13 +40,12 @@ export default function DashboardError({
)}
<div className="flex gap-2">
<button
<Button
type="button"
onClick={reset}
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
>
Try again
</button>
</Button>
<Link
href="/dashboard"
className="rounded-md border border-input bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors"

View file

@ -2,6 +2,7 @@
import { ExternalLink } from "lucide-react";
import { useEffect, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { buildIssueUrl } from "@/lib/error-toast";
export default function ErrorPage({
@ -37,13 +38,12 @@ export default function ErrorPage({
)}
<div className="flex gap-2">
<button
<Button
type="button"
onClick={reset}
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
>
Try again
</button>
</Button>
<a
href={issueUrl}
target="_blank"