mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-30 21:59:46 +02:00
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:
parent
0b028bab49
commit
f08ca26e3e
4 changed files with 44 additions and 24 deletions
|
|
@ -26,6 +26,7 @@ function AvatarDisplay({ url, fallback }: { url?: string; fallback: string }) {
|
||||||
height={64}
|
height={64}
|
||||||
className="h-16 w-16 rounded-xl object-cover"
|
className="h-16 w-16 rounded-xl object-cover"
|
||||||
onError={() => setErrorUrl(url)}
|
onError={() => setErrorUrl(url)}
|
||||||
|
unoptimized
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
import { UserKey, User } from "lucide-react";
|
import { UserKey, User } from "lucide-react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import { useCallback } from "react";
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
|
|
@ -11,13 +13,32 @@ import {
|
||||||
import { ApiKeyContent } from "./components/ApiKeyContent";
|
import { ApiKeyContent } from "./components/ApiKeyContent";
|
||||||
import { ProfileContent } from "./components/ProfileContent";
|
import { ProfileContent } from "./components/ProfileContent";
|
||||||
|
|
||||||
|
const VALID_TABS = ["profile", "api-key"] as const;
|
||||||
|
const DEFAULT_TAB = "profile";
|
||||||
|
|
||||||
export default function UserSettingsPage() {
|
export default function UserSettingsPage() {
|
||||||
const t = useTranslations("userSettings");
|
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 (
|
return (
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
<div className="mx-auto w-full max-w-4xl px-4 py-10">
|
<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>
|
<TabsList showBottomBorder>
|
||||||
<TabsTrigger value="profile">
|
<TabsTrigger value="profile">
|
||||||
<User className="mr-2 h-4 w-4" />
|
<User className="mr-2 h-4 w-4" />
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleUserSettings = useCallback(() => {
|
const handleUserSettings = useCallback(() => {
|
||||||
router.push(`/dashboard/${searchSpaceId}/user-settings`);
|
router.push(`/dashboard/${searchSpaceId}/user-settings?tab=profile`);
|
||||||
}, [router, searchSpaceId]);
|
}, [router, searchSpaceId]);
|
||||||
|
|
||||||
const handleSearchSpaceSettings = useCallback(
|
const handleSearchSpaceSettings = useCallback(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Check, ChevronUp, Languages, Laptop, LogOut, Moon, Settings, Sun } from "lucide-react";
|
import { Check, ChevronUp, Languages, Laptop, LogOut, Moon, Settings, Sun } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
|
@ -16,7 +17,6 @@ import {
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
||||||
import { useLocaleContext } from "@/contexts/LocaleContext";
|
import { useLocaleContext } from "@/contexts/LocaleContext";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { User } from "../../types/layout.types";
|
import type { User } from "../../types/layout.types";
|
||||||
|
|
@ -100,11 +100,14 @@ function UserAvatar({
|
||||||
}) {
|
}) {
|
||||||
if (avatarUrl) {
|
if (avatarUrl) {
|
||||||
return (
|
return (
|
||||||
<img
|
<Image
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
alt="User avatar"
|
alt="User avatar"
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
className="h-8 w-8 shrink-0 rounded-lg object-cover"
|
className="h-8 w-8 shrink-0 rounded-lg object-cover"
|
||||||
referrerPolicy="no-referrer"
|
referrerPolicy="no-referrer"
|
||||||
|
unoptimized
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -156,26 +159,21 @@ export function SidebarUserProfile({
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
return (
|
return (
|
||||||
<div className="border-t p-2">
|
<div className="border-t p-2">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<Tooltip>
|
<DropdownMenuTrigger asChild>
|
||||||
<TooltipTrigger asChild>
|
<button
|
||||||
<DropdownMenuTrigger asChild>
|
type="button"
|
||||||
<button
|
className={cn(
|
||||||
type="button"
|
"flex h-10 w-full items-center justify-center rounded-md",
|
||||||
className={cn(
|
"hover:bg-accent transition-colors",
|
||||||
"flex h-10 w-full items-center justify-center rounded-md",
|
"focus:outline-none focus-visible:outline-none",
|
||||||
"hover:bg-accent transition-colors",
|
"data-[state=open]:bg-transparent"
|
||||||
"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>
|
||||||
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />
|
</button>
|
||||||
<span className="sr-only">{displayName}</span>
|
</DropdownMenuTrigger>
|
||||||
</button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right">{displayName}</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<DropdownMenuContent className="w-48" side="right" align="center" sideOffset={8}>
|
<DropdownMenuContent className="w-48" side="right" align="center" sideOffset={8}>
|
||||||
<DropdownMenuLabel className="font-normal">
|
<DropdownMenuLabel className="font-normal">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue