feat: enhance user settings page with tab navigation and default tab handling, update ProfileContent to use Next.js Image component, and improve sidebar user profile dropdown functionality

This commit is contained in:
Anish Sarkar 2026-03-08 19:54:12 +05:30
parent 0b028bab49
commit f08ca26e3e
4 changed files with 44 additions and 24 deletions

View file

@ -26,6 +26,7 @@ function AvatarDisplay({ url, fallback }: { url?: string; fallback: string }) {
height={64}
className="h-16 w-16 rounded-xl object-cover"
onError={() => setErrorUrl(url)}
unoptimized
/>
);
}

View file

@ -2,6 +2,8 @@
import { UserKey, User } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback } from "react";
import {
Tabs,
TabsContent,
@ -11,13 +13,32 @@ import {
import { ApiKeyContent } from "./components/ApiKeyContent";
import { ProfileContent } from "./components/ProfileContent";
const VALID_TABS = ["profile", "api-key"] as const;
const DEFAULT_TAB = "profile";
export default function UserSettingsPage() {
const t = useTranslations("userSettings");
const router = useRouter();
const searchParams = useSearchParams();
const tabParam = searchParams.get("tab") ?? "";
const activeTab = VALID_TABS.includes(tabParam as (typeof VALID_TABS)[number])
? tabParam
: DEFAULT_TAB;
const handleTabChange = useCallback(
(value: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set("tab", value);
router.replace(`?${params.toString()}`, { scroll: false });
},
[router, searchParams]
);
return (
<div className="h-full overflow-y-auto">
<div className="mx-auto w-full max-w-4xl px-4 py-10">
<Tabs defaultValue="profile" className="w-full">
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
<TabsList showBottomBorder>
<TabsTrigger value="profile">
<User className="mr-2 h-4 w-4" />

View file

@ -304,7 +304,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
}, []);
const handleUserSettings = useCallback(() => {
router.push(`/dashboard/${searchSpaceId}/user-settings`);
router.push(`/dashboard/${searchSpaceId}/user-settings?tab=profile`);
}, [router, searchSpaceId]);
const handleSearchSpaceSettings = useCallback(

View file

@ -1,6 +1,7 @@
"use client";
import { Check, ChevronUp, Languages, Laptop, LogOut, Moon, Settings, Sun } from "lucide-react";
import Image from "next/image";
import { useTranslations } from "next-intl";
import { useState } from "react";
import {
@ -16,7 +17,6 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useLocaleContext } from "@/contexts/LocaleContext";
import { cn } from "@/lib/utils";
import type { User } from "../../types/layout.types";
@ -100,11 +100,14 @@ function UserAvatar({
}) {
if (avatarUrl) {
return (
<img
<Image
src={avatarUrl}
alt="User avatar"
width={32}
height={32}
className="h-8 w-8 shrink-0 rounded-lg object-cover"
referrerPolicy="no-referrer"
unoptimized
/>
);
}
@ -156,26 +159,21 @@ export function SidebarUserProfile({
if (isCollapsed) {
return (
<div className="border-t p-2">
<DropdownMenu>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuTrigger asChild>
<button
type="button"
className={cn(
"flex h-10 w-full items-center justify-center rounded-md",
"hover:bg-accent transition-colors",
"focus:outline-none focus-visible:outline-none",
"data-[state=open]:bg-transparent"
)}
>
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />
<span className="sr-only">{displayName}</span>
</button>
</DropdownMenuTrigger>
</TooltipTrigger>
<TooltipContent side="right">{displayName}</TooltipContent>
</Tooltip>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className={cn(
"flex h-10 w-full items-center justify-center rounded-md",
"hover:bg-accent transition-colors",
"focus:outline-none focus-visible:outline-none",
"data-[state=open]:bg-transparent"
)}
>
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />
<span className="sr-only">{displayName}</span>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48" side="right" align="center" sideOffset={8}>
<DropdownMenuLabel className="font-normal">