- added settings page and function

- add Role Ring for avatar and display role for user nav
This commit is contained in:
2026-01-06 21:37:53 +07:00
parent 8146565d2c
commit a4e96fe045
64 changed files with 2828 additions and 726 deletions

View File

@@ -1,4 +1,4 @@
import { authClient } from '@/lib/auth-client';
import { authClient, useSession } from '@/lib/auth-client';
import {
DotsThreeVerticalIcon,
KeyIcon,
@@ -7,12 +7,11 @@ import {
UserCircleIcon,
} from '@phosphor-icons/react';
import { useQueryClient } from '@tanstack/react-query';
import { createLink, useNavigate } from '@tanstack/react-router';
import { createLink, Link, useNavigate } from '@tanstack/react-router';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
import { useAuth } from '../auth/auth-provider';
import AvatarUser from '../AvatarUser';
import AvatarUser from '../avatar/AvatarUser';
import RoleBadge from '../avatar/RoleBadge';
import {
DropdownMenu,
DropdownMenuContent,
@@ -30,21 +29,20 @@ import {
} from '../ui/sidebar';
const SidebarMenuButtonLink = createLink(SidebarMenuButton);
const DropdownMenuItemLink = createLink(DropdownMenuItem);
const NavUser = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const { isMobile } = useSidebar();
const queryClient = useQueryClient();
const { data: session, isLoading } = useAuth();
const { data: session } = useSession();
const signout = async () => {
await authClient.signOut({
fetchOptions: {
onSuccess: () => {
navigate({ to: '/' });
queryClient.invalidateQueries({ queryKey: ['session'] });
queryClient.invalidateQueries({ queryKey: ['auth', 'session'] });
toast.success(t('loginPage.messages.logout_success'));
},
onError: (ctx) => {
@@ -54,7 +52,6 @@ const NavUser = () => {
});
};
if (isLoading) return null;
if (!session?.user)
return (
<SidebarMenu>
@@ -81,7 +78,7 @@ const NavUser = () => {
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground cursor-pointer"
tooltip={session?.user?.name}
>
<AvatarUser session={session} className="h-8 w-8" />
<AvatarUser className="h-8 w-8" />
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">
{session?.user?.name}
@@ -95,16 +92,22 @@ const NavUser = () => {
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
side={isMobile ? 'bottom' : 'right'}
align="end"
sideOffset={4}
sideOffset={15}
>
{/* Dropdown menu content */}
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<AvatarUser session={session} className="h-8 w-8" />
<AvatarUser className="h-8 w-8" />
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">
{session?.user?.name}
</span>
<div className="flex gap-2 items-center">
<span className="truncate font-medium">
{session?.user?.name}
</span>
<RoleBadge
type={session?.user?.role}
className="text-[10px] px-2 py-0 leading-0.5 h-4"
/>
</div>
<span className="truncate text-xs">
{session?.user?.email}
</span>
@@ -113,21 +116,22 @@ const NavUser = () => {
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItemLink to="/profile" className="cursor-pointer">
<UserCircleIcon size={28} />
{t('nav.account')}
</DropdownMenuItemLink>
<DropdownMenuItemLink
to="/change-password"
className="cursor-pointer"
>
<KeyIcon size={28} />
{t('nav.change_password')}
</DropdownMenuItemLink>
<DropdownMenuItem className="cursor-pointer" asChild>
<Link to="/profile">
<UserCircleIcon size={28} />
{t('nav.account')}
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="cursor-pointer" asChild>
<Link to="/change-password">
<KeyIcon size={28} />
{t('nav.change_password')}
</Link>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem onClick={signout} className="cursor-pointer">
<DropdownMenuItem onSelect={signout} className="cursor-pointer">
<SignOutIcon size={28} />
{t('ui.logout_btn')}
</DropdownMenuItem>