refactor: enhance ApiKeyContent and MobileSidebar components with improved styling and event handling for better user experience

This commit is contained in:
Anish Sarkar 2026-03-08 20:34:12 +05:30
parent 863ba6865c
commit f14efa18b3
2 changed files with 96 additions and 40 deletions

View file

@ -1,16 +1,30 @@
"use client"; "use client";
import { Check, Copy, Shield } from "lucide-react"; import { Check, Copy, Info } from "lucide-react";
import { AnimatePresence, motion } from "motion/react"; import { AnimatePresence, motion } from "motion/react";
import { useCallback, useRef, useState } from "react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useApiKey } from "@/hooks/use-api-key"; import { useApiKey } from "@/hooks/use-api-key";
import { copyToClipboard as copyToClipboardUtil } from "@/lib/utils";
export function ApiKeyContent() { export function ApiKeyContent() {
const t = useTranslations("userSettings"); const t = useTranslations("userSettings");
const { apiKey, isLoading, copied, copyToClipboard } = useApiKey(); const { apiKey, isLoading, copied, copyToClipboard } = useApiKey();
const [copiedUsage, setCopiedUsage] = useState(false);
const usageCopyTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
const copyUsageToClipboard = useCallback(async () => {
const text = `Authorization: Bearer ${apiKey || "YOUR_API_KEY"}`;
const success = await copyToClipboardUtil(text);
if (success) {
setCopiedUsage(true);
if (usageCopyTimeoutRef.current) clearTimeout(usageCopyTimeoutRef.current);
usageCopyTimeoutRef.current = setTimeout(() => setCopiedUsage(false), 2000);
}
}, [apiKey]);
return ( return (
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
@ -22,49 +36,70 @@ export function ApiKeyContent() {
transition={{ duration: 0.35, ease: [0.4, 0, 0.2, 1] }} transition={{ duration: 0.35, ease: [0.4, 0, 0.2, 1] }}
className="space-y-6" className="space-y-6"
> >
<Alert> <Alert className="border-border/60 bg-muted/30 text-muted-foreground">
<Shield className="h-4 w-4" /> <Info className="h-4 w-4 text-muted-foreground" />
<AlertTitle>{t("api_key_warning_title")}</AlertTitle> <AlertTitle className="text-muted-foreground">{t("api_key_warning_title")}</AlertTitle>
<AlertDescription>{t("api_key_warning_description")}</AlertDescription> <AlertDescription className="text-muted-foreground/60">{t("api_key_warning_description")}</AlertDescription>
</Alert> </Alert>
<div className="rounded-lg border bg-card p-6"> <div className="rounded-lg border border-border/60 bg-card p-6">
<h3 className="mb-4 font-medium">{t("your_api_key")}</h3> <h3 className="mb-4 text-sm font-semibold tracking-tight">{t("your_api_key")}</h3>
{isLoading ? ( {isLoading ? (
<div className="h-12 w-full animate-pulse rounded-md bg-muted" /> <div className="h-12 w-full animate-pulse rounded-md border border-border/60 bg-muted/30" />
) : apiKey ? ( ) : apiKey ? (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
<div className="flex-1 overflow-x-auto rounded-md bg-muted p-3 font-mono text-sm"> <div className="min-w-0 flex-1 overflow-x-auto scrollbar-hide">
<p className="font-mono text-[10px] text-muted-foreground whitespace-nowrap select-all cursor-text">
{apiKey} {apiKey}
</div> </p>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
onClick={copyToClipboard}
className="shrink-0"
>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</TooltipTrigger>
<TooltipContent>{copied ? t("copied") : t("copy")}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div> </div>
) : ( <TooltipProvider>
<p className="text-center text-muted-foreground">{t("no_api_key")}</p> <Tooltip>
)} <TooltipTrigger asChild>
</div> <Button
variant="ghost"
size="icon"
onClick={copyToClipboard}
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
>
{copied ? <Check className="h-3 w-3 text-green-500" /> : <Copy className="h-3 w-3" />}
</Button>
</TooltipTrigger>
<TooltipContent>{copied ? t("copied") : t("copy")}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
) : (
<p className="text-center text-muted-foreground/60">{t("no_api_key")}</p>
)}
</div>
<div className="rounded-lg border bg-card p-6"> <div className="rounded-lg border border-border/60 bg-card p-6">
<h3 className="mb-2 font-medium">{t("usage_title")}</h3> <h3 className="mb-2 text-sm font-semibold tracking-tight">{t("usage_title")}</h3>
<p className="mb-4 text-sm text-muted-foreground">{t("usage_description")}</p> <p className="mb-4 text-[11px] text-muted-foreground/60">{t("usage_description")}</p>
<pre className="overflow-x-auto rounded-md bg-muted p-3 text-sm"> <div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
<div className="min-w-0 flex-1 overflow-x-auto scrollbar-hide">
<pre className="font-mono text-[10px] text-muted-foreground whitespace-nowrap select-all cursor-text">
<code>Authorization: Bearer {apiKey || "YOUR_API_KEY"}</code> <code>Authorization: Bearer {apiKey || "YOUR_API_KEY"}</code>
</pre> </pre>
</div> </div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={copyUsageToClipboard}
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
>
{copiedUsage ? <Check className="h-3 w-3 text-green-500" /> : <Copy className="h-3 w-3" />}
</Button>
</TooltipTrigger>
<TooltipContent>{copiedUsage ? t("copied") : t("copy")}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
</motion.div> </motion.div>
</AnimatePresence> </AnimatePresence>
); );

View file

@ -166,9 +166,30 @@ export function MobileSidebar({
: undefined : undefined
} }
user={user} user={user}
onSettings={onSettings} onSettings={
onManageMembers={onManageMembers} onSettings
onUserSettings={onUserSettings} ? () => {
onOpenChange(false);
onSettings();
}
: undefined
}
onManageMembers={
onManageMembers
? () => {
onOpenChange(false);
onManageMembers();
}
: undefined
}
onUserSettings={
onUserSettings
? () => {
onOpenChange(false);
onUserSettings();
}
: undefined
}
onLogout={onLogout} onLogout={onLogout}
pageUsage={pageUsage} pageUsage={pageUsage}
theme={theme} theme={theme}